Warning, file /office/calligra/libs/flake/KoShape.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* This file is part of the KDE project
0002    Copyright (C) 2006 C. Boemann Rasmussen <cbo@boemann.dk>
0003    Copyright (C) 2006-2010 Thomas Zander <zander@kde.org>
0004    Copyright (C) 2006-2010 Thorsten Zachmann <zachmann@kde.org>
0005    Copyright (C) 2007-2009,2011 Jan Hambrecht <jaham@gmx.net>
0006    CopyRight (C) 2010 Boudewijn Rempt <boud@kogmbh.com>
0007 
0008    This library is free software; you can redistribute it and/or
0009    modify it under the terms of the GNU Library General Public
0010    License as published by the Free Software Foundation; either
0011    version 2 of the License, or (at your option) any later version.
0012 
0013    This library is distributed in the hope that it will be useful,
0014    but WITHOUT ANY WARRANTY; without even the implied warranty of
0015    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0016    Library General Public License for more details.
0017 
0018    You should have received a copy of the GNU Library General Public License
0019    along with this library; see the file COPYING.LIB.  If not, write to
0020    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0021    Boston, MA 02110-1301, USA.
0022 */
0023 
0024 #include "KoShape.h"
0025 #include "KoShape_p.h"
0026 #include "KoShapeContainer.h"
0027 #include "KoShapeLayer.h"
0028 #include "KoShapeContainerModel.h"
0029 #include "KoSelection.h"
0030 #include "KoPointerEvent.h"
0031 #include "KoInsets.h"
0032 #include "KoShapeStrokeModel.h"
0033 #include "KoShapeBackground.h"
0034 #include "KoColorBackground.h"
0035 #include "KoHatchBackground.h"
0036 #include "KoGradientBackground.h"
0037 #include "KoPatternBackground.h"
0038 #include "KoShapeManager.h"
0039 #include "KoShapeUserData.h"
0040 #include "KoShapeApplicationData.h"
0041 #include "KoShapeSavingContext.h"
0042 #include "KoShapeLoadingContext.h"
0043 #include "KoViewConverter.h"
0044 #include "KoShapeStroke.h"
0045 #include "KoShapeShadow.h"
0046 #include "KoClipPath.h"
0047 #include "KoPathShape.h"
0048 #include "KoEventAction.h"
0049 #include "KoEventActionRegistry.h"
0050 #include "KoOdfWorkaround.h"
0051 #include "KoFilterEffectStack.h"
0052 #include <KoSnapData.h>
0053 #include <KoElementReference.h>
0054 
0055 #include <KoXmlReader.h>
0056 #include <KoXmlWriter.h>
0057 #include <KoXmlNS.h>
0058 #include <KoGenStyle.h>
0059 #include <KoGenStyles.h>
0060 #include <KoUnit.h>
0061 #include <KoOdfStylesReader.h>
0062 #include <KoOdfGraphicStyles.h>
0063 #include <KoOdfLoadingContext.h>
0064 #include <KoStyleStack.h>
0065 #include <KoBorder.h>
0066 
0067 #include <QPainter>
0068 #include <QVariant>
0069 #include <QPainterPath>
0070 #include <QList>
0071 #include <QMap>
0072 #include <QByteArray>
0073 #include <FlakeDebug.h>
0074 
0075 #include <limits>
0076 #include "KoOdfGradientBackground.h"
0077 
0078 // KoShapePrivate
0079 
0080 KoShapePrivate::KoShapePrivate(KoShape *shape)
0081     : q_ptr(shape),
0082       size(50, 50),
0083       parent(0),
0084       userData(0),
0085       appData(0),
0086       stroke(0),
0087       shadow(0),
0088       border(0),
0089       clipPath(0),
0090       filterEffectStack(0),
0091       transparency(0.0),
0092       zIndex(0),
0093       runThrough(0),
0094       visible(true),
0095       printable(true),
0096       keepAspect(false),
0097       detectCollision(false),
0098       textRunAroundSide(KoShape::BiggestRunAroundSide),
0099       textRunAroundDistanceLeft(0.0),
0100       textRunAroundDistanceTop(0.0),
0101       textRunAroundDistanceRight(0.0),
0102       textRunAroundDistanceBottom(0.0),
0103       textRunAroundThreshold(0.0),
0104       textRunAroundContour(KoShape::ContourFull),
0105       anchor(0),
0106       minimumHeight(0.0)
0107 {
0108     // All interactions allowed by default
0109     allowedInteractions = KoShape::MoveAllowed
0110                         | KoShape::ResizeAllowed
0111                         | KoShape::ShearingAllowed
0112                         | KoShape::RotationAllowed
0113                         | KoShape::SelectionAllowed
0114                         | KoShape::ContentChangeAllowed
0115                         | KoShape::DeletionAllowed;
0116 
0117     connectors[KoConnectionPoint::TopConnectionPoint] = KoConnectionPoint::defaultConnectionPoint(KoConnectionPoint::TopConnectionPoint);
0118     connectors[KoConnectionPoint::RightConnectionPoint] = KoConnectionPoint::defaultConnectionPoint(KoConnectionPoint::RightConnectionPoint);
0119     connectors[KoConnectionPoint::BottomConnectionPoint] = KoConnectionPoint::defaultConnectionPoint(KoConnectionPoint::BottomConnectionPoint);
0120     connectors[KoConnectionPoint::LeftConnectionPoint] = KoConnectionPoint::defaultConnectionPoint(KoConnectionPoint::LeftConnectionPoint);
0121     connectors[KoConnectionPoint::FirstCustomConnectionPoint] = KoConnectionPoint(QPointF(0.5, 0.5), KoConnectionPoint::AllDirections, KoConnectionPoint::AlignCenter);
0122 }
0123 
0124 KoShapePrivate::~KoShapePrivate()
0125 {
0126     Q_Q(KoShape);
0127     if (parent)
0128         parent->removeShape(q);
0129     foreach(KoShapeManager *manager, shapeManagers) {
0130         manager->remove(q);
0131         manager->removeAdditional(q);
0132     }
0133     delete userData;
0134     delete appData;
0135     if (stroke && !stroke->deref())
0136         delete stroke;
0137     if (shadow && !shadow->deref())
0138         delete shadow;
0139     if (filterEffectStack && !filterEffectStack->deref())
0140         delete filterEffectStack;
0141     delete clipPath;
0142     qDeleteAll(eventActions);
0143 }
0144 
0145 void KoShapePrivate::shapeChanged(KoShape::ChangeType type)
0146 {
0147     Q_Q(KoShape);
0148     if (parent)
0149         parent->model()->childChanged(q, type);
0150     q->shapeChanged(type);
0151     foreach(KoShape * shape, dependees)
0152         shape->shapeChanged(type, q);
0153 }
0154 
0155 void KoShapePrivate::updateStroke()
0156 {
0157     Q_Q(KoShape);
0158     if (stroke == 0)
0159         return;
0160     KoInsets insets;
0161     stroke->strokeInsets(q, insets);
0162     QSizeF inner = q->size();
0163     // update left
0164     q->update(QRectF(-insets.left, -insets.top, insets.left,
0165                      inner.height() + insets.top + insets.bottom));
0166     // update top
0167     q->update(QRectF(-insets.left, -insets.top,
0168                      inner.width() + insets.left + insets.right, insets.top));
0169     // update right
0170     q->update(QRectF(inner.width(), -insets.top, insets.right,
0171                      inner.height() + insets.top + insets.bottom));
0172     // update bottom
0173     q->update(QRectF(-insets.left, inner.height(),
0174                      inner.width() + insets.left + insets.right, insets.bottom));
0175 }
0176 
0177 void KoShapePrivate::addShapeManager(KoShapeManager *manager)
0178 {
0179     shapeManagers.insert(manager);
0180 }
0181 
0182 void KoShapePrivate::removeShapeManager(KoShapeManager *manager)
0183 {
0184     shapeManagers.remove(manager);
0185 }
0186 
0187 void KoShapePrivate::convertFromShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
0188 {
0189     switch(point.alignment) {
0190         case KoConnectionPoint::AlignNone:
0191             point.position = KoFlake::toRelative(point.position, shapeSize);
0192             point.position.rx() = qBound<qreal>(0.0, point.position.x(), 1.0);
0193             point.position.ry() = qBound<qreal>(0.0, point.position.y(), 1.0);
0194             break;
0195         case KoConnectionPoint::AlignRight:
0196             point.position.rx() -= shapeSize.width();
0197         case KoConnectionPoint::AlignLeft:
0198             point.position.ry() = 0.5*shapeSize.height();
0199             break;
0200         case KoConnectionPoint::AlignBottom:
0201             point.position.ry() -= shapeSize.height();
0202         case KoConnectionPoint::AlignTop:
0203             point.position.rx() = 0.5*shapeSize.width();
0204             break;
0205         case KoConnectionPoint::AlignTopLeft:
0206             // nothing to do here
0207             break;
0208         case KoConnectionPoint::AlignTopRight:
0209             point.position.rx() -= shapeSize.width();
0210             break;
0211         case KoConnectionPoint::AlignBottomLeft:
0212             point.position.ry() -= shapeSize.height();
0213             break;
0214         case KoConnectionPoint::AlignBottomRight:
0215             point.position.rx() -= shapeSize.width();
0216             point.position.ry() -= shapeSize.height();
0217             break;
0218         case KoConnectionPoint::AlignCenter:
0219             point.position.rx() -= 0.5 * shapeSize.width();
0220             point.position.ry() -= 0.5 * shapeSize.height();
0221             break;
0222     }
0223 }
0224 
0225 void KoShapePrivate::convertToShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
0226 {
0227     switch(point.alignment) {
0228         case KoConnectionPoint::AlignNone:
0229             point.position = KoFlake::toAbsolute(point.position, shapeSize);
0230             break;
0231         case KoConnectionPoint::AlignRight:
0232             point.position.rx() += shapeSize.width();
0233         case KoConnectionPoint::AlignLeft:
0234             point.position.ry() = 0.5*shapeSize.height();
0235             break;
0236         case KoConnectionPoint::AlignBottom:
0237             point.position.ry() += shapeSize.height();
0238         case KoConnectionPoint::AlignTop:
0239             point.position.rx() = 0.5*shapeSize.width();
0240             break;
0241         case KoConnectionPoint::AlignTopLeft:
0242             // nothing to do here
0243             break;
0244         case KoConnectionPoint::AlignTopRight:
0245             point.position.rx() += shapeSize.width();
0246             break;
0247         case KoConnectionPoint::AlignBottomLeft:
0248             point.position.ry() += shapeSize.height();
0249             break;
0250         case KoConnectionPoint::AlignBottomRight:
0251             point.position.rx() += shapeSize.width();
0252             point.position.ry() += shapeSize.height();
0253             break;
0254         case KoConnectionPoint::AlignCenter:
0255             point.position.rx() += 0.5 * shapeSize.width();
0256             point.position.ry() += 0.5 * shapeSize.height();
0257             break;
0258     }
0259 }
0260 
0261 // static
0262 QString KoShapePrivate::getStyleProperty(const char *property, KoShapeLoadingContext &context)
0263 {
0264     KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
0265     QString value;
0266 
0267     if (styleStack.hasProperty(KoXmlNS::draw, property)) {
0268         value = styleStack.property(KoXmlNS::draw, property);
0269     }
0270 
0271     return value;
0272 }
0273 
0274 
0275 
0276 // ======== KoShape
0277 KoShape::KoShape()
0278     : d_ptr(new KoShapePrivate(this))
0279 {
0280     notifyChanged();
0281 }
0282 
0283 KoShape::KoShape(KoShapePrivate &dd)
0284     : d_ptr(&dd)
0285 {
0286 }
0287 
0288 KoShape::~KoShape()
0289 {
0290     Q_D(KoShape);
0291     d->shapeChanged(Deleted);
0292     delete d_ptr;
0293 }
0294 
0295 void KoShape::scale(qreal sx, qreal sy)
0296 {
0297     Q_D(KoShape);
0298     QPointF pos = position();
0299     QTransform scaleMatrix;
0300     scaleMatrix.translate(pos.x(), pos.y());
0301     scaleMatrix.scale(sx, sy);
0302     scaleMatrix.translate(-pos.x(), -pos.y());
0303     d->localMatrix = d->localMatrix * scaleMatrix;
0304 
0305     notifyChanged();
0306     d->shapeChanged(ScaleChanged);
0307 }
0308 
0309 void KoShape::rotate(qreal angle)
0310 {
0311     Q_D(KoShape);
0312     QPointF center = d->localMatrix.map(QPointF(0.5 * size().width(), 0.5 * size().height()));
0313     QTransform rotateMatrix;
0314     rotateMatrix.translate(center.x(), center.y());
0315     rotateMatrix.rotate(angle);
0316     rotateMatrix.translate(-center.x(), -center.y());
0317     d->localMatrix = d->localMatrix * rotateMatrix;
0318 
0319     notifyChanged();
0320     d->shapeChanged(RotationChanged);
0321 }
0322 
0323 void KoShape::shear(qreal sx, qreal sy)
0324 {
0325     Q_D(KoShape);
0326     QPointF pos = position();
0327     QTransform shearMatrix;
0328     shearMatrix.translate(pos.x(), pos.y());
0329     shearMatrix.shear(sx, sy);
0330     shearMatrix.translate(-pos.x(), -pos.y());
0331     d->localMatrix = d->localMatrix * shearMatrix;
0332 
0333     notifyChanged();
0334     d->shapeChanged(ShearChanged);
0335 }
0336 
0337 void KoShape::setSize(const QSizeF &newSize)
0338 {
0339     Q_D(KoShape);
0340     QSizeF oldSize(size());
0341 
0342     // always set size, as d->size and size() may vary
0343     d->size = newSize;
0344 
0345     if (oldSize == newSize)
0346         return;
0347 
0348     notifyChanged();
0349     d->shapeChanged(SizeChanged);
0350 }
0351 
0352 void KoShape::setPosition(const QPointF &newPosition)
0353 {
0354     Q_D(KoShape);
0355     QPointF currentPos = position();
0356     if (newPosition == currentPos)
0357         return;
0358     QTransform translateMatrix;
0359     translateMatrix.translate(newPosition.x() - currentPos.x(), newPosition.y() - currentPos.y());
0360     d->localMatrix = d->localMatrix * translateMatrix;
0361 
0362     notifyChanged();
0363     d->shapeChanged(PositionChanged);
0364 }
0365 
0366 bool KoShape::hitTest(const QPointF &position) const
0367 {
0368     Q_D(const KoShape);
0369     if (d->parent && d->parent->isClipped(this) && !d->parent->hitTest(position))
0370         return false;
0371 
0372     QPointF point = absoluteTransformation(0).inverted().map(position);
0373     QRectF bb(QPointF(), size());
0374     if (d->stroke) {
0375         KoInsets insets;
0376         d->stroke->strokeInsets(this, insets);
0377         bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
0378     }
0379     if (bb.contains(point))
0380         return true;
0381 
0382     // if there is no shadow we can as well just leave
0383     if (! d->shadow)
0384         return false;
0385 
0386     // the shadow has an offset to the shape, so we simply
0387     // check if the position minus the shadow offset hits the shape
0388     point = absoluteTransformation(0).inverted().map(position - d->shadow->offset());
0389 
0390     return bb.contains(point);
0391 }
0392 
0393 QRectF KoShape::boundingRect() const
0394 {
0395     Q_D(const KoShape);
0396 
0397     QTransform transform = absoluteTransformation(0);
0398     QRectF bb = outlineRect();
0399     if (d->stroke) {
0400         KoInsets insets;
0401         d->stroke->strokeInsets(this, insets);
0402         bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
0403     }
0404     bb = transform.mapRect(bb);
0405     if (d->shadow) {
0406         KoInsets insets;
0407         d->shadow->insets(insets);
0408         bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
0409     }
0410     if (d->filterEffectStack) {
0411         QRectF clipRect = d->filterEffectStack->clipRectForBoundingRect(outlineRect());
0412         bb |= transform.mapRect(clipRect);
0413     }
0414 
0415     return bb;
0416 }
0417 
0418 QTransform KoShape::absoluteTransformation(const KoViewConverter *converter) const
0419 {
0420     Q_D(const KoShape);
0421     QTransform matrix;
0422     // apply parents matrix to inherit any transformations done there.
0423     KoShapeContainer * container = d->parent;
0424     if (container) {
0425         if (container->inheritsTransform(this)) {
0426             // We do need to pass the converter here, otherwise the parent's
0427             // translation is not inherited.
0428             matrix = container->absoluteTransformation(converter);
0429         } else {
0430             QSizeF containerSize = container->size();
0431             QPointF containerPos = container->absolutePosition() - QPointF(0.5 * containerSize.width(), 0.5 * containerSize.height());
0432             if (converter)
0433                 containerPos = converter->documentToView(containerPos);
0434             matrix.translate(containerPos.x(), containerPos.y());
0435         }
0436     }
0437 
0438     if (converter) {
0439         QPointF pos = d->localMatrix.map(QPointF());
0440         QPointF trans = converter->documentToView(pos) - pos;
0441         matrix.translate(trans.x(), trans.y());
0442     }
0443 
0444     return d->localMatrix * matrix;
0445 }
0446 
0447 void KoShape::applyAbsoluteTransformation(const QTransform &matrix)
0448 {
0449     QTransform globalMatrix = absoluteTransformation(0);
0450     // the transformation is relative to the global coordinate system
0451     // but we want to change the local matrix, so convert the matrix
0452     // to be relative to the local coordinate system
0453     QTransform transformMatrix = globalMatrix * matrix * globalMatrix.inverted();
0454     applyTransformation(transformMatrix);
0455 }
0456 
0457 void KoShape::applyTransformation(const QTransform &matrix)
0458 {
0459     Q_D(KoShape);
0460     d->localMatrix = matrix * d->localMatrix;
0461     notifyChanged();
0462     d->shapeChanged(GenericMatrixChange);
0463 }
0464 
0465 void KoShape::setTransformation(const QTransform &matrix)
0466 {
0467     Q_D(KoShape);
0468     d->localMatrix = matrix;
0469     notifyChanged();
0470     d->shapeChanged(GenericMatrixChange);
0471 }
0472 
0473 QTransform KoShape::transformation() const
0474 {
0475     Q_D(const KoShape);
0476     return d->localMatrix;
0477 }
0478 
0479 KoShape::ChildZOrderPolicy KoShape::childZOrderPolicy()
0480 {
0481     return ChildZDefault;
0482 }
0483 
0484 bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
0485 {
0486     // First sort according to runThrough which is sort of a master level
0487     KoShape *parentShapeS1 = s1->parent();
0488     KoShape *parentShapeS2 = s2->parent();
0489     int runThrough1 = s1->runThrough();
0490     int runThrough2 = s2->runThrough();
0491     while (parentShapeS1) {
0492         if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) {
0493             runThrough1 = parentShapeS1->runThrough();
0494         } else {
0495             runThrough1 = runThrough1 + parentShapeS1->runThrough();
0496         }
0497         parentShapeS1 = parentShapeS1->parent();
0498     }
0499 
0500     while (parentShapeS2) {
0501         if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) {
0502             runThrough2 = parentShapeS2->runThrough();
0503         } else {
0504             runThrough2 = runThrough2 + parentShapeS2->runThrough();
0505         }
0506         parentShapeS2 = parentShapeS2->parent();
0507     }
0508 
0509     if (runThrough1 > runThrough2) {
0510         return false;
0511     }
0512     if (runThrough1 < runThrough2) {
0513         return true;
0514     }
0515 
0516     // If on the same runThrough level then the zIndex is all that matters.
0517     //
0518     // We basically walk up through the parents until we find a common base parent
0519     // To do that we need two loops where the inner loop walks up through the parents
0520     // of s2 every time we step up one parent level on s1
0521     //
0522     // We don't update the index value until after we have seen that it's not a common base
0523     // That way we ensure that two children of a common base are sorted according to their respective
0524     // z value
0525     bool foundCommonParent = false;
0526     int index1 = s1->zIndex();
0527     int index2 = s2->zIndex();
0528     parentShapeS1 = s1;
0529     parentShapeS2 = s2;
0530     while (parentShapeS1 && !foundCommonParent) {
0531         parentShapeS2 = s2;
0532         index2 = parentShapeS2->zIndex();
0533         while (parentShapeS2) {
0534             if (parentShapeS2 == parentShapeS1) {
0535                 foundCommonParent = true;
0536                 break;
0537             }
0538             if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) {
0539                 index2 = parentShapeS2->zIndex();
0540             }
0541             parentShapeS2 = parentShapeS2->parent();
0542         }
0543 
0544         if (!foundCommonParent) {
0545             if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) {
0546                 index1 = parentShapeS1->zIndex();
0547             }
0548             parentShapeS1 = parentShapeS1->parent();
0549         }
0550     }
0551 
0552     // If the one shape is a parent/child of the other then sort so.
0553     if (s1 == parentShapeS2) {
0554         return true;
0555     }
0556     if (s2 == parentShapeS1) {
0557         return false;
0558     }
0559 
0560     // If we went that far then the z-Index is used for sorting.
0561     return index1 < index2;
0562 }
0563 
0564 void KoShape::setParent(KoShapeContainer *parent)
0565 {
0566     Q_D(KoShape);
0567     if (d->parent == parent)
0568         return;
0569     KoShapeContainer *oldParent = d->parent;
0570     d->parent = 0; // avoids recursive removing
0571     if (oldParent)
0572         oldParent->removeShape(this);
0573     if (parent && parent != this) {
0574         d->parent = parent;
0575         parent->addShape(this);
0576     }
0577     notifyChanged();
0578     d->shapeChanged(ParentChanged);
0579 }
0580 
0581 int KoShape::zIndex() const
0582 {
0583     Q_D(const KoShape);
0584     return d->zIndex;
0585 }
0586 
0587 void KoShape::update() const
0588 {
0589     Q_D(const KoShape);
0590 
0591     if (!d->shapeManagers.empty()) {
0592         QRectF rect(boundingRect());
0593         foreach(KoShapeManager * manager, d->shapeManagers) {
0594             manager->update(rect, this, true);
0595         }
0596     }
0597 }
0598 
0599 void KoShape::update(const QRectF &rect) const
0600 {
0601 
0602     if (rect.isEmpty() && !rect.isNull()) {
0603         return;
0604     }
0605 
0606     Q_D(const KoShape);
0607 
0608     if (!d->shapeManagers.empty() && isVisible()) {
0609         QRectF rc(absoluteTransformation(0).mapRect(rect));
0610         foreach(KoShapeManager * manager, d->shapeManagers) {
0611             manager->update(rc);
0612         }
0613     }
0614 }
0615 
0616 QPainterPath KoShape::outline() const
0617 {
0618     QPainterPath path;
0619     path.addRect(outlineRect());
0620     return path;
0621 }
0622 
0623 QRectF KoShape::outlineRect() const
0624 {
0625     const QSizeF s = size();
0626     return QRectF(QPointF(0, 0), QSizeF(qMax(s.width(),  qreal(0.0001)),
0627                                         qMax(s.height(), qreal(0.0001))));
0628 }
0629 
0630 QPainterPath KoShape::shadowOutline() const
0631 {
0632     Q_D(const KoShape);
0633     if (d->fill) {
0634         return outline();
0635     }
0636 
0637     return QPainterPath();
0638 }
0639 
0640 QPointF KoShape::absolutePosition(KoFlake::Position anchor) const
0641 {
0642     QPointF point;
0643     switch (anchor) {
0644     case KoFlake::TopLeftCorner: break;
0645     case KoFlake::TopRightCorner: point = QPointF(size().width(), 0.0); break;
0646     case KoFlake::BottomLeftCorner: point = QPointF(0.0, size().height()); break;
0647     case KoFlake::BottomRightCorner: point = QPointF(size().width(), size().height()); break;
0648     case KoFlake::CenteredPosition: point = QPointF(size().width() / 2.0, size().height() / 2.0); break;
0649     }
0650     return absoluteTransformation(0).map(point);
0651 }
0652 
0653 void KoShape::setAbsolutePosition(const QPointF &newPosition, KoFlake::Position anchor)
0654 {
0655     Q_D(KoShape);
0656     QPointF currentAbsPosition = absolutePosition(anchor);
0657     QPointF translate = newPosition - currentAbsPosition;
0658     QTransform translateMatrix;
0659     translateMatrix.translate(translate.x(), translate.y());
0660     applyAbsoluteTransformation(translateMatrix);
0661     notifyChanged();
0662     d->shapeChanged(PositionChanged);
0663 }
0664 
0665 void KoShape::copySettings(const KoShape *shape)
0666 {
0667     Q_D(KoShape);
0668     d->size = shape->size();
0669     d->connectors.clear();
0670     foreach(const KoConnectionPoint &point, shape->connectionPoints())
0671         addConnectionPoint(point);
0672     d->zIndex = shape->zIndex();
0673     d->visible = shape->isVisible();
0674 
0675     // Ensure printable is true by default
0676     if (!d->visible)
0677         d->printable = true;
0678     else
0679         d->printable = shape->isPrintable();
0680 
0681     d->allowedInteractions = shape->allowedInteractions();
0682     d->keepAspect = shape->keepAspectRatio();
0683     d->localMatrix = shape->d_ptr->localMatrix;
0684 }
0685 
0686 void KoShape::notifyChanged()
0687 {
0688     Q_D(KoShape);
0689     foreach(KoShapeManager * manager, d->shapeManagers) {
0690         manager->notifyShapeChanged(this);
0691     }
0692 }
0693 
0694 void KoShape::setUserData(KoShapeUserData *userData)
0695 {
0696     Q_D(KoShape);
0697     delete d->userData;
0698     d->userData = userData;
0699 }
0700 
0701 KoShapeUserData *KoShape::userData() const
0702 {
0703     Q_D(const KoShape);
0704     return d->userData;
0705 }
0706 
0707 void KoShape::setApplicationData(KoShapeApplicationData *appData)
0708 {
0709     Q_D(KoShape);
0710     // appdata is deleted by the application.
0711     d->appData = appData;
0712 }
0713 
0714 KoShapeApplicationData *KoShape::applicationData() const
0715 {
0716     Q_D(const KoShape);
0717     return d->appData;
0718 }
0719 
0720 bool KoShape::hasTransparency() const
0721 {
0722     Q_D(const KoShape);
0723     if (! d->fill)
0724         return true;
0725     else
0726         return d->fill->hasTransparency() || d->transparency > 0.0;
0727 }
0728 
0729 void KoShape::setTransparency(qreal transparency)
0730 {
0731     Q_D(KoShape);
0732     d->transparency = qBound<qreal>(0.0, transparency, 1.0);
0733 }
0734 
0735 qreal KoShape::transparency(bool recursive) const
0736 {
0737     Q_D(const KoShape);
0738     if (!recursive || !parent()) {
0739         return d->transparency;
0740     } else {
0741         const qreal parentOpacity = 1.0-parent()->transparency(recursive);
0742         const qreal childOpacity = 1.0-d->transparency;
0743         return 1.0-(parentOpacity*childOpacity);
0744     }
0745 }
0746 
0747 KoInsets KoShape::strokeInsets() const
0748 {
0749     Q_D(const KoShape);
0750     KoInsets answer;
0751     if (d->stroke)
0752         d->stroke->strokeInsets(this, answer);
0753     return answer;
0754 }
0755 
0756 qreal KoShape::rotation() const
0757 {
0758     Q_D(const KoShape);
0759     // try to extract the rotation angle out of the local matrix
0760     // if it is a pure rotation matrix
0761 
0762     // check if the matrix has shearing mixed in
0763     if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
0764         return std::numeric_limits<qreal>::quiet_NaN();
0765     // check if the matrix has scaling mixed in
0766     if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
0767         return std::numeric_limits<qreal>::quiet_NaN();
0768 
0769     // calculate the angle from the matrix elements
0770     qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
0771     if (angle < 0.0)
0772         angle += 360.0;
0773 
0774     return angle;
0775 }
0776 
0777 QSizeF KoShape::size() const
0778 {
0779     Q_D(const KoShape);
0780     return d->size;
0781 }
0782 
0783 QPointF KoShape::position() const
0784 {
0785     Q_D(const KoShape);
0786     QPointF center(0.5*size().width(), 0.5*size().height());
0787     return d->localMatrix.map(center) - center;
0788 }
0789 
0790 int KoShape::addConnectionPoint(const KoConnectionPoint &point)
0791 {
0792     Q_D(KoShape);
0793 
0794     // get next glue point id
0795     int nextConnectionPointId = KoConnectionPoint::FirstCustomConnectionPoint;
0796     if (d->connectors.size())
0797         nextConnectionPointId = qMax(nextConnectionPointId, (--d->connectors.end()).key()+1);
0798 
0799     KoConnectionPoint p = point;
0800     d->convertFromShapeCoordinates(p, size());
0801     d->connectors[nextConnectionPointId] = p;
0802 
0803     return nextConnectionPointId;
0804 }
0805 
0806 bool KoShape::setConnectionPoint(int connectionPointId, const KoConnectionPoint &point)
0807 {
0808     Q_D(KoShape);
0809     if (connectionPointId < 0)
0810         return false;
0811 
0812     const bool insertPoint = !hasConnectionPoint(connectionPointId);
0813 
0814     switch(connectionPointId) {
0815         case KoConnectionPoint::TopConnectionPoint:
0816         case KoConnectionPoint::RightConnectionPoint:
0817         case KoConnectionPoint::BottomConnectionPoint:
0818         case KoConnectionPoint::LeftConnectionPoint:
0819         {
0820             KoConnectionPoint::PointId id = static_cast<KoConnectionPoint::PointId>(connectionPointId);
0821             d->connectors[id] = KoConnectionPoint::defaultConnectionPoint(id);
0822             break;
0823         }
0824         default:
0825         {
0826             KoConnectionPoint p = point;
0827             d->convertFromShapeCoordinates(p, size());
0828             d->connectors[connectionPointId] = p;
0829             break;
0830         }
0831     }
0832 
0833     if(!insertPoint)
0834         d->shapeChanged(ConnectionPointChanged);
0835 
0836     return true;
0837 }
0838 
0839 bool KoShape::hasConnectionPoint(int connectionPointId) const
0840 {
0841     Q_D(const KoShape);
0842     return d->connectors.contains(connectionPointId);
0843 }
0844 
0845 KoConnectionPoint KoShape::connectionPoint(int connectionPointId) const
0846 {
0847     Q_D(const KoShape);
0848     KoConnectionPoint p = d->connectors.value(connectionPointId, KoConnectionPoint());
0849     // convert glue point to shape coordinates
0850     d->convertToShapeCoordinates(p, size());
0851     return p;
0852 }
0853 
0854 KoConnectionPoints KoShape::connectionPoints() const
0855 {
0856     Q_D(const KoShape);
0857     QSizeF s = size();
0858     KoConnectionPoints points = d->connectors;
0859     KoConnectionPoints::iterator point = points.begin();
0860     KoConnectionPoints::iterator lastPoint = points.end();
0861     // convert glue points to shape coordinates
0862     for(; point != lastPoint; ++point) {
0863         d->convertToShapeCoordinates(point.value(), s);
0864     }
0865 
0866     return points;
0867 }
0868 
0869 void KoShape::removeConnectionPoint(int connectionPointId)
0870 {
0871     Q_D(KoShape);
0872     d->connectors.remove(connectionPointId);
0873     d->shapeChanged(ConnectionPointChanged);
0874 }
0875 
0876 void KoShape::clearConnectionPoints()
0877 {
0878     Q_D(KoShape);
0879     d->connectors.clear();
0880 }
0881 
0882 void KoShape::addEventAction(KoEventAction *action)
0883 {
0884     Q_D(KoShape);
0885     d->eventActions.insert(action);
0886 }
0887 
0888 void KoShape::removeEventAction(KoEventAction *action)
0889 {
0890     Q_D(KoShape);
0891     d->eventActions.remove(action);
0892 }
0893 
0894 QSet<KoEventAction *> KoShape::eventActions() const
0895 {
0896     Q_D(const KoShape);
0897     return d->eventActions;
0898 }
0899 
0900 KoShape::TextRunAroundSide KoShape::textRunAroundSide() const
0901 {
0902     Q_D(const KoShape);
0903     return d->textRunAroundSide;
0904 }
0905 
0906 void KoShape::setTextRunAroundSide(TextRunAroundSide side, RunThroughLevel runThrought)
0907 {
0908     Q_D(KoShape);
0909 
0910     if (side == RunThrough) {
0911         if (runThrought == Background) {
0912             setRunThrough(-1);
0913         } else {
0914             setRunThrough(1);
0915         }
0916     } else {
0917         setRunThrough(0);
0918     }
0919 
0920     if ( d->textRunAroundSide == side) {
0921         return;
0922     }
0923 
0924     d->textRunAroundSide = side;
0925     notifyChanged();
0926     d->shapeChanged(TextRunAroundChanged);
0927 }
0928 
0929 qreal KoShape::textRunAroundDistanceTop() const
0930 {
0931     Q_D(const KoShape);
0932     return d->textRunAroundDistanceTop;
0933 }
0934 
0935 void KoShape::setTextRunAroundDistanceTop(qreal distance)
0936 {
0937     Q_D(KoShape);
0938     d->textRunAroundDistanceTop = distance;
0939 }
0940 
0941 qreal KoShape::textRunAroundDistanceLeft() const
0942 {
0943     Q_D(const KoShape);
0944     return d->textRunAroundDistanceLeft;
0945 }
0946 
0947 void KoShape::setTextRunAroundDistanceLeft(qreal distance)
0948 {
0949     Q_D(KoShape);
0950     d->textRunAroundDistanceLeft = distance;
0951 }
0952 
0953 qreal KoShape::textRunAroundDistanceRight() const
0954 {
0955     Q_D(const KoShape);
0956     return d->textRunAroundDistanceRight;
0957 }
0958 
0959 void KoShape::setTextRunAroundDistanceRight(qreal distance)
0960 {
0961     Q_D(KoShape);
0962     d->textRunAroundDistanceRight = distance;
0963 }
0964 
0965 qreal KoShape::textRunAroundDistanceBottom() const
0966 {
0967     Q_D(const KoShape);
0968     return d->textRunAroundDistanceBottom;
0969 }
0970 
0971 void KoShape::setTextRunAroundDistanceBottom(qreal distance)
0972 {
0973     Q_D(KoShape);
0974     d->textRunAroundDistanceBottom = distance;
0975 }
0976 
0977 qreal KoShape::textRunAroundThreshold() const
0978 {
0979     Q_D(const KoShape);
0980     return d->textRunAroundThreshold;
0981 }
0982 
0983 void KoShape::setTextRunAroundThreshold(qreal threshold)
0984 {
0985     Q_D(KoShape);
0986     d->textRunAroundThreshold = threshold;
0987 }
0988 
0989 KoShape::TextRunAroundContour KoShape::textRunAroundContour() const
0990 {
0991     Q_D(const KoShape);
0992     return d->textRunAroundContour;
0993 }
0994 
0995 void KoShape::setTextRunAroundContour(KoShape::TextRunAroundContour contour)
0996 {
0997     Q_D(KoShape);
0998     d->textRunAroundContour = contour;
0999 }
1000 
1001 void KoShape::setAnchor(KoShapeAnchor *anchor)
1002 {
1003     Q_D(KoShape);
1004     d->anchor = anchor;
1005 }
1006 
1007 KoShapeAnchor *KoShape::anchor() const
1008 {
1009     Q_D(const KoShape);
1010     return d->anchor;
1011 }
1012 
1013 void KoShape::setMinimumHeight(qreal height)
1014 {
1015     Q_D(KoShape);
1016     d->minimumHeight = height;
1017 }
1018 
1019 qreal KoShape::minimumHeight() const
1020 {
1021     Q_D(const KoShape);
1022     return d->minimumHeight;
1023 }
1024 
1025 
1026 void KoShape::setBackground(QSharedPointer<KoShapeBackground> fill)
1027 {
1028     Q_D(KoShape);
1029     d->fill = fill;
1030     d->shapeChanged(BackgroundChanged);
1031     notifyChanged();
1032 }
1033 
1034 QSharedPointer<KoShapeBackground> KoShape::background() const
1035 {
1036     Q_D(const KoShape);
1037     return d->fill;
1038 }
1039 
1040 void KoShape::setZIndex(int zIndex)
1041 {
1042     Q_D(KoShape);
1043     if (d->zIndex == zIndex)
1044         return;
1045     d->zIndex = zIndex;
1046     notifyChanged();
1047 }
1048 
1049 int KoShape::runThrough()
1050 {
1051     Q_D(const KoShape);
1052     return d->runThrough;
1053 }
1054 
1055 void KoShape::setRunThrough(short int runThrough)
1056 {
1057     Q_D(KoShape);
1058     d->runThrough = runThrough;
1059 }
1060 
1061 void KoShape::setVisible(bool on)
1062 {
1063     Q_D(KoShape);
1064     int _on = (on ? 1 : 0);
1065     if (d->visible == _on) return;
1066     d->visible = _on;
1067 }
1068 
1069 bool KoShape::isVisible(bool recursive) const
1070 {
1071     Q_D(const KoShape);
1072     if (! recursive)
1073         return d->visible;
1074     if (recursive && ! d->visible)
1075         return false;
1076 
1077     KoShapeContainer * parentShape = parent();
1078     while (parentShape) {
1079         if (! parentShape->isVisible())
1080             return false;
1081         parentShape = parentShape->parent();
1082     }
1083     return true;
1084 }
1085 
1086 void KoShape::setPrintable(bool on)
1087 {
1088     Q_D(KoShape);
1089     d->printable = on;
1090 }
1091 
1092 bool KoShape::isPrintable() const
1093 {
1094     Q_D(const KoShape);
1095     if (d->visible)
1096         return d->printable;
1097     else
1098         return false;
1099 }
1100 
1101 void KoShape::setSelectable(bool selectable)
1102 {
1103     Q_D(KoShape);
1104 #if QT_VERSION >= 0x050700
1105     d->allowedInteractions.setFlag(SelectionAllowed, selectable);
1106 #else
1107     selectable ? (d->allowedInteractions |= SelectionAllowed) : (d->allowedInteractions &= ~SelectionAllowed);
1108 #endif
1109 }
1110 
1111 bool KoShape::isSelectable() const
1112 {
1113     Q_D(const KoShape);
1114     return d->allowedInteractions.testFlag(SelectionAllowed);
1115 }
1116 
1117 void KoShape::setGeometryProtected(bool on)
1118 {
1119     Q_D(KoShape);
1120 #if QT_VERSION >= 0x050700
1121     d->allowedInteractions.setFlag(MoveAllowed, !on);
1122     d->allowedInteractions.setFlag(ResizeAllowed, !on);
1123 #else
1124     (!on) ? (d->allowedInteractions |= MoveAllowed) : (d->allowedInteractions &= ~MoveAllowed);
1125     (!on) ? (d->allowedInteractions |= ResizeAllowed) : (d->allowedInteractions &= ~ResizeAllowed);
1126 #endif
1127 }
1128 
1129 bool KoShape::isGeometryProtected() const
1130 {
1131     Q_D(const KoShape);
1132     return !d->allowedInteractions.testFlag(MoveAllowed) || !d->allowedInteractions.testFlag(ResizeAllowed);
1133 }
1134 
1135 void KoShape::setContentProtected(bool protect)
1136 {
1137     Q_D(KoShape);
1138 #if QT_VERSION >= 0x050700
1139     d->allowedInteractions.setFlag(ContentChangeAllowed, !protect);
1140 #else
1141     (!protect) ? (d->allowedInteractions |= ContentChangeAllowed) : (d->allowedInteractions &= ~ContentChangeAllowed);
1142 #endif
1143 }
1144 
1145 bool KoShape::isContentProtected() const
1146 {
1147     Q_D(const KoShape);
1148     return !d->allowedInteractions.testFlag(ContentChangeAllowed);
1149 }
1150 
1151 void KoShape::setDeletable(bool deletable)
1152 {
1153     Q_D(KoShape);
1154 #if QT_VERSION >= 0x050700
1155     d->allowedInteractions.setFlag(DeletionAllowed, deletable);
1156 #else
1157     deletable ? (d->allowedInteractions |= DeletionAllowed) : (d->allowedInteractions &= ~DeletionAllowed);
1158 #endif
1159 }
1160 
1161 bool KoShape::isDeletable() const
1162 {
1163     Q_D(const KoShape);
1164     return d->allowedInteractions.testFlag(DeletionAllowed);
1165 }
1166 
1167 void KoShape::setAllowedInteraction(KoShape::AllowedInteraction flag, bool value)
1168 {
1169     Q_D(KoShape);
1170 #if QT_VERSION >= 0x050700
1171     d->allowedInteractions.setFlag(flag, value);
1172 #else
1173     value ? (d->allowedInteractions |= flag) : (d->allowedInteractions &= ~flag);
1174 #endif
1175 }
1176 
1177 bool KoShape::allowedInteraction(KoShape::AllowedInteraction flag, bool recursive) const
1178 {
1179     return allowedInteractions(recursive).testFlag(flag);
1180 }
1181 
1182 void KoShape::setAllowedInteractions(KoShape::AllowedInteractions interactions)
1183 {
1184     Q_D(KoShape);
1185     d->allowedInteractions = interactions;
1186 }
1187 
1188 KoShape::AllowedInteractions KoShape::allowedInteractions(bool recursive) const
1189 {
1190     Q_D(const KoShape);
1191     if (!recursive) {
1192         return d->allowedInteractions;
1193     }
1194     AllowedInteractions state;
1195     if (!d->visible) {
1196         return state;
1197     }
1198     state = d->allowedInteractions;
1199     if (state && d->parent) {
1200         state &= d->parent->allowedInteractions(this);
1201     }
1202     return state;
1203 }
1204 
1205 KoShapeContainer *KoShape::parent() const
1206 {
1207     Q_D(const KoShape);
1208     return d->parent;
1209 }
1210 
1211 void KoShape::setKeepAspectRatio(bool keepAspect)
1212 {
1213     Q_D(KoShape);
1214     d->keepAspect = keepAspect;
1215 }
1216 
1217 bool KoShape::keepAspectRatio() const
1218 {
1219     Q_D(const KoShape);
1220     return d->keepAspect;
1221 }
1222 
1223 QString KoShape::shapeId() const
1224 {
1225     Q_D(const KoShape);
1226     return d->shapeId;
1227 }
1228 
1229 void KoShape::setShapeId(const QString &id)
1230 {
1231     Q_D(KoShape);
1232     d->shapeId = id;
1233 }
1234 
1235 void KoShape::setCollisionDetection(bool detect)
1236 {
1237     Q_D(KoShape);
1238     d->detectCollision = detect;
1239 }
1240 
1241 bool KoShape::collisionDetection()
1242 {
1243     Q_D(KoShape);
1244     return d->detectCollision;
1245 }
1246 
1247 KoShapeStrokeModel *KoShape::stroke() const
1248 {
1249     Q_D(const KoShape);
1250     return d->stroke;
1251 }
1252 
1253 void KoShape::setStroke(KoShapeStrokeModel *stroke)
1254 {
1255     Q_D(KoShape);
1256     if (stroke)
1257         stroke->ref();
1258     d->updateStroke();
1259     if (d->stroke)
1260         d->stroke->deref();
1261     d->stroke = stroke;
1262     d->updateStroke();
1263     d->shapeChanged(StrokeChanged);
1264     notifyChanged();
1265 }
1266 
1267 void KoShape::setShadow(KoShapeShadow *shadow)
1268 {
1269     Q_D(KoShape);
1270     if (d->shadow)
1271         d->shadow->deref();
1272     d->shadow = shadow;
1273     if (d->shadow) {
1274         d->shadow->ref();
1275         // TODO update changed area
1276     }
1277     d->shapeChanged(ShadowChanged);
1278     notifyChanged();
1279 }
1280 
1281 KoShapeShadow *KoShape::shadow() const
1282 {
1283     Q_D(const KoShape);
1284     return d->shadow;
1285 }
1286 
1287 void KoShape::setBorder(KoBorder *border)
1288 {
1289     Q_D(KoShape);
1290     if (d->border) {
1291         // The shape owns the border.
1292         delete d->border;
1293     }
1294     d->border = border;
1295     d->shapeChanged(BorderChanged);
1296     notifyChanged();
1297 }
1298 
1299 KoBorder *KoShape::border() const
1300 {
1301     Q_D(const KoShape);
1302     return d->border;
1303 }
1304 
1305 void KoShape::setClipPath(KoClipPath *clipPath)
1306 {
1307     Q_D(KoShape);
1308     d->clipPath = clipPath;
1309     d->shapeChanged(ClipPathChanged);
1310     notifyChanged();
1311 }
1312 
1313 KoClipPath * KoShape::clipPath() const
1314 {
1315     Q_D(const KoShape);
1316     return d->clipPath;
1317 }
1318 
1319 QTransform KoShape::transform() const
1320 {
1321     Q_D(const KoShape);
1322     return d->localMatrix;
1323 }
1324 
1325 QString KoShape::name() const
1326 {
1327     Q_D(const KoShape);
1328     return d->name;
1329 }
1330 
1331 void KoShape::setName(const QString &name)
1332 {
1333     Q_D(KoShape);
1334     d->name = name;
1335 }
1336 
1337 void KoShape::waitUntilReady(const KoViewConverter &converter, bool asynchronous) const
1338 {
1339     Q_UNUSED(converter);
1340     Q_UNUSED(asynchronous);
1341 }
1342 
1343 bool KoShape::isEditable() const
1344 {
1345     Q_D(const KoShape);
1346     if (!d->visible || isGeometryProtected())
1347         return false;
1348 
1349     if (d->parent && d->parent->isChildLocked(this))
1350         return false;
1351 
1352     return true;
1353 }
1354 
1355 // painting
1356 void KoShape::paintBorder(QPainter &painter, const KoViewConverter &converter)
1357 {
1358     Q_UNUSED(converter);
1359     KoBorder *bd = border();
1360     if (!bd) {
1361         return;
1362     }
1363 
1364     QRectF borderRect = QRectF(QPointF(0, 0), size());
1365     // Paint the border.
1366     bd->paint(painter, borderRect, KoBorder::PaintInsideLine);
1367 }
1368 
1369 
1370 // loading & saving methods
1371 QString KoShape::saveStyle(KoGenStyle &style, KoShapeSavingContext &context) const
1372 {
1373     Q_D(const KoShape);
1374     // and fill the style
1375     KoShapeStrokeModel *sm = stroke();
1376     if (sm) {
1377         sm->fillStyle(style, context);
1378     }
1379     else {
1380         style.addProperty("draw:stroke", "none", KoGenStyle::GraphicType);
1381     }
1382     KoShapeShadow *s = shadow();
1383     if (s)
1384         s->fillStyle(style, context);
1385 
1386     QSharedPointer<KoShapeBackground> bg = background();
1387     if (bg) {
1388         bg->fillStyle(style, context);
1389     }
1390     else {
1391         style.addProperty("draw:fill", "none", KoGenStyle::GraphicType);
1392     }
1393 
1394     KoBorder *b = border();
1395     if (b) {
1396         b->saveOdf(style);
1397     }
1398 
1399     if (context.isSet(KoShapeSavingContext::AutoStyleInStyleXml)) {
1400         style.setAutoStyleInStylesDotXml(true);
1401     }
1402 
1403     QString value;
1404     if (!d->allowedInteractions.testFlag(MoveAllowed)) {
1405         value = "position";
1406     }
1407     if (!d->allowedInteractions.testFlag(ResizeAllowed)) {
1408         if (! value.isEmpty())
1409             value += ' ';
1410         value += "size";
1411     }
1412     if (isContentProtected()) {
1413         if (! value.isEmpty())
1414             value += ' ';
1415         value += "content";
1416     }
1417     if (!value.isEmpty()) {
1418         style.addProperty("style:protect", value, KoGenStyle::GraphicType);
1419     }
1420 
1421     QMap<QByteArray, QString>::const_iterator it(d->additionalStyleAttributes.constBegin());
1422     for (; it != d->additionalStyleAttributes.constEnd(); ++it) {
1423         style.addProperty(it.key(), it.value());
1424     }
1425 
1426     if (parent() && parent()->isClipped(this)) {
1427         /*
1428          * In Calligra clipping is done using a parent shape which can be rotated, sheared etc
1429          * and even non-square.  So the ODF interoperability version we write here is really
1430          * just a very simple version of that...
1431          */
1432         qreal top = -position().y();
1433         qreal left = -position().x();
1434         qreal right = parent()->size().width() - size().width() - left;
1435         qreal bottom = parent()->size().height() - size().height() - top;
1436 
1437         style.addProperty("fo:clip", QString("rect(%1pt, %2pt, %3pt, %4pt)")
1438                           .arg(top, 10, 'f').arg(right, 10, 'f')
1439                           .arg(bottom, 10, 'f').arg(left, 10, 'f'), KoGenStyle::GraphicType);
1440 
1441     }
1442 
1443     QString wrap;
1444     switch (textRunAroundSide()) {
1445         case BiggestRunAroundSide:
1446             wrap = "biggest";
1447             break;
1448         case LeftRunAroundSide:
1449             wrap = "left";
1450             break;
1451         case RightRunAroundSide:
1452             wrap = "right";
1453             break;
1454         case EnoughRunAroundSide:
1455             wrap = "dynamic";
1456             break;
1457         case BothRunAroundSide:
1458             wrap = "parallel";
1459             break;
1460         case NoRunAround:
1461             wrap = "none";
1462             break;
1463         case RunThrough:
1464             wrap = "run-through";
1465             break;
1466     }
1467     style.addProperty("style:wrap", wrap, KoGenStyle::GraphicType);
1468     switch (textRunAroundContour()) {
1469         case ContourBox:
1470             style.addProperty("style:wrap-contour", "false", KoGenStyle::GraphicType);
1471             break;
1472         case ContourFull:
1473             style.addProperty("style:wrap-contour", "true", KoGenStyle::GraphicType);
1474             style.addProperty("style:wrap-contour-mode", "full", KoGenStyle::GraphicType);
1475             break;
1476         case ContourOutside:
1477             style.addProperty("style:wrap-contour", "true", KoGenStyle::GraphicType);
1478             style.addProperty("style:wrap-contour-mode", "outside", KoGenStyle::GraphicType);
1479             break;
1480     }
1481     style.addPropertyPt("style:wrap-dynamic-threshold", textRunAroundThreshold(), KoGenStyle::GraphicType);
1482     if ((textRunAroundDistanceLeft() == textRunAroundDistanceRight())
1483                 && (textRunAroundDistanceTop() == textRunAroundDistanceBottom())
1484                 && (textRunAroundDistanceLeft() == textRunAroundDistanceTop())) {
1485         style.addPropertyPt("fo:margin", textRunAroundDistanceLeft(), KoGenStyle::GraphicType);
1486     } else {
1487         style.addPropertyPt("fo:margin-left", textRunAroundDistanceLeft(), KoGenStyle::GraphicType);
1488         style.addPropertyPt("fo:margin-top", textRunAroundDistanceTop(), KoGenStyle::GraphicType);
1489         style.addPropertyPt("fo:margin-right", textRunAroundDistanceRight(), KoGenStyle::GraphicType);
1490         style.addPropertyPt("fo:margin-bottom", textRunAroundDistanceBottom(), KoGenStyle::GraphicType);
1491     }
1492 
1493     return context.mainStyles().insert(style, context.styleFamily());
1494 }
1495 
1496 void KoShape::loadStyle(const KoXmlElement &element, KoShapeLoadingContext &context)
1497 {
1498     Q_D(KoShape);
1499 
1500     KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
1501     styleStack.setTypeProperties("graphic");
1502 
1503     d->fill.clear();
1504     if (d->stroke && !d->stroke->deref()) {
1505         delete d->stroke;
1506         d->stroke = 0;
1507     }
1508     if (d->shadow && !d->shadow->deref()) {
1509         delete d->shadow;
1510         d->shadow = 0;
1511     }
1512     setBackground(loadOdfFill(context));
1513     setStroke(loadOdfStroke(element, context));
1514     setShadow(d->loadOdfShadow(context));
1515     setBorder(d->loadOdfBorder(context));
1516 
1517     QString protect(styleStack.property(KoXmlNS::style, "protect"));
1518 #if QT_VERSION >= 0x050700
1519     d->allowedInteractions.setFlag(MoveAllowed, !protect.contains("position"));
1520     d->allowedInteractions.setFlag(ResizeAllowed, !protect.contains("size"));
1521 #else
1522     (!protect.contains("position")) ? (d->allowedInteractions |= MoveAllowed) : (d->allowedInteractions &= ~MoveAllowed);
1523     (!protect.contains("size")) ? (d->allowedInteractions |= ResizeAllowed) : (d->allowedInteractions &= ~ResizeAllowed);
1524 #endif
1525     setContentProtected(protect.contains("content"));
1526 
1527     QString margin = styleStack.property(KoXmlNS::fo, "margin");
1528     if (!margin.isEmpty()) {
1529         setTextRunAroundDistanceLeft(KoUnit::parseValue(margin));
1530         setTextRunAroundDistanceTop(KoUnit::parseValue(margin));
1531         setTextRunAroundDistanceRight(KoUnit::parseValue(margin));
1532         setTextRunAroundDistanceBottom(KoUnit::parseValue(margin));
1533     }
1534     margin = styleStack.property(KoXmlNS::fo, "margin-left");
1535     if (!margin.isEmpty()) {
1536         setTextRunAroundDistanceLeft(KoUnit::parseValue(margin));
1537     }
1538 
1539     margin = styleStack.property(KoXmlNS::fo, "margin-top");
1540     if (!margin.isEmpty()) {
1541         setTextRunAroundDistanceTop(KoUnit::parseValue(margin));
1542     }
1543     margin = styleStack.property(KoXmlNS::fo, "margin-right");
1544     if (!margin.isEmpty()) {
1545         setTextRunAroundDistanceRight(KoUnit::parseValue(margin));
1546     }
1547     margin = styleStack.property(KoXmlNS::fo, "margin-bottom");
1548     if (!margin.isEmpty()) {
1549         setTextRunAroundDistanceBottom(KoUnit::parseValue(margin));
1550     }
1551 
1552     QString wrap;
1553     if (styleStack.hasProperty(KoXmlNS::style, "wrap")) {
1554         wrap = styleStack.property(KoXmlNS::style, "wrap");
1555     } else {
1556         // no value given in the file, but guess biggest
1557         wrap = "biggest";
1558     }
1559     if (wrap == "none") {
1560         setTextRunAroundSide(KoShape::NoRunAround);
1561     } else if (wrap == "run-through") {
1562         QString runTrought = styleStack.property(KoXmlNS::style, "run-through", "background");
1563         if (runTrought == "background") {
1564             setTextRunAroundSide(KoShape::RunThrough, KoShape::Background);
1565         } else {
1566             setTextRunAroundSide(KoShape::RunThrough, KoShape::Foreground);
1567         }
1568     } else {
1569         if (wrap == "biggest")
1570             setTextRunAroundSide(KoShape::BiggestRunAroundSide);
1571         else if (wrap == "left")
1572             setTextRunAroundSide(KoShape::LeftRunAroundSide);
1573         else if (wrap == "right")
1574             setTextRunAroundSide(KoShape::RightRunAroundSide);
1575         else if (wrap == "dynamic")
1576             setTextRunAroundSide(KoShape::EnoughRunAroundSide);
1577         else if (wrap == "parallel")
1578             setTextRunAroundSide(KoShape::BothRunAroundSide);
1579     }
1580 
1581     if (styleStack.hasProperty(KoXmlNS::style, "wrap-dynamic-threshold")) {
1582         QString wrapThreshold = styleStack.property(KoXmlNS::style, "wrap-dynamic-threshold");
1583         if (!wrapThreshold.isEmpty()) {
1584             setTextRunAroundThreshold(KoUnit::parseValue(wrapThreshold));
1585         }
1586     }
1587     if (styleStack.property(KoXmlNS::style, "wrap-contour", "false") == "true") {
1588         if (styleStack.property(KoXmlNS::style, "wrap-contour-mode", "full") == "full") {
1589             setTextRunAroundContour(KoShape::ContourFull);
1590         } else {
1591             setTextRunAroundContour(KoShape::ContourOutside);
1592         }
1593     } else {
1594         setTextRunAroundContour(KoShape::ContourBox);
1595     }
1596 }
1597 
1598 bool KoShape::loadOdfAttributes(const KoXmlElement &element, KoShapeLoadingContext &context, int attributes)
1599 {
1600     Q_D(KoShape);
1601     if (attributes & OdfPosition) {
1602         QPointF pos(position());
1603         if (element.hasAttributeNS(KoXmlNS::svg, "x"))
1604             pos.setX(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "x", QString())));
1605         if (element.hasAttributeNS(KoXmlNS::svg, "y"))
1606             pos.setY(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "y", QString())));
1607         setPosition(pos);
1608     }
1609 
1610     if (attributes & OdfSize) {
1611         QSizeF s(size());
1612         if (element.hasAttributeNS(KoXmlNS::svg, "width"))
1613             s.setWidth(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "width", QString())));
1614         if (element.hasAttributeNS(KoXmlNS::svg, "height"))
1615             s.setHeight(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "height", QString())));
1616         setSize(s);
1617     }
1618 
1619     if (attributes & OdfLayer) {
1620         if (element.hasAttributeNS(KoXmlNS::draw, "layer")) {
1621             KoShapeLayer *layer = context.layer(element.attributeNS(KoXmlNS::draw, "layer"));
1622             if (layer) {
1623                 setParent(layer);
1624             }
1625         }
1626     }
1627 
1628     if (attributes & OdfId) {
1629         KoElementReference ref;
1630         ref.loadOdf(element);
1631         if (ref.isValid()) {
1632             context.addShapeId(this, ref.toString());
1633         }
1634     }
1635 
1636     if (attributes & OdfZIndex) {
1637         if (element.hasAttributeNS(KoXmlNS::draw, "z-index")) {
1638             setZIndex(element.attributeNS(KoXmlNS::draw, "z-index").toInt());
1639         } else {
1640             setZIndex(context.zIndex());
1641         }
1642     }
1643 
1644     if (attributes & OdfName) {
1645         if (element.hasAttributeNS(KoXmlNS::draw, "name")) {
1646             setName(element.attributeNS(KoXmlNS::draw, "name"));
1647         }
1648     }
1649 
1650     if (attributes & OdfStyle) {
1651         KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
1652         styleStack.save();
1653         if (element.hasAttributeNS(KoXmlNS::draw, "style-name")) {
1654             context.odfLoadingContext().fillStyleStack(element, KoXmlNS::draw, "style-name", "graphic");
1655         }
1656         if (element.hasAttributeNS(KoXmlNS::presentation, "style-name")) {
1657             context.odfLoadingContext().fillStyleStack(element, KoXmlNS::presentation, "style-name", "presentation");
1658         }
1659         loadStyle(element, context);
1660 
1661         styleStack.restore();
1662     }
1663 
1664     if (attributes & OdfTransformation) {
1665         QString transform = element.attributeNS(KoXmlNS::draw, "transform", QString());
1666         if (! transform.isEmpty())
1667             applyAbsoluteTransformation(parseOdfTransform(transform, context));
1668     }
1669 
1670     if (attributes & OdfAdditionalAttributes) {
1671         QSet<KoShapeLoadingContext::AdditionalAttributeData> additionalAttributeData = KoShapeLoadingContext::additionalAttributeData();
1672         foreach(const KoShapeLoadingContext::AdditionalAttributeData &attributeData, additionalAttributeData) {
1673             if (element.hasAttributeNS(attributeData.ns, attributeData.tag)) {
1674                 QString value = element.attributeNS(attributeData.ns, attributeData.tag);
1675                 //debugFlake << "load additional attribute" << attributeData.tag << value;
1676                 setAdditionalAttribute(attributeData.name, value);
1677             }
1678         }
1679     }
1680 
1681     if (attributes & OdfCommonChildElements) {
1682         const KoXmlElement eventActionsElement(KoXml::namedItemNS(element, KoXmlNS::office, "event-listeners"));
1683         if (!eventActionsElement.isNull()) {
1684             d->eventActions = KoEventActionRegistry::instance()->createEventActionsFromOdf(eventActionsElement, context);
1685         }
1686         // load glue points (connection points)
1687         loadOdfGluePoints(element, context);
1688     }
1689 
1690     return true;
1691 }
1692 
1693 QSharedPointer<KoShapeBackground> KoShape::loadOdfFill(KoShapeLoadingContext &context) const
1694 {
1695     QString fill = KoShapePrivate::getStyleProperty("fill", context);
1696     QSharedPointer<KoShapeBackground> bg;
1697     if (fill == "solid") {
1698         bg = QSharedPointer<KoShapeBackground>(new KoColorBackground());
1699     }
1700     else if (fill == "hatch") {
1701         bg = QSharedPointer<KoShapeBackground>(new KoHatchBackground());
1702     }
1703     else if (fill == "gradient") {
1704         QString styleName = KoShapePrivate::getStyleProperty("fill-gradient-name", context);
1705         KoXmlElement *e = context.odfLoadingContext().stylesReader().drawStyles("gradient").value(styleName);
1706         QString style;
1707         if (e) {
1708             style = e->attributeNS(KoXmlNS::draw, "style", QString());
1709         }
1710         if ((style == "rectangular") || (style == "square")) {
1711             bg = QSharedPointer<KoShapeBackground>(new KoOdfGradientBackground());
1712         } else {
1713             QGradient *gradient = new QLinearGradient();
1714             gradient->setCoordinateMode(QGradient::ObjectBoundingMode);
1715             bg = QSharedPointer<KoShapeBackground>(new KoGradientBackground(gradient));
1716         }
1717     } else if (fill == "bitmap") {
1718         bg = QSharedPointer<KoShapeBackground>(new KoPatternBackground(context.imageCollection()));
1719 #ifndef NWORKAROUND_ODF_BUGS
1720     } else if (fill.isEmpty()) {
1721         bg = QSharedPointer<KoShapeBackground>(KoOdfWorkaround::fixBackgroundColor(this, context));
1722         return bg;
1723 #endif
1724     } else {
1725         return QSharedPointer<KoShapeBackground>(0);
1726     }
1727 
1728     if (!bg->loadStyle(context.odfLoadingContext(), size())) {
1729         return QSharedPointer<KoShapeBackground>(0);
1730     }
1731 
1732     return bg;
1733 }
1734 
1735 KoShapeStrokeModel *KoShape::loadOdfStroke(const KoXmlElement &element, KoShapeLoadingContext &context) const
1736 {
1737     KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
1738     KoOdfStylesReader &stylesReader = context.odfLoadingContext().stylesReader();
1739 
1740     QString stroke = KoShapePrivate::getStyleProperty("stroke", context);
1741     if (stroke == "solid" || stroke == "dash") {
1742         QPen pen = KoOdfGraphicStyles::loadOdfStrokeStyle(styleStack, stroke, stylesReader);
1743 
1744         KoShapeStroke *stroke = new KoShapeStroke();
1745 
1746         if (styleStack.hasProperty(KoXmlNS::calligra, "stroke-gradient")) {
1747             QString gradientName = styleStack.property(KoXmlNS::calligra, "stroke-gradient");
1748             QBrush brush = KoOdfGraphicStyles::loadOdfGradientStyleByName(stylesReader, gradientName, size());
1749             stroke->setLineBrush(brush);
1750         } else {
1751             stroke->setColor(pen.color());
1752         }
1753 
1754 #ifndef NWORKAROUND_ODF_BUGS
1755         KoOdfWorkaround::fixPenWidth(pen, context);
1756 #endif
1757         stroke->setLineWidth(pen.widthF());
1758         stroke->setJoinStyle(pen.joinStyle());
1759         stroke->setLineStyle(pen.style(), pen.dashPattern());
1760         stroke->setCapStyle(pen.capStyle());
1761 
1762         return stroke;
1763 #ifndef NWORKAROUND_ODF_BUGS
1764     } else if (stroke.isEmpty()) {
1765         QPen pen = KoOdfGraphicStyles::loadOdfStrokeStyle(styleStack, "solid", stylesReader);
1766         if (KoOdfWorkaround::fixMissingStroke(pen, element, context, this)) {
1767             KoShapeStroke *stroke = new KoShapeStroke();
1768 
1769 #ifndef NWORKAROUND_ODF_BUGS
1770             KoOdfWorkaround::fixPenWidth(pen, context);
1771 #endif
1772             stroke->setLineWidth(pen.widthF());
1773             stroke->setJoinStyle(pen.joinStyle());
1774             stroke->setLineStyle(pen.style(), pen.dashPattern());
1775             stroke->setCapStyle(pen.capStyle());
1776             stroke->setColor(pen.color());
1777 
1778             return stroke;
1779         }
1780 #endif
1781     }
1782 
1783     return 0;
1784 }
1785 
1786 KoShapeShadow *KoShapePrivate::loadOdfShadow(KoShapeLoadingContext &context) const
1787 {
1788     KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
1789     QString shadowStyle = KoShapePrivate::getStyleProperty("shadow", context);
1790     if (shadowStyle == "visible" || shadowStyle == "hidden") {
1791         KoShapeShadow *shadow = new KoShapeShadow();
1792         QColor shadowColor(styleStack.property(KoXmlNS::draw, "shadow-color"));
1793         qreal offsetX = KoUnit::parseValue(styleStack.property(KoXmlNS::draw, "shadow-offset-x"));
1794         qreal offsetY = KoUnit::parseValue(styleStack.property(KoXmlNS::draw, "shadow-offset-y"));
1795         shadow->setOffset(QPointF(offsetX, offsetY));
1796         qreal blur = KoUnit::parseValue(styleStack.property(KoXmlNS::calligra, "shadow-blur-radius"));
1797         shadow->setBlur(blur);
1798 
1799         QString opacity = styleStack.property(KoXmlNS::draw, "shadow-opacity");
1800         if (! opacity.isEmpty() && opacity.right(1) == "%")
1801             shadowColor.setAlphaF(opacity.leftRef(opacity.length() - 1).toFloat() / 100.0);
1802         shadow->setColor(shadowColor);
1803         shadow->setVisible(shadowStyle == "visible");
1804 
1805         return shadow;
1806     }
1807     return 0;
1808 }
1809 
1810 KoBorder *KoShapePrivate::loadOdfBorder(KoShapeLoadingContext &context) const
1811 {
1812     KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
1813 
1814     KoBorder *border = new KoBorder();
1815     if (border->loadOdf(styleStack)) {
1816         return border;
1817     }
1818     delete border;
1819     return 0;
1820 }
1821 
1822 
1823 void KoShape::loadOdfGluePoints(const KoXmlElement &element, KoShapeLoadingContext &context)
1824 {
1825     Q_D(KoShape);
1826 
1827     KoXmlElement child;
1828     bool hasCenterGluePoint = false;
1829     forEachElement(child, element) {
1830         if (child.namespaceURI() != KoXmlNS::draw)
1831             continue;
1832         if (child.localName() != "glue-point")
1833             continue;
1834 
1835         // NOTE: this uses draw:id, but apparently while ODF 1.2 has deprecated
1836         // all use of draw:id for xml:id, it didn't specify that here, so it
1837         // doesn't support xml:id (and so, maybe, shouldn't use KoElementReference.
1838         const QString id = child.attributeNS(KoXmlNS::draw, "id", QString());
1839         const int index = id.toInt();
1840         // connection point in center should be default but odf doesn't support,
1841         // in new shape, first custom point is in center, it's okay to replace that point
1842         // with point from xml now, we'll add it back later
1843         if(id.isEmpty() || index < KoConnectionPoint::FirstCustomConnectionPoint ||
1844                 (index != KoConnectionPoint::FirstCustomConnectionPoint && d->connectors.contains(index))) {
1845             warnFlake << "glue-point with no or invalid id";
1846             continue;
1847         }
1848         QString xStr = child.attributeNS(KoXmlNS::svg, "x", QString()).simplified();
1849         QString yStr = child.attributeNS(KoXmlNS::svg, "y", QString()).simplified();
1850         if(xStr.isEmpty() || yStr.isEmpty()) {
1851             warnFlake << "glue-point with invald position";
1852             continue;
1853         }
1854 
1855         KoConnectionPoint connector;
1856 
1857         const QString align = child.attributeNS(KoXmlNS::draw, "align", QString());
1858         if (align.isEmpty()) {
1859 #ifndef NWORKAROUND_ODF_BUGS
1860             KoOdfWorkaround::fixGluePointPosition(xStr, context);
1861             KoOdfWorkaround::fixGluePointPosition(yStr, context);
1862 #endif
1863             if(!xStr.endsWith('%') || !yStr.endsWith('%')) {
1864                 warnFlake << "glue-point with invald position";
1865                 continue;
1866             }
1867             // x and y are relative to drawing object center
1868             connector.position.setX(xStr.remove('%').toDouble()/100.0);
1869             connector.position.setY(yStr.remove('%').toDouble()/100.0);
1870             // convert position to be relative to top-left corner
1871             connector.position += QPointF(0.5, 0.5);
1872             connector.position.rx() = qBound<qreal>(0.0, connector.position.x(), 1.0);
1873             connector.position.ry() = qBound<qreal>(0.0, connector.position.y(), 1.0);
1874         } else {
1875             // absolute distances to the edge specified by align
1876             connector.position.setX(KoUnit::parseValue(xStr));
1877             connector.position.setY(KoUnit::parseValue(yStr));
1878             if (align == "top-left") {
1879                 connector.alignment = KoConnectionPoint::AlignTopLeft;
1880             } else if (align == "top") {
1881                 connector.alignment = KoConnectionPoint::AlignTop;
1882             } else if (align == "top-right") {
1883                 connector.alignment = KoConnectionPoint::AlignTopRight;
1884             } else if (align == "left") {
1885                 connector.alignment = KoConnectionPoint::AlignLeft;
1886             } else if (align == "center") {
1887                 connector.alignment = KoConnectionPoint::AlignCenter;
1888             } else if (align == "right") {
1889                 connector.alignment = KoConnectionPoint::AlignRight;
1890             } else if (align == "bottom-left") {
1891                 connector.alignment = KoConnectionPoint::AlignBottomLeft;
1892             } else if (align == "bottom") {
1893                 connector.alignment = KoConnectionPoint::AlignBottom;
1894             } else if (align == "bottom-right") {
1895                 connector.alignment = KoConnectionPoint::AlignBottomRight;
1896             }
1897             debugFlake << "using alignment" << align;
1898         }
1899         const QString escape = child.attributeNS(KoXmlNS::draw, "escape-direction", QString());
1900         if (!escape.isEmpty()) {
1901             if (escape == "horizontal") {
1902                 connector.escapeDirection = KoConnectionPoint::HorizontalDirections;
1903             } else if (escape == "vertical") {
1904                 connector.escapeDirection = KoConnectionPoint::VerticalDirections;
1905             } else if (escape == "left") {
1906                 connector.escapeDirection = KoConnectionPoint::LeftDirection;
1907             } else if (escape == "right") {
1908                 connector.escapeDirection = KoConnectionPoint::RightDirection;
1909             } else if (escape == "up") {
1910                 connector.escapeDirection = KoConnectionPoint::UpDirection;
1911             } else if (escape == "down") {
1912                 connector.escapeDirection = KoConnectionPoint::DownDirection;
1913             }
1914             debugFlake << "using escape direction" << escape;
1915         }
1916         d->connectors[index] = connector;
1917         debugFlake << "loaded glue-point" << index << "at position" << connector.position;
1918         if (d->connectors[index].position == QPointF(0.5, 0.5)) {
1919             hasCenterGluePoint = true;
1920             debugFlake << "center glue-point found at id " << index;
1921         }
1922     }
1923     if (!hasCenterGluePoint) {
1924         d->connectors[d->connectors.count()] = KoConnectionPoint(QPointF(0.5, 0.5),
1925                      KoConnectionPoint::AllDirections, KoConnectionPoint::AlignCenter);
1926     }
1927     debugFlake << "shape has now" << d->connectors.count() << "glue-points";
1928 }
1929 
1930 void KoShape::loadOdfClipContour(const KoXmlElement &element, KoShapeLoadingContext &context, const QSizeF &scaleFactor)
1931 {
1932     Q_D(KoShape);
1933 
1934     KoXmlElement child;
1935     forEachElement(child, element) {
1936         if (child.namespaceURI() != KoXmlNS::draw)
1937             continue;
1938         if (child.localName() != "contour-polygon")
1939             continue;
1940 
1941         debugFlake << "shape loads contour-polygon";
1942         KoPathShape *ps = new KoPathShape();
1943         ps->loadContourOdf(child, context, scaleFactor);
1944         ps->setTransformation(transformation());
1945 
1946         KoClipData *cd = new KoClipData(ps);
1947         KoClipPath *clipPath = new KoClipPath(this, cd);
1948         d->clipPath = clipPath;
1949     }
1950 }
1951 
1952 QTransform KoShape::parseOdfTransform(const QString &transform, KoShapeLoadingContext &context)
1953 {
1954     QTransform matrix;
1955 
1956     // Split string for handling 1 transform statement at a time
1957     QStringList subtransforms = transform.split(')', QString::SkipEmptyParts);
1958     QStringList::ConstIterator it = subtransforms.constBegin();
1959     QStringList::ConstIterator end = subtransforms.constEnd();
1960     for (; it != end; ++it) {
1961         QStringList subtransform = (*it).split('(', QString::SkipEmptyParts);
1962 
1963         subtransform[0] = subtransform[0].trimmed().toLower();
1964         subtransform[1] = subtransform[1].simplified();
1965         QRegExp reg("[,( ]");
1966         QStringList params = subtransform[1].split(reg, QString::SkipEmptyParts);
1967 
1968         if (subtransform[0].startsWith(';') || subtransform[0].startsWith(','))
1969             subtransform[0] = subtransform[0].right(subtransform[0].length() - 1);
1970 
1971         QString cmd = subtransform[0].toLower();
1972 
1973         if (cmd == "rotate") {
1974             QTransform rotMatrix;
1975 #ifndef NWORKAROUND_ODF_BUGS
1976             KoOdfWorkaround::fixRotate(params, context);
1977 #endif
1978             if (params.count() == 3) {
1979                 qreal x = KoUnit::parseValue(params[1]);
1980                 qreal y = KoUnit::parseValue(params[2]);
1981 
1982                 rotMatrix.translate(x, y);
1983                 rotMatrix.rotate(KoUnit::parseAngle(params[0], 0.0));
1984                 rotMatrix.translate(x, y);
1985             } else {
1986                 rotMatrix.rotate(KoUnit::parseAngle(params[0], 0.0));
1987             }
1988             matrix = matrix * rotMatrix;
1989         } else if (cmd == "translate") {
1990             QTransform moveMatrix;
1991             if (params.count() == 2) {
1992                 qreal x = KoUnit::parseValue(params[0]);
1993                 qreal y = KoUnit::parseValue(params[1]);
1994                 moveMatrix.translate(x, y);
1995             } else   // Spec : if only one param given, assume 2nd param to be 0
1996                 moveMatrix.translate(KoUnit::parseValue(params[0]) , 0);
1997             matrix = matrix * moveMatrix;
1998         } else if (cmd == "scale") {
1999             QTransform scaleMatrix;
2000             if (params.count() == 2)
2001                 scaleMatrix.scale(params[0].toDouble(), params[1].toDouble());
2002             else    // Spec : if only one param given, assume uniform scaling
2003                 scaleMatrix.scale(params[0].toDouble(), params[0].toDouble());
2004             matrix = matrix * scaleMatrix;
2005         } else if (cmd == "skewx") {
2006 #ifndef NWORKAROUND_ODF_BUGS
2007             KoOdfWorkaround::fixSkew(params, context);
2008 #endif
2009             QPointF p = absolutePosition(KoFlake::TopLeftCorner);
2010             QTransform shearMatrix;
2011             shearMatrix.translate(p.x(), p.y());
2012             shearMatrix.shear(tan(KoUnit::parseAngle(params[0], 0.0F) * M_PI / 180), 0.0F);
2013             shearMatrix.translate(-p.x(), -p.y());
2014             matrix = matrix * shearMatrix;
2015         } else if (cmd == "skewy") {
2016 #ifndef NWORKAROUND_ODF_BUGS
2017             KoOdfWorkaround::fixSkew(params, context);
2018 #endif
2019             QPointF p = absolutePosition(KoFlake::TopLeftCorner);
2020             QTransform shearMatrix;
2021             shearMatrix.translate(p.x(), p.y());
2022             shearMatrix.shear(0.0F, tan(KoUnit::parseAngle(params[0], 0.0F) * M_PI / 180));
2023             shearMatrix.translate(-p.x(), -p.y());
2024             matrix = matrix * shearMatrix;
2025         } else if (cmd == "matrix") {
2026             QTransform m;
2027             if (params.count() >= 6) {
2028                 m.setMatrix(params[0].toDouble(), params[1].toDouble(), 0,
2029                             params[2].toDouble(), params[3].toDouble(), 0,
2030                             KoUnit::parseValue(params[4]), KoUnit::parseValue(params[5]), 1);
2031             }
2032             matrix = matrix * m;
2033         }
2034     }
2035 
2036     return matrix;
2037 }
2038 
2039 void KoShape::saveOdfAttributes(KoShapeSavingContext &context, int attributes) const
2040 {
2041     Q_D(const KoShape);
2042     if (attributes & OdfStyle) {
2043         KoGenStyle style;
2044         // all items that should be written to 'draw:frame' and any other 'draw:' object that inherits this shape
2045         if (context.isSet(KoShapeSavingContext::PresentationShape)) {
2046             style = KoGenStyle(KoGenStyle::PresentationAutoStyle, "presentation");
2047             context.xmlWriter().addAttribute("presentation:style-name", saveStyle(style, context));
2048         } else {
2049             style = KoGenStyle(KoGenStyle::GraphicAutoStyle, "graphic");
2050             context.xmlWriter().addAttribute("draw:style-name", saveStyle(style, context));
2051         }
2052     }
2053 
2054     if (attributes & OdfId)  {
2055         if (context.isSet(KoShapeSavingContext::DrawId)) {
2056             KoElementReference ref = context.xmlid(this, "shape", KoElementReference::Counter);
2057             ref.saveOdf(&context.xmlWriter(), KoElementReference::DrawId);
2058         }
2059     }
2060 
2061     if (attributes & OdfName) {
2062         if (! name().isEmpty())
2063             context.xmlWriter().addAttribute("draw:name", name());
2064     }
2065 
2066     if (attributes & OdfLayer) {
2067         KoShape *parent = d->parent;
2068         while (parent) {
2069             if (dynamic_cast<KoShapeLayer*>(parent)) {
2070                 context.xmlWriter().addAttribute("draw:layer", parent->name());
2071                 break;
2072             }
2073             parent = parent->parent();
2074         }
2075     }
2076 
2077     if (attributes & OdfZIndex && context.isSet(KoShapeSavingContext::ZIndex)) {
2078         context.xmlWriter().addAttribute("draw:z-index", zIndex());
2079     }
2080 
2081     if (attributes & OdfSize) {
2082         QSizeF s(size());
2083         if (parent() && parent()->isClipped(this)) { // being clipped shrinks our visible size
2084             // clipping in ODF is done using a combination of visual size and content cliprect.
2085             // A picture of 10cm x 10cm displayed in a box of 2cm x 4cm will be scaled (out
2086             // of proportion in this case).  If we then add a fo:clip like;
2087             // fo:clip="rect(2cm, 3cm, 4cm, 5cm)" (top, right, bottom, left)
2088             // our original 10x10 is clipped to 2cm x 4cm  and *then* fitted in that box.
2089 
2090             // TODO do this properly by subtracting rects
2091             s = parent()->size();
2092         }
2093         context.xmlWriter().addAttributePt("svg:width", s.width());
2094         context.xmlWriter().addAttributePt("svg:height", s.height());
2095     }
2096 
2097     // The position is implicitly stored in the transformation matrix
2098     // if the transformation is saved as well
2099     if ((attributes & OdfPosition) && !(attributes & OdfTransformation)) {
2100         const QPointF p(position() * context.shapeOffset(this));
2101         context.xmlWriter().addAttributePt("svg:x", p.x());
2102         context.xmlWriter().addAttributePt("svg:y", p.y());
2103     }
2104 
2105     if (attributes & OdfTransformation) {
2106         QTransform matrix = absoluteTransformation(0) * context.shapeOffset(this);
2107         if (! matrix.isIdentity()) {
2108             if (qAbs(matrix.m11() - 1) < 1E-5           // 1
2109                     && qAbs(matrix.m12()) < 1E-5        // 0
2110                     && qAbs(matrix.m21()) < 1E-5        // 0
2111                     && qAbs(matrix.m22() - 1) < 1E-5) { // 1
2112                 context.xmlWriter().addAttributePt("svg:x", matrix.dx());
2113                 context.xmlWriter().addAttributePt("svg:y", matrix.dy());
2114             } else {
2115                 QString m = QString("matrix(%1 %2 %3 %4 %5pt %6pt)")
2116                             .arg(matrix.m11(), 0, 'f', 11)
2117                             .arg(matrix.m12(), 0, 'f', 11)
2118                             .arg(matrix.m21(), 0, 'f', 11)
2119                             .arg(matrix.m22(), 0, 'f', 11)
2120                             .arg(matrix.dx(), 0, 'f', 11)
2121                             .arg(matrix.dy(), 0, 'f', 11);
2122                 context.xmlWriter().addAttribute("draw:transform", m);
2123             }
2124         }
2125     }
2126 
2127     if (attributes & OdfViewbox) {
2128         const QSizeF s(size());
2129         QString viewBox = QString("0 0 %1 %2").arg(qRound(s.width())).arg(qRound(s.height()));
2130         context.xmlWriter().addAttribute("svg:viewBox", viewBox);
2131     }
2132 
2133     if (attributes & OdfAdditionalAttributes) {
2134         QMap<QString, QString>::const_iterator it(d->additionalAttributes.constBegin());
2135         for (; it != d->additionalAttributes.constEnd(); ++it) {
2136             context.xmlWriter().addAttribute(it.key().toUtf8(), it.value());
2137         }
2138     }
2139 }
2140 
2141 void KoShape::saveOdfCommonChildElements(KoShapeSavingContext &context) const
2142 {
2143     Q_D(const KoShape);
2144     // save event listeners see ODF 9.2.21 Event Listeners
2145     if (d->eventActions.size() > 0) {
2146         context.xmlWriter().startElement("office:event-listeners");
2147         foreach(KoEventAction * action, d->eventActions) {
2148             action->saveOdf(context);
2149         }
2150         context.xmlWriter().endElement();
2151     }
2152 
2153     // save glue points see ODF 9.2.19 Glue Points
2154     if(d->connectors.count()) {
2155         KoConnectionPoints::const_iterator cp = d->connectors.constBegin();
2156         KoConnectionPoints::const_iterator lastCp = d->connectors.constEnd();
2157         for(; cp != lastCp; ++cp) {
2158             // do not save default glue points
2159             if(cp.key() < 4)
2160                 continue;
2161             context.xmlWriter().startElement("draw:glue-point");
2162             context.xmlWriter().addAttribute("draw:id", QString("%1").arg(cp.key()));
2163             if (cp.value().alignment == KoConnectionPoint::AlignNone) {
2164                 // convert to percent from center
2165                 const qreal x = cp.value().position.x() * 100.0 - 50.0;
2166                 const qreal y = cp.value().position.y() * 100.0 - 50.0;
2167                 context.xmlWriter().addAttribute("svg:x", QString("%1%").arg(x));
2168                 context.xmlWriter().addAttribute("svg:y", QString("%1%").arg(y));
2169             } else {
2170                 context.xmlWriter().addAttributePt("svg:x", cp.value().position.x());
2171                 context.xmlWriter().addAttributePt("svg:y", cp.value().position.y());
2172             }
2173             QString escapeDirection;
2174             switch(cp.value().escapeDirection) {
2175                 case KoConnectionPoint::HorizontalDirections:
2176                     escapeDirection = "horizontal";
2177                     break;
2178                 case KoConnectionPoint::VerticalDirections:
2179                     escapeDirection = "vertical";
2180                     break;
2181                 case KoConnectionPoint::LeftDirection:
2182                     escapeDirection = "left";
2183                     break;
2184                 case KoConnectionPoint::RightDirection:
2185                     escapeDirection = "right";
2186                     break;
2187                 case KoConnectionPoint::UpDirection:
2188                     escapeDirection = "up";
2189                     break;
2190                 case KoConnectionPoint::DownDirection:
2191                     escapeDirection = "down";
2192                     break;
2193                 default:
2194                     // fall through
2195                     break;
2196             }
2197             if(!escapeDirection.isEmpty()) {
2198                 context.xmlWriter().addAttribute("draw:escape-direction", escapeDirection);
2199             }
2200             QString alignment;
2201             switch(cp.value().alignment) {
2202                 case KoConnectionPoint::AlignTopLeft:
2203                     alignment = "top-left";
2204                     break;
2205                 case KoConnectionPoint::AlignTop:
2206                     alignment = "top";
2207                     break;
2208                 case KoConnectionPoint::AlignTopRight:
2209                     alignment = "top-right";
2210                     break;
2211                 case KoConnectionPoint::AlignLeft:
2212                     alignment = "left";
2213                     break;
2214                 case KoConnectionPoint::AlignCenter:
2215                     alignment = "center";
2216                     break;
2217                 case KoConnectionPoint::AlignRight:
2218                     alignment = "right";
2219                     break;
2220                 case KoConnectionPoint::AlignBottomLeft:
2221                     alignment = "bottom-left";
2222                     break;
2223                 case KoConnectionPoint::AlignBottom:
2224                     alignment = "bottom";
2225                     break;
2226                 case KoConnectionPoint::AlignBottomRight:
2227                     alignment = "bottom-right";
2228                     break;
2229                 default:
2230                     // fall through
2231                     break;
2232             }
2233             if(!alignment.isEmpty()) {
2234                 context.xmlWriter().addAttribute("draw:align", alignment);
2235             }
2236             context.xmlWriter().endElement();
2237         }
2238     }
2239 }
2240 
2241 void KoShape::saveOdfClipContour(KoShapeSavingContext &context, const QSizeF &originalSize) const
2242 {
2243     Q_D(const KoShape);
2244 
2245     debugFlake << "shape saves contour-polygon";
2246     if (d->clipPath && !d->clipPath->clipPathShapes().isEmpty()) {
2247         // This will loose data as odf can only save one set of contour wheras
2248         // svg loading and at least karbon editing can produce more than one
2249         // TODO, FIXME see if we can save more than one clipshape to odf
2250         d->clipPath->clipPathShapes().constFirst()->saveContourOdf(context, originalSize);
2251     }
2252 }
2253 
2254 // end loading & saving methods
2255 
2256 // static
2257 void KoShape::applyConversion(QPainter &painter, const KoViewConverter &converter)
2258 {
2259     qreal zoomX, zoomY;
2260     converter.zoom(&zoomX, &zoomY);
2261     painter.scale(zoomX, zoomY);
2262 }
2263 
2264 QPointF KoShape::shapeToDocument(const QPointF &point) const
2265 {
2266     return absoluteTransformation(0).map(point);
2267 }
2268 
2269 QRectF KoShape::shapeToDocument(const QRectF &rect) const
2270 {
2271     return absoluteTransformation(0).mapRect(rect);
2272 }
2273 
2274 QPointF KoShape::documentToShape(const QPointF &point) const
2275 {
2276     return absoluteTransformation(0).inverted().map(point);
2277 }
2278 
2279 QRectF KoShape::documentToShape(const QRectF &rect) const
2280 {
2281     return absoluteTransformation(0).inverted().mapRect(rect);
2282 }
2283 
2284 bool KoShape::addDependee(KoShape *shape)
2285 {
2286     Q_D(KoShape);
2287     if (! shape)
2288         return false;
2289 
2290     // refuse to establish a circular dependency
2291     if (shape->hasDependee(this))
2292         return false;
2293 
2294     if (! d->dependees.contains(shape))
2295         d->dependees.append(shape);
2296 
2297     return true;
2298 }
2299 
2300 void KoShape::removeDependee(KoShape *shape)
2301 {
2302     Q_D(KoShape);
2303     int index = d->dependees.indexOf(shape);
2304     if (index >= 0)
2305         d->dependees.removeAt(index);
2306 }
2307 
2308 bool KoShape::hasDependee(KoShape *shape) const
2309 {
2310     Q_D(const KoShape);
2311     return d->dependees.contains(shape);
2312 }
2313 
2314 QList<KoShape*> KoShape::dependees() const
2315 {
2316     Q_D(const KoShape);
2317     return d->dependees;
2318 }
2319 
2320 void KoShape::shapeChanged(ChangeType type, KoShape *shape)
2321 {
2322     Q_UNUSED(type);
2323     Q_UNUSED(shape);
2324 }
2325 
2326 KoSnapData KoShape::snapData() const
2327 {
2328     return KoSnapData();
2329 }
2330 
2331 void KoShape::setAdditionalAttribute(const QString &name, const QString &value)
2332 {
2333     Q_D(KoShape);
2334     d->additionalAttributes.insert(name, value);
2335 }
2336 
2337 void KoShape::removeAdditionalAttribute(const QString &name)
2338 {
2339     Q_D(KoShape);
2340     d->additionalAttributes.remove(name);
2341 }
2342 
2343 bool KoShape::hasAdditionalAttribute(const QString &name) const
2344 {
2345     Q_D(const KoShape);
2346     return d->additionalAttributes.contains(name);
2347 }
2348 
2349 QString KoShape::additionalAttribute(const QString &name) const
2350 {
2351     Q_D(const KoShape);
2352     return d->additionalAttributes.value(name);
2353 }
2354 
2355 void KoShape::setAdditionalStyleAttribute(const char *name, const QString &value)
2356 {
2357     Q_D(KoShape);
2358     d->additionalStyleAttributes.insert(name, value);
2359 }
2360 
2361 void KoShape::removeAdditionalStyleAttribute(const char *name)
2362 {
2363     Q_D(KoShape);
2364     d->additionalStyleAttributes.remove(name);
2365 }
2366 
2367 QString KoShape::additionalStyleAttribute(const QByteArray &name) const
2368 {
2369     Q_D(const KoShape);
2370     return d->additionalStyleAttributes.value(name);
2371 }
2372 
2373 QMap<QByteArray, QString> KoShape::additionalStyleAttributes() const
2374 {
2375     Q_D(const KoShape);
2376     return d->additionalStyleAttributes;
2377 }
2378 
2379 KoFilterEffectStack *KoShape::filterEffectStack() const
2380 {
2381     Q_D(const KoShape);
2382     return d->filterEffectStack;
2383 }
2384 
2385 void KoShape::setFilterEffectStack(KoFilterEffectStack *filterEffectStack)
2386 {
2387     Q_D(KoShape);
2388     if (d->filterEffectStack)
2389         d->filterEffectStack->deref();
2390     d->filterEffectStack = filterEffectStack;
2391     if (d->filterEffectStack) {
2392         d->filterEffectStack->ref();
2393     }
2394     notifyChanged();
2395 }
2396 
2397 QSet<KoShape*> KoShape::toolDelegates() const
2398 {
2399     Q_D(const KoShape);
2400     return d->toolDelegates;
2401 }
2402 
2403 void KoShape::setToolDelegates(const QSet<KoShape*> &delegates)
2404 {
2405     Q_D(KoShape);
2406     d->toolDelegates = delegates;
2407 }
2408 
2409 QString KoShape::hyperLink () const
2410 {
2411     Q_D(const KoShape);
2412     return d->hyperLink;
2413 }
2414 
2415 void KoShape::setHyperLink(const QString &hyperLink)
2416 {
2417     Q_D(KoShape);
2418     d->hyperLink = hyperLink;
2419 }
2420 
2421 KoShapePrivate *KoShape::priv()
2422 {
2423     Q_D(KoShape);
2424     return d;
2425 }