File indexing completed on 2024-05-12 15:56:46

0001 /* This file is part of the KDE project
0002    SPDX-FileCopyrightText: 2006 C. Boemann Rasmussen <cbo@boemann.dk>
0003    SPDX-FileCopyrightText: 2006-2010 Thomas Zander <zander@kde.org>
0004    SPDX-FileCopyrightText: 2006-2010 Thorsten Zachmann <zachmann@kde.org>
0005    SPDX-FileCopyrightText: 2007-2009, 2011 Jan Hambrecht <jaham@gmx.net>
0006    SPDX-FileCopyrightText: 2010 Boudewijn Rempt <boud@valdyas.org>
0007 
0008    SPDX-License-Identifier: LGPL-2.0-or-later
0009 */
0010 
0011 #include <limits>
0012 
0013 #include "KoShape.h"
0014 #include "KoShape_p.h"
0015 #include "KoShapeContainer.h"
0016 #include "KoShapeLayer.h"
0017 #include "KoShapeContainerModel.h"
0018 #include "KoSelection.h"
0019 #include "KoPointerEvent.h"
0020 #include "KoInsets.h"
0021 #include "KoShapeStrokeModel.h"
0022 #include "KoShapeBackground.h"
0023 #include "KoColorBackground.h"
0024 #include "KoHatchBackground.h"
0025 #include "KoGradientBackground.h"
0026 #include "KoPatternBackground.h"
0027 #include "KoShapeManager.h"
0028 #include "KoShapeUserData.h"
0029 #include "KoShapeApplicationData.h"
0030 #include "KoShapeSavingContext.h"
0031 #include "KoShapeLoadingContext.h"
0032 #include "KoViewConverter.h"
0033 #include "KoShapeStroke.h"
0034 #include "KoShapeShadow.h"
0035 #include "KoClipPath.h"
0036 #include "KoPathShape.h"
0037 #include "KoFilterEffectStack.h"
0038 #include <KoSnapData.h>
0039 
0040 #include <KoXmlWriter.h>
0041 #include <KoXmlNS.h>
0042 #include <KoUnit.h>
0043 
0044 #include <QPainter>
0045 #include <QVariant>
0046 #include <QPainterPath>
0047 #include <QList>
0048 #include <QMap>
0049 #include <QByteArray>
0050 #include <FlakeDebug.h>
0051 
0052 #include "kis_assert.h"
0053 
0054 #include <KisHandlePainterHelper.h>
0055 
0056 // KoShape::Private
0057 
0058 KoShape::SharedData::SharedData()
0059     : QSharedData()
0060     , size(50, 50)
0061     , shadow(0)
0062     , filterEffectStack(0)
0063     , transparency(0.0)
0064     , zIndex(0)
0065     , runThrough(0)
0066     , visible(true)
0067     , printable(true)
0068     , geometryProtected(false)
0069     , keepAspect(false)
0070     , selectable(true)
0071     , protectContent(false)
0072     , textRunAroundSide(KoShape::BiggestRunAroundSide)
0073     , textRunAroundDistanceLeft(0.0)
0074     , textRunAroundDistanceTop(0.0)
0075     , textRunAroundDistanceRight(0.0)
0076     , textRunAroundDistanceBottom(0.0)
0077     , textRunAroundThreshold(0.0)
0078     , textRunAroundContour(KoShape::ContourFull)
0079 { }
0080 
0081 KoShape::SharedData::SharedData(const SharedData &rhs)
0082     : QSharedData()
0083     , size(rhs.size)
0084     , shapeId(rhs.shapeId)
0085     , name(rhs.name)
0086     , localMatrix(rhs.localMatrix)
0087     , userData(rhs.userData ? rhs.userData->clone() : 0)
0088     , stroke(rhs.stroke)
0089     , fill(rhs.fill)
0090     , inheritBackground(rhs.inheritBackground)
0091     , inheritStroke(rhs.inheritStroke)
0092     , shadow(0) // WARNING: not implemented in Krita
0093     , clipPath(rhs.clipPath ? rhs.clipPath->clone() : 0)
0094     , clipMask(rhs.clipMask ? rhs.clipMask->clone() : 0)
0095     , additionalAttributes(rhs.additionalAttributes)
0096     , additionalStyleAttributes(rhs.additionalStyleAttributes)
0097     , filterEffectStack(0) // WARNING: not implemented in Krita
0098     , transparency(rhs.transparency)
0099     , hyperLink(rhs.hyperLink)
0100 
0101     , zIndex(rhs.zIndex)
0102     , runThrough(rhs.runThrough)
0103     , visible(rhs.visible)
0104     , printable(rhs.visible)
0105     , geometryProtected(rhs.geometryProtected)
0106     , keepAspect(rhs.keepAspect)
0107     , selectable(rhs.selectable)
0108     , protectContent(rhs.protectContent)
0109 
0110     , textRunAroundSide(rhs.textRunAroundSide)
0111     , textRunAroundDistanceLeft(rhs.textRunAroundDistanceLeft)
0112     , textRunAroundDistanceTop(rhs.textRunAroundDistanceTop)
0113     , textRunAroundDistanceRight(rhs.textRunAroundDistanceRight)
0114     , textRunAroundDistanceBottom(rhs.textRunAroundDistanceBottom)
0115     , textRunAroundThreshold(rhs.textRunAroundThreshold)
0116     , textRunAroundContour(rhs.textRunAroundContour)
0117 {
0118 }
0119 
0120 KoShape::SharedData::~SharedData()
0121 {
0122     if (shadow && !shadow->deref())
0123         delete shadow;
0124     if (filterEffectStack && !filterEffectStack->deref())
0125         delete filterEffectStack;
0126 }
0127 
0128 void KoShape::shapeChangedPriv(KoShape::ChangeType type)
0129 {
0130     if (d->parent)
0131         d->parent->model()->childChanged(this, type);
0132 
0133     this->shapeChanged(type);
0134 
0135     Q_FOREACH (KoShape * shape, d->dependees) {
0136         shape->shapeChanged(type, this);
0137     }
0138 
0139     Q_FOREACH (KoShape::ShapeChangeListener *listener, d->listeners) {
0140         listener->notifyShapeChangedImpl(type, this);
0141     }
0142 }
0143 
0144 void KoShape::addShapeManager(KoShapeManager *manager)
0145 {
0146     d->shapeManagers.insert(manager);
0147 }
0148 
0149 void KoShape::removeShapeManager(KoShapeManager *manager)
0150 {
0151     d->shapeManagers.remove(manager);
0152 }
0153 
0154 // ======== KoShape
0155 
0156 const qint16 KoShape::maxZIndex = std::numeric_limits<qint16>::max();
0157 const qint16 KoShape::minZIndex = std::numeric_limits<qint16>::min();
0158 
0159 KoShape::KoShape()
0160     : d(new Private()),
0161       s(new SharedData)
0162 {
0163     notifyChanged();
0164 }
0165 
0166 KoShape::KoShape(const KoShape &rhs)
0167     : d(new Private()),
0168       s(rhs.s)
0169 {
0170 }
0171 
0172 KoShape::~KoShape()
0173 {
0174     shapeChangedPriv(Deleted);
0175     d->listeners.clear();
0176     /**
0177      * The shape must have already been detached from all the parents and
0178      * shape managers. Otherwise we migh accidentally request some RTTI
0179      * information, which is not available anymore (we are in d-tor).
0180      *
0181      * TL;DR: fix the code that caused this destruction without unparenting
0182      *        instead of trying to remove these assert!
0183      */
0184     KIS_SAFE_ASSERT_RECOVER (!d->parent) {
0185         d->parent->removeShape(this);
0186     }
0187 
0188     KIS_SAFE_ASSERT_RECOVER (d->shapeManagers.isEmpty()) {
0189         Q_FOREACH (KoShapeManager *manager, d->shapeManagers) {
0190             manager->shapeInterface()->notifyShapeDestructed(this);
0191         }
0192         d->shapeManagers.clear();
0193     }
0194 }
0195 
0196 KoShape *KoShape::cloneShape() const
0197 {
0198     KIS_SAFE_ASSERT_RECOVER_NOOP(0 && "not implemented!");
0199     qWarning() << shapeId() << "cannot be cloned";
0200     return 0;
0201 }
0202 
0203 void KoShape::paintStroke(QPainter &painter) const
0204 {
0205     if (stroke()) {
0206         stroke()->paint(this, painter);
0207     }
0208 }
0209 
0210 void KoShape::scale(qreal sx, qreal sy)
0211 {
0212     QPointF pos = position();
0213     QTransform scaleMatrix;
0214     scaleMatrix.translate(pos.x(), pos.y());
0215     scaleMatrix.scale(sx, sy);
0216     scaleMatrix.translate(-pos.x(), -pos.y());
0217     s->localMatrix = s->localMatrix * scaleMatrix;
0218 
0219     notifyChanged();
0220     shapeChangedPriv(ScaleChanged);
0221 }
0222 
0223 void KoShape::rotate(qreal angle)
0224 {
0225     QPointF center = s->localMatrix.map(QPointF(0.5 * size().width(), 0.5 * size().height()));
0226     QTransform rotateMatrix;
0227     rotateMatrix.translate(center.x(), center.y());
0228     rotateMatrix.rotate(angle);
0229     rotateMatrix.translate(-center.x(), -center.y());
0230     s->localMatrix = s->localMatrix * rotateMatrix;
0231 
0232     notifyChanged();
0233     shapeChangedPriv(RotationChanged);
0234 }
0235 
0236 void KoShape::shear(qreal sx, qreal sy)
0237 {
0238     QPointF pos = position();
0239     QTransform shearMatrix;
0240     shearMatrix.translate(pos.x(), pos.y());
0241     shearMatrix.shear(sx, sy);
0242     shearMatrix.translate(-pos.x(), -pos.y());
0243     s->localMatrix = s->localMatrix * shearMatrix;
0244 
0245     notifyChanged();
0246     shapeChangedPriv(ShearChanged);
0247 }
0248 
0249 void KoShape::setSize(const QSizeF &newSize)
0250 {
0251     QSizeF oldSize(size());
0252 
0253     // always set size, as d->size and size() may vary
0254     setSizeImpl(newSize);
0255 
0256     if (oldSize == newSize)
0257         return;
0258 
0259     notifyChanged();
0260     shapeChangedPriv(SizeChanged);
0261 }
0262 
0263 void KoShape::setSizeImpl(const QSizeF &size) const
0264 {
0265     s->size = size;
0266 }
0267 
0268 void KoShape::setPosition(const QPointF &newPosition)
0269 {
0270     QPointF currentPos = position();
0271     if (newPosition == currentPos)
0272         return;
0273     QTransform translateMatrix;
0274     translateMatrix.translate(newPosition.x() - currentPos.x(), newPosition.y() - currentPos.y());
0275     s->localMatrix = s->localMatrix * translateMatrix;
0276 
0277     notifyChanged();
0278     shapeChangedPriv(PositionChanged);
0279 }
0280 
0281 bool KoShape::hitTest(const QPointF &position) const
0282 {
0283     if (d->parent && d->parent->isClipped(this) && !d->parent->hitTest(position))
0284         return false;
0285 
0286     QPointF point = absoluteTransformation().inverted().map(position);
0287     QRectF bb = outlineRect();
0288 
0289     if (s->stroke) {
0290         KoInsets insets;
0291         s->stroke->strokeInsets(this, insets);
0292         bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
0293     }
0294     if (bb.contains(point))
0295         return true;
0296 
0297     // if there is no shadow we can as well just leave
0298     if (! s->shadow)
0299         return false;
0300 
0301     // the shadow has an offset to the shape, so we simply
0302     // check if the position minus the shadow offset hits the shape
0303     point = absoluteTransformation().inverted().map(position - s->shadow->offset());
0304 
0305     return bb.contains(point);
0306 }
0307 
0308 QRectF KoShape::boundingRect() const
0309 {
0310 
0311     QTransform transform = absoluteTransformation();
0312     QRectF bb = outlineRect();
0313     if (s->stroke) {
0314         KoInsets insets;
0315         s->stroke->strokeInsets(this, insets);
0316         bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
0317     }
0318     bb = transform.mapRect(bb);
0319     if (s->shadow) {
0320         KoInsets insets;
0321         s->shadow->insets(insets);
0322         bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
0323     }
0324     if (s->filterEffectStack) {
0325         QRectF clipRect = s->filterEffectStack->clipRectForBoundingRect(outlineRect());
0326         bb |= transform.mapRect(clipRect);
0327     }
0328 
0329     return bb;
0330 }
0331 
0332 QRectF KoShape::boundingRect(const QList<KoShape *> &shapes)
0333 {
0334     QRectF boundingRect;
0335     Q_FOREACH (KoShape *shape, shapes) {
0336         boundingRect |= shape->boundingRect();
0337     }
0338     return boundingRect;
0339 }
0340 
0341 QRectF KoShape::absoluteOutlineRect() const
0342 {
0343     return absoluteTransformation().map(outline()).boundingRect();
0344 }
0345 
0346 QRectF KoShape::absoluteOutlineRect(const QList<KoShape *> &shapes)
0347 {
0348     QRectF absoluteOutlineRect;
0349     Q_FOREACH (KoShape *shape, shapes) {
0350         absoluteOutlineRect |= shape->absoluteOutlineRect();
0351     }
0352     return absoluteOutlineRect;
0353 }
0354 
0355 QTransform KoShape::absoluteTransformation() const
0356 {
0357     QTransform matrix;
0358     // apply parents matrix to inherit any transformations done there.
0359     KoShapeContainer * container = d->parent;
0360     if (container) {
0361         if (container->inheritsTransform(this)) {
0362             matrix = container->absoluteTransformation();
0363         } else {
0364             QSizeF containerSize = container->size();
0365             QPointF containerPos = container->absolutePosition() - QPointF(0.5 * containerSize.width(), 0.5 * containerSize.height());
0366             matrix.translate(containerPos.x(), containerPos.y());
0367         }
0368     }
0369 
0370     return s->localMatrix * matrix;
0371 }
0372 
0373 void KoShape::applyAbsoluteTransformation(const QTransform &matrix)
0374 {
0375     QTransform globalMatrix = absoluteTransformation();
0376     // the transformation is relative to the global coordinate system
0377     // but we want to change the local matrix, so convert the matrix
0378     // to be relative to the local coordinate system
0379     QTransform transformMatrix = globalMatrix * matrix * globalMatrix.inverted();
0380     applyTransformation(transformMatrix);
0381 }
0382 
0383 void KoShape::applyTransformation(const QTransform &matrix)
0384 {
0385     s->localMatrix = matrix * s->localMatrix;
0386     notifyChanged();
0387     shapeChangedPriv(GenericMatrixChange);
0388 }
0389 
0390 void KoShape::setTransformation(const QTransform &matrix)
0391 {
0392     s->localMatrix = matrix;
0393     notifyChanged();
0394     shapeChangedPriv(GenericMatrixChange);
0395 }
0396 
0397 QTransform KoShape::transformation() const
0398 {
0399     return s->localMatrix;
0400 }
0401 
0402 KoShape::ChildZOrderPolicy KoShape::childZOrderPolicy()
0403 {
0404     return ChildZDefault;
0405 }
0406 
0407 bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
0408 {
0409     /**
0410      * WARNING: Our definition of zIndex is not yet compatible with SVG2's
0411      *          definition. In SVG stacking context of groups with the same
0412      *          zIndex are **merged**, while in Krita the contents of groups
0413      *          is never merged. One group will always below than the other.
0414      *          Therefore, when zIndex of two groups inside the same parent
0415      *          coincide, the resulting painting order in Krita is
0416      *          **UNDEFINED**.
0417      *
0418      *          To avoid this trouble we use  KoShapeReorderCommand::mergeInShape()
0419      *          inside KoShapeCreateCommand.
0420      */
0421 
0422     /**
0423      * The algorithm below doesn't correctly handle the case when the two pointers actually
0424      * point to the same shape. So just check it in advance to guarantee strict weak ordering
0425      * relation requirement
0426      */
0427     if (s1 == s2) return false;
0428 
0429 
0430     // First sort according to runThrough which is sort of a master level
0431     KoShape *parentShapeS1 = s1->parent();
0432     KoShape *parentShapeS2 = s2->parent();
0433     int runThrough1 = s1->runThrough();
0434     int runThrough2 = s2->runThrough();
0435     while (parentShapeS1) {
0436         if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) {
0437             runThrough1 = parentShapeS1->runThrough();
0438         } else {
0439             runThrough1 = runThrough1 + parentShapeS1->runThrough();
0440         }
0441         parentShapeS1 = parentShapeS1->parent();
0442     }
0443 
0444     while (parentShapeS2) {
0445         if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) {
0446             runThrough2 = parentShapeS2->runThrough();
0447         } else {
0448             runThrough2 = runThrough2 + parentShapeS2->runThrough();
0449         }
0450         parentShapeS2 = parentShapeS2->parent();
0451     }
0452 
0453     if (runThrough1 > runThrough2) {
0454         return false;
0455     }
0456     if (runThrough1 < runThrough2) {
0457         return true;
0458     }
0459 
0460     // If on the same runThrough level then the zIndex is all that matters.
0461     //
0462     // We basically walk up through the parents until we find a common base parent
0463     // To do that we need two loops where the inner loop walks up through the parents
0464     // of s2 every time we step up one parent level on s1
0465     //
0466     // We don't update the index value until after we have seen that it's not a common base
0467     // That way we ensure that two children of a common base are sorted according to their respective
0468     // z value
0469     bool foundCommonParent = false;
0470     int index1 = s1->zIndex();
0471     int index2 = s2->zIndex();
0472     parentShapeS1 = s1;
0473     parentShapeS2 = s2;
0474     while (parentShapeS1 && !foundCommonParent) {
0475         parentShapeS2 = s2;
0476         index2 = parentShapeS2->zIndex();
0477         while (parentShapeS2) {
0478             if (parentShapeS2 == parentShapeS1) {
0479                 foundCommonParent = true;
0480                 break;
0481             }
0482             if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) {
0483                 index2 = parentShapeS2->zIndex();
0484             }
0485             parentShapeS2 = parentShapeS2->parent();
0486         }
0487 
0488         if (!foundCommonParent) {
0489             if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) {
0490                 index1 = parentShapeS1->zIndex();
0491             }
0492             parentShapeS1 = parentShapeS1->parent();
0493         }
0494     }
0495 
0496     // If the one shape is a parent/child of the other then sort so.
0497     if (s1 == parentShapeS2) {
0498         return true;
0499     }
0500     if (s2 == parentShapeS1) {
0501         return false;
0502     }
0503 
0504     // If we went that far then the z-Index is used for sorting.
0505     return index1 < index2;
0506 }
0507 
0508 void KoShape::setParent(KoShapeContainer *parent)
0509 {
0510 
0511     if (d->parent == parent) {
0512         return;
0513     }
0514 
0515     if (d->parent) {
0516         d->parent->shapeInterface()->removeShape(this);
0517         d->parent = 0;
0518     }
0519 
0520 
0521     KIS_SAFE_ASSERT_RECOVER_NOOP(parent != this);
0522 
0523     if (parent && parent != this) {
0524         d->parent = parent;
0525         parent->shapeInterface()->addShape(this);
0526     }
0527 
0528     notifyChanged();
0529     shapeChangedPriv(ParentChanged);
0530 }
0531 
0532 bool KoShape::inheritsTransformFromAny(const QList<KoShape *> ancestorsInQuestion) const
0533 {
0534     bool result = false;
0535 
0536     KoShape *shape = const_cast<KoShape*>(this);
0537     while (shape) {
0538         KoShapeContainer *parent = shape->parent();
0539         if (parent && !parent->inheritsTransform(shape)) {
0540             break;
0541         }
0542 
0543         if (ancestorsInQuestion.contains(shape)) {
0544             result = true;
0545             break;
0546         }
0547 
0548         shape = parent;
0549     }
0550 
0551     return result;
0552 }
0553 
0554 bool KoShape::hasCommonParent(const KoShape *shape) const
0555 {
0556     const KoShape *thisShape = this;
0557     while (thisShape) {
0558 
0559         const KoShape *otherShape = shape;
0560         while (otherShape) {
0561             if (thisShape == otherShape) {
0562                 return true;
0563             }
0564             otherShape = otherShape->parent();
0565         }
0566 
0567         thisShape = thisShape->parent();
0568     }
0569 
0570     return false;
0571 }
0572 
0573 qint16 KoShape::zIndex() const
0574 {
0575     return s->zIndex;
0576 }
0577 
0578 void KoShape::update() const
0579 {
0580 
0581     if (!d->shapeManagers.empty()) {
0582         const QRectF rect(boundingRect());
0583         Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
0584             manager->update(rect, this, true);
0585         }
0586     }
0587 }
0588 
0589 void KoShape::updateAbsolute(const QRectF &rect) const
0590 {
0591     if (rect.isEmpty() && !rect.isNull()) {
0592         return;
0593     }
0594 
0595 
0596     if (!d->shapeManagers.empty() && isVisible()) {
0597         Q_FOREACH (KoShapeManager *manager, d->shapeManagers) {
0598             manager->update(rect);
0599         }
0600     }
0601 }
0602 
0603 QPainterPath KoShape::outline() const
0604 {
0605     QPainterPath path;
0606     path.addRect(outlineRect());
0607     return path;
0608 }
0609 
0610 QRectF KoShape::outlineRect() const
0611 {
0612     const QSizeF s = size();
0613     return QRectF(QPointF(0, 0), QSizeF(qMax(s.width(),  qreal(0.0001)),
0614                                         qMax(s.height(), qreal(0.0001))));
0615 }
0616 
0617 QPainterPath KoShape::shadowOutline() const
0618 {
0619     if (background()) {
0620         return outline();
0621     }
0622 
0623     return QPainterPath();
0624 }
0625 
0626 QPointF KoShape::absolutePosition(KoFlake::AnchorPosition anchor) const
0627 {
0628     const QRectF rc = outlineRect();
0629 
0630     QPointF point = rc.topLeft();
0631 
0632     bool valid = false;
0633     QPointF anchoredPoint = KoFlake::anchorToPoint(anchor, rc, &valid);
0634     if (valid) {
0635         point = anchoredPoint;
0636     }
0637 
0638     return absoluteTransformation().map(point);
0639 }
0640 
0641 void KoShape::setAbsolutePosition(const QPointF &newPosition, KoFlake::AnchorPosition anchor)
0642 {
0643     QPointF currentAbsPosition = absolutePosition(anchor);
0644     QPointF translate = newPosition - currentAbsPosition;
0645     QTransform translateMatrix;
0646     translateMatrix.translate(translate.x(), translate.y());
0647     applyAbsoluteTransformation(translateMatrix);
0648     notifyChanged();
0649     shapeChangedPriv(PositionChanged);
0650 }
0651 
0652 void KoShape::copySettings(const KoShape *shape)
0653 {
0654     s->size = shape->size();
0655     s->zIndex = shape->zIndex();
0656     s->visible = shape->isVisible(false);
0657 
0658     // Ensure printable is true by default
0659     if (!s->visible)
0660         s->printable = true;
0661     else
0662         s->printable = shape->isPrintable();
0663 
0664     s->geometryProtected = shape->isGeometryProtected();
0665     s->protectContent = shape->isContentProtected();
0666     s->selectable = shape->isSelectable();
0667     s->keepAspect = shape->keepAspectRatio();
0668     s->localMatrix = shape->s->localMatrix;
0669 }
0670 
0671 void KoShape::notifyChanged()
0672 {
0673     Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
0674         manager->notifyShapeChanged(this);
0675     }
0676 }
0677 
0678 void KoShape::setUserData(KoShapeUserData *userData)
0679 {
0680     s->userData.reset(userData);
0681 }
0682 
0683 KoShapeUserData *KoShape::userData() const
0684 {
0685     return s->userData.data();
0686 }
0687 
0688 bool KoShape::hasTransparency() const
0689 {
0690     QSharedPointer<KoShapeBackground> bg = background();
0691 
0692     return !bg || bg->hasTransparency() || s->transparency > 0.0;
0693 }
0694 
0695 void KoShape::setTransparency(qreal transparency)
0696 {
0697     s->transparency = qBound<qreal>(0.0, transparency, 1.0);
0698 
0699     shapeChangedPriv(TransparencyChanged);
0700     notifyChanged();
0701 }
0702 
0703 qreal KoShape::transparency(bool recursive) const
0704 {
0705     if (!recursive || !parent()) {
0706         return s->transparency;
0707     } else {
0708         const qreal parentOpacity = 1.0-parent()->transparency(recursive);
0709         const qreal childOpacity = 1.0-s->transparency;
0710         return 1.0-(parentOpacity*childOpacity);
0711     }
0712 }
0713 
0714 KoInsets KoShape::strokeInsets() const
0715 {
0716     KoInsets answer;
0717     if (s->stroke)
0718         s->stroke->strokeInsets(this, answer);
0719     return answer;
0720 }
0721 
0722 qreal KoShape::rotation() const
0723 {
0724     // try to extract the rotation angle out of the local matrix
0725     // if it is a pure rotation matrix
0726 
0727     // check if the matrix has shearing mixed in
0728     if (fabs(fabs(s->localMatrix.m12()) - fabs(s->localMatrix.m21())) > 1e-10)
0729         return std::numeric_limits<qreal>::quiet_NaN();
0730     // check if the matrix has scaling mixed in
0731     if (fabs(s->localMatrix.m11() - s->localMatrix.m22()) > 1e-10)
0732         return std::numeric_limits<qreal>::quiet_NaN();
0733 
0734     // calculate the angle from the matrix elements
0735     qreal angle = atan2(-s->localMatrix.m21(), s->localMatrix.m11()) * 180.0 / M_PI;
0736     if (angle < 0.0)
0737         angle += 360.0;
0738 
0739     return angle;
0740 }
0741 
0742 QSizeF KoShape::size() const
0743 {
0744     return s->size;
0745 }
0746 
0747 QPointF KoShape::position() const
0748 {
0749     QPointF center = outlineRect().center();
0750     return s->localMatrix.map(center) - center;
0751 }
0752 
0753 KoShape::TextRunAroundSide KoShape::textRunAroundSide() const
0754 {
0755     return s->textRunAroundSide;
0756 }
0757 
0758 void KoShape::setTextRunAroundSide(TextRunAroundSide side, RunThroughLevel runThrought)
0759 {
0760 
0761     if (side == RunThrough) {
0762         if (runThrought == Background) {
0763             setRunThrough(-1);
0764         } else {
0765             setRunThrough(1);
0766         }
0767     } else {
0768         setRunThrough(0);
0769     }
0770 
0771     if ( s->textRunAroundSide == side) {
0772         return;
0773     }
0774 
0775     s->textRunAroundSide = side;
0776     notifyChanged();
0777     shapeChangedPriv(TextRunAroundChanged);
0778 }
0779 
0780 qreal KoShape::textRunAroundDistanceTop() const
0781 {
0782     return s->textRunAroundDistanceTop;
0783 }
0784 
0785 void KoShape::setTextRunAroundDistanceTop(qreal distance)
0786 {
0787     s->textRunAroundDistanceTop = distance;
0788 }
0789 
0790 qreal KoShape::textRunAroundDistanceLeft() const
0791 {
0792     return s->textRunAroundDistanceLeft;
0793 }
0794 
0795 void KoShape::setTextRunAroundDistanceLeft(qreal distance)
0796 {
0797     s->textRunAroundDistanceLeft = distance;
0798 }
0799 
0800 qreal KoShape::textRunAroundDistanceRight() const
0801 {
0802     return s->textRunAroundDistanceRight;
0803 }
0804 
0805 void KoShape::setTextRunAroundDistanceRight(qreal distance)
0806 {
0807     s->textRunAroundDistanceRight = distance;
0808 }
0809 
0810 qreal KoShape::textRunAroundDistanceBottom() const
0811 {
0812     return s->textRunAroundDistanceBottom;
0813 }
0814 
0815 void KoShape::setTextRunAroundDistanceBottom(qreal distance)
0816 {
0817     s->textRunAroundDistanceBottom = distance;
0818 }
0819 
0820 qreal KoShape::textRunAroundThreshold() const
0821 {
0822     return s->textRunAroundThreshold;
0823 }
0824 
0825 void KoShape::setTextRunAroundThreshold(qreal threshold)
0826 {
0827     s->textRunAroundThreshold = threshold;
0828 }
0829 
0830 KoShape::TextRunAroundContour KoShape::textRunAroundContour() const
0831 {
0832     return s->textRunAroundContour;
0833 }
0834 
0835 void KoShape::setTextRunAroundContour(KoShape::TextRunAroundContour contour)
0836 {
0837     s->textRunAroundContour = contour;
0838 }
0839 
0840 void KoShape::setBackground(QSharedPointer<KoShapeBackground> fill)
0841 {
0842     s->inheritBackground = false;
0843     s->fill = fill;
0844     shapeChangedPriv(BackgroundChanged);
0845     notifyChanged();
0846 }
0847 
0848 QSharedPointer<KoShapeBackground> KoShape::background() const
0849 {
0850 
0851     QSharedPointer<KoShapeBackground> bg;
0852 
0853     if (!s->inheritBackground) {
0854         bg = s->fill;
0855     } else if (parent()) {
0856         bg = parent()->background();
0857     }
0858 
0859     return bg;
0860 }
0861 
0862 void KoShape::setInheritBackground(bool value)
0863 {
0864 
0865     s->inheritBackground = value;
0866     if (s->inheritBackground) {
0867         s->fill.clear();
0868     }
0869 }
0870 
0871 bool KoShape::inheritBackground() const
0872 {
0873     return s->inheritBackground;
0874 }
0875 
0876 void KoShape::setZIndex(qint16 zIndex)
0877 {
0878     if (s->zIndex == zIndex)
0879         return;
0880     s->zIndex = zIndex;
0881     notifyChanged();
0882 }
0883 
0884 int KoShape::runThrough() const
0885 {
0886     return s->runThrough;
0887 }
0888 
0889 void KoShape::setRunThrough(short int runThrough)
0890 {
0891     s->runThrough = runThrough;
0892 }
0893 
0894 void KoShape::setVisible(bool on)
0895 {
0896     int _on = (on ? 1 : 0);
0897     if (s->visible == _on) return;
0898     s->visible = _on;
0899 }
0900 
0901 bool KoShape::isVisible(bool recursive) const
0902 {
0903     if (!recursive)
0904         return s->visible;
0905 
0906     if (!s->visible)
0907         return false;
0908 
0909     KoShapeContainer * parentShape = parent();
0910 
0911     if (parentShape) {
0912         return parentShape->isVisible(true);
0913     }
0914 
0915     return true;
0916 }
0917 
0918 void KoShape::setPrintable(bool on)
0919 {
0920     s->printable = on;
0921 }
0922 
0923 bool KoShape::isPrintable() const
0924 {
0925     if (s->visible)
0926         return s->printable;
0927     else
0928         return false;
0929 }
0930 
0931 void KoShape::setSelectable(bool selectable)
0932 {
0933     s->selectable = selectable;
0934 }
0935 
0936 bool KoShape::isSelectable() const
0937 {
0938     return s->selectable;
0939 }
0940 
0941 void KoShape::setGeometryProtected(bool on)
0942 {
0943     s->geometryProtected = on;
0944 }
0945 
0946 bool KoShape::isGeometryProtected() const
0947 {
0948     return s->geometryProtected;
0949 }
0950 
0951 void KoShape::setContentProtected(bool protect)
0952 {
0953     s->protectContent = protect;
0954 }
0955 
0956 bool KoShape::isContentProtected() const
0957 {
0958     return s->protectContent;
0959 }
0960 
0961 KoShapeContainer *KoShape::parent() const
0962 {
0963     return d->parent;
0964 }
0965 
0966 void KoShape::setKeepAspectRatio(bool keepAspect)
0967 {
0968     s->keepAspect = keepAspect;
0969 
0970     shapeChangedPriv(KeepAspectRatioChange);
0971     notifyChanged();
0972 }
0973 
0974 bool KoShape::keepAspectRatio() const
0975 {
0976     return s->keepAspect;
0977 }
0978 
0979 QString KoShape::shapeId() const
0980 {
0981     return s->shapeId;
0982 }
0983 
0984 void KoShape::setShapeId(const QString &id)
0985 {
0986     s->shapeId = id;
0987 }
0988 
0989 KoShapeStrokeModelSP KoShape::stroke() const
0990 {
0991 
0992     KoShapeStrokeModelSP stroke;
0993 
0994     if (!s->inheritStroke) {
0995         stroke = s->stroke;
0996     } else if (parent()) {
0997         stroke = parent()->stroke();
0998     }
0999 
1000     return stroke;
1001 }
1002 
1003 void KoShape::setStroke(KoShapeStrokeModelSP stroke)
1004 {
1005 
1006     s->inheritStroke = false;
1007     s->stroke = stroke;
1008     shapeChangedPriv(StrokeChanged);
1009     notifyChanged();
1010 }
1011 
1012 void KoShape::setInheritStroke(bool value)
1013 {
1014     s->inheritStroke = value;
1015     if (s->inheritStroke) {
1016         s->stroke.clear();
1017     }
1018 }
1019 
1020 bool KoShape::inheritStroke() const
1021 {
1022     return s->inheritStroke;
1023 }
1024 
1025 void KoShape::setShadow(KoShapeShadow *shadow)
1026 {
1027     if (s->shadow)
1028         s->shadow->deref();
1029     s->shadow = shadow;
1030     if (s->shadow) {
1031         s->shadow->ref();
1032         // TODO update changed area
1033     }
1034     shapeChangedPriv(ShadowChanged);
1035     notifyChanged();
1036 }
1037 
1038 KoShapeShadow *KoShape::shadow() const
1039 {
1040     return s->shadow;
1041 }
1042 
1043 void KoShape::setClipPath(KoClipPath *clipPath)
1044 {
1045     s->clipPath.reset(clipPath);
1046     shapeChangedPriv(ClipPathChanged);
1047     notifyChanged();
1048 }
1049 
1050 KoClipPath * KoShape::clipPath() const
1051 {
1052     return s->clipPath.data();
1053 }
1054 
1055 void KoShape::setClipMask(KoClipMask *clipMask)
1056 {
1057     s->clipMask.reset(clipMask);
1058     shapeChangedPriv(ClipMaskChanged);
1059     notifyChanged();
1060 }
1061 
1062 KoClipMask* KoShape::clipMask() const
1063 {
1064     return s->clipMask.data();
1065 }
1066 
1067 QTransform KoShape::transform() const
1068 {
1069     return s->localMatrix;
1070 }
1071 
1072 QString KoShape::name() const
1073 {
1074     return s->name;
1075 }
1076 
1077 void KoShape::setName(const QString &name)
1078 {
1079     s->name = name;
1080 }
1081 
1082 void KoShape::waitUntilReady(bool asynchronous) const
1083 {
1084     Q_UNUSED(asynchronous);
1085 }
1086 
1087 bool KoShape::isShapeEditable(bool recursive) const
1088 {
1089     if (!s->visible || s->geometryProtected)
1090         return false;
1091 
1092     if (recursive && d->parent) {
1093         return d->parent->isShapeEditable(true);
1094     }
1095 
1096     return true;
1097 }
1098 
1099 KisHandlePainterHelper KoShape::createHandlePainterHelperView(QPainter *painter, KoShape *shape, const KoViewConverter &converter, qreal handleRadius)
1100 {
1101     const QTransform originalPainterTransform = painter->transform();
1102 
1103     painter->setTransform(shape->absoluteTransformation() *
1104                           converter.documentToView() *
1105                           painter->transform());
1106 
1107     // move c-tor
1108     return KisHandlePainterHelper(painter, originalPainterTransform, handleRadius);
1109 }
1110 
1111 KisHandlePainterHelper KoShape::createHandlePainterHelperDocument(QPainter *painter, KoShape *shape, qreal handleRadius)
1112 {
1113     const QTransform originalPainterTransform = painter->transform();
1114 
1115     painter->setTransform(shape->absoluteTransformation() *
1116                           painter->transform());
1117 
1118     // move c-tor
1119     return KisHandlePainterHelper(painter, originalPainterTransform, handleRadius);
1120 }
1121 
1122 
1123 QPointF KoShape::shapeToDocument(const QPointF &point) const
1124 {
1125     return absoluteTransformation().map(point);
1126 }
1127 
1128 QRectF KoShape::shapeToDocument(const QRectF &rect) const
1129 {
1130     return absoluteTransformation().mapRect(rect);
1131 }
1132 
1133 QPointF KoShape::documentToShape(const QPointF &point) const
1134 {
1135     return absoluteTransformation().inverted().map(point);
1136 }
1137 
1138 QRectF KoShape::documentToShape(const QRectF &rect) const
1139 {
1140     return absoluteTransformation().inverted().mapRect(rect);
1141 }
1142 
1143 bool KoShape::addDependee(KoShape *shape)
1144 {
1145     if (! shape)
1146         return false;
1147 
1148     // refuse to establish a circular dependency
1149     if (shape->hasDependee(this))
1150         return false;
1151 
1152     if (! d->dependees.contains(shape))
1153         d->dependees.append(shape);
1154 
1155     return true;
1156 }
1157 
1158 void KoShape::removeDependee(KoShape *shape)
1159 {
1160     int index = d->dependees.indexOf(shape);
1161     if (index >= 0)
1162         d->dependees.removeAt(index);
1163 }
1164 
1165 bool KoShape::hasDependee(KoShape *shape) const
1166 {
1167     return d->dependees.contains(shape);
1168 }
1169 
1170 QList<KoShape*> KoShape::dependees() const
1171 {
1172     return d->dependees;
1173 }
1174 
1175 void KoShape::shapeChanged(ChangeType type, KoShape *shape)
1176 {
1177     Q_UNUSED(type);
1178     Q_UNUSED(shape);
1179 }
1180 
1181 KoSnapData KoShape::snapData() const
1182 {
1183     return KoSnapData();
1184 }
1185 
1186 void KoShape::setAdditionalAttribute(const QString &name, const QString &value)
1187 {
1188     s->additionalAttributes.insert(name, value);
1189 }
1190 
1191 void KoShape::removeAdditionalAttribute(const QString &name)
1192 {
1193     s->additionalAttributes.remove(name);
1194 }
1195 
1196 bool KoShape::hasAdditionalAttribute(const QString &name) const
1197 {
1198     return s->additionalAttributes.contains(name);
1199 }
1200 
1201 QString KoShape::additionalAttribute(const QString &name) const
1202 {
1203     return s->additionalAttributes.value(name);
1204 }
1205 
1206 void KoShape::setAdditionalStyleAttribute(const char *name, const QString &value)
1207 {
1208     s->additionalStyleAttributes.insert(name, value);
1209 }
1210 
1211 void KoShape::removeAdditionalStyleAttribute(const char *name)
1212 {
1213     s->additionalStyleAttributes.remove(name);
1214 }
1215 
1216 KoFilterEffectStack *KoShape::filterEffectStack() const
1217 {
1218     return s->filterEffectStack;
1219 }
1220 
1221 void KoShape::setFilterEffectStack(KoFilterEffectStack *filterEffectStack)
1222 {
1223     if (s->filterEffectStack)
1224         s->filterEffectStack->deref();
1225     s->filterEffectStack = filterEffectStack;
1226     if (s->filterEffectStack) {
1227         s->filterEffectStack->ref();
1228     }
1229     notifyChanged();
1230 }
1231 
1232 QSet<KoShape*> KoShape::toolDelegates() const
1233 {
1234     return d->toolDelegates;
1235 }
1236 
1237 void KoShape::setToolDelegates(const QSet<KoShape*> &delegates)
1238 {
1239     d->toolDelegates = delegates;
1240 }
1241 
1242 QString KoShape::hyperLink () const
1243 {
1244     return s->hyperLink;
1245 }
1246 
1247 void KoShape::setHyperLink(const QString &hyperLink)
1248 {
1249     s->hyperLink = hyperLink;
1250 }
1251 
1252 KoShape::ShapeChangeListener::~ShapeChangeListener()
1253 {
1254     Q_FOREACH(KoShape *shape, m_registeredShapes) {
1255         shape->removeShapeChangeListener(this);
1256     }
1257 }
1258 
1259 void KoShape::ShapeChangeListener::registerShape(KoShape *shape)
1260 {
1261     KIS_SAFE_ASSERT_RECOVER_RETURN(!m_registeredShapes.contains(shape));
1262     m_registeredShapes.append(shape);
1263 }
1264 
1265 void KoShape::ShapeChangeListener::unregisterShape(KoShape *shape)
1266 {
1267     KIS_SAFE_ASSERT_RECOVER_RETURN(m_registeredShapes.contains(shape));
1268     m_registeredShapes.removeAll(shape);
1269 }
1270 
1271 void KoShape::ShapeChangeListener::notifyShapeChangedImpl(KoShape::ChangeType type, KoShape *shape)
1272 {
1273     KIS_SAFE_ASSERT_RECOVER_RETURN(m_registeredShapes.contains(shape));
1274 
1275     notifyShapeChanged(type, shape);
1276 
1277     if (type == KoShape::Deleted) {
1278         unregisterShape(shape);
1279     }
1280 }
1281 
1282 void KoShape::addShapeChangeListener(KoShape::ShapeChangeListener *listener)
1283 {
1284 
1285     KIS_SAFE_ASSERT_RECOVER_RETURN(!d->listeners.contains(listener));
1286     listener->registerShape(this);
1287     d->listeners.append(listener);
1288 }
1289 
1290 void KoShape::removeShapeChangeListener(KoShape::ShapeChangeListener *listener)
1291 {
1292 
1293     KIS_SAFE_ASSERT_RECOVER_RETURN(d->listeners.contains(listener));
1294     d->listeners.removeAll(listener);
1295     listener->unregisterShape(this);
1296 }
1297 
1298 QList<KoShape::ShapeChangeListener *> KoShape::listeners() const
1299 {
1300     return d->listeners;
1301 }
1302 
1303 QList<KoShape *> KoShape::linearizeSubtree(const QList<KoShape *> &shapes)
1304 {
1305     QList<KoShape *> result;
1306 
1307     Q_FOREACH (KoShape *shape, shapes) {
1308         result << shape;
1309 
1310         KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
1311         if (container) {
1312             result << linearizeSubtree(container->shapes());
1313         }
1314     }
1315 
1316     return result;
1317 }
1318 
1319 QList<KoShape *> KoShape::linearizeSubtreeSorted(const QList<KoShape *> &shapes)
1320 {
1321     QList<KoShape*> sortedShapes = shapes;
1322     std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex);
1323 
1324     QList<KoShape *> result;
1325 
1326     Q_FOREACH (KoShape *shape, sortedShapes) {
1327         result << shape;
1328 
1329         KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
1330         if (container) {
1331             result << linearizeSubtreeSorted(container->shapes());
1332         }
1333     }
1334 
1335     return result;
1336 }