KoShape.cpp 74.7 KB
Newer Older
Thomas Zander's avatar
Thomas Zander committed
1
/* This file is part of the KDE project
C. Boemann's avatar
C. Boemann committed
2
   Copyright (C) 2006 C. Boemann Rasmussen <cbo@boemann.dk>
3
   Copyright (C) 2006-2010 Thomas Zander <zander@kde.org>
4
   Copyright (C) 2006-2010 Thorsten Zachmann <zachmann@kde.org>
5
   Copyright (C) 2007-2009,2011 Jan Hambrecht <jaham@gmx.net>
6
   CopyRight (C) 2010 Boudewijn Rempt <boud@kogmbh.com>
Thomas Zander's avatar
Thomas Zander committed
7 8 9 10 11 12 13 14 15 16 17 18 19 20

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21
   Boston, MA 02110-1301, USA.
Thomas Zander's avatar
Thomas Zander committed
22 23 24
*/

#include "KoShape.h"
25
#include "KoShape_p.h"
Thomas Zander's avatar
Thomas Zander committed
26
#include "KoShapeContainer.h"
27
#include "KoShapeLayer.h"
28
#include "KoShapeContainerModel.h"
Thomas Zander's avatar
Thomas Zander committed
29
#include "KoSelection.h"
30
#include "KoPointerEvent.h"
Thomas Zander's avatar
Thomas Zander committed
31
#include "KoInsets.h"
32
#include "KoShapeStrokeModel.h"
33 34
#include "KoShapeBackground.h"
#include "KoColorBackground.h"
35
#include "KoHatchBackground.h"
36 37
#include "KoGradientBackground.h"
#include "KoPatternBackground.h"
38
#include "KoShapeManager.h"
39
#include "KoShapeUserData.h"
40
#include "KoShapeApplicationData.h"
41
#include "KoShapeSavingContext.h"
42
#include "KoShapeLoadingContext.h"
43
#include "KoViewConverter.h"
44
#include "KoShapeStroke.h"
45
#include "KoShapeShadow.h"
46
#include "KoClipPath.h"
47
#include "KoPathShape.h"
48
#include "KoOdfWorkaround.h"
49
#include "KoFilterEffectStack.h"
50
#include <KoSnapData.h>
51
#include <KoElementReference.h>
Thomas Zander's avatar
Thomas Zander committed
52

53
#include <KoXmlReader.h>
54
#include <KoXmlWriter.h>
55
#include <KoXmlNS.h>
56
#include <KoGenStyle.h>
57
#include <KoGenStyles.h>
58
#include <KoUnit.h>
59
#include <KoOdfStylesReader.h>
60
#include <KoOdfGraphicStyles.h>
61
#include <KoOdfLoadingContext.h>
62
#include <KoStyleStack.h>
63
#include <KoBorder.h>
64

Thomas Zander's avatar
Thomas Zander committed
65
#include <QPainter>
66
#include <QVariant>
Thomas Zander's avatar
Thomas Zander committed
67
#include <QPainterPath>
68
#include <QList>
69
#include <QMap>
70
#include <QByteArray>
Boudewijn Rempt's avatar
Boudewijn Rempt committed
71
#include <FlakeDebug.h>
72

73 74
#include "kis_assert.h"

75
#include <limits>
76
#include "KoOdfGradientBackground.h"
77
#include <KisHandlePainterHelper.h>
78

79 80
// KoShapePrivate

81
KoShapePrivate::KoShapePrivate(KoShape *shape)
82 83
    : q_ptr(shape),
      size(50, 50),
84 85
      parent(0),
      shadow(0),
86
      border(0),
87 88 89
      filterEffectStack(0),
      transparency(0.0),
      zIndex(0),
90
      runThrough(0),
91 92 93 94 95 96 97
      visible(true),
      printable(true),
      geometryProtected(false),
      keepAspect(false),
      selectable(true),
      detectCollision(false),
      protectContent(false),
98
      textRunAroundSide(KoShape::BiggestRunAroundSide),
99
      textRunAroundDistanceLeft(0.0),
100
      textRunAroundDistanceTop(0.0),
101 102
      textRunAroundDistanceRight(0.0),
      textRunAroundDistanceBottom(0.0),
103
      textRunAroundThreshold(0.0),
104
      textRunAroundContour(KoShape::ContourFull)
105
{
106 107 108 109
    connectors[KoConnectionPoint::TopConnectionPoint] = KoConnectionPoint::defaultConnectionPoint(KoConnectionPoint::TopConnectionPoint);
    connectors[KoConnectionPoint::RightConnectionPoint] = KoConnectionPoint::defaultConnectionPoint(KoConnectionPoint::RightConnectionPoint);
    connectors[KoConnectionPoint::BottomConnectionPoint] = KoConnectionPoint::defaultConnectionPoint(KoConnectionPoint::BottomConnectionPoint);
    connectors[KoConnectionPoint::LeftConnectionPoint] = KoConnectionPoint::defaultConnectionPoint(KoConnectionPoint::LeftConnectionPoint);
110
    connectors[KoConnectionPoint::FirstCustomConnectionPoint] = KoConnectionPoint(QPointF(0.5, 0.5), KoConnectionPoint::AllDirections, KoConnectionPoint::AlignCenter);
111
}
Thomas Zander's avatar
Thomas Zander committed
112

113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
KoShapePrivate::KoShapePrivate(const KoShapePrivate &rhs, KoShape *q)
    : q_ptr(q),
      size(rhs.size),
      shapeId(rhs.shapeId),
      name(rhs.name),
      localMatrix(rhs.localMatrix),
      connectors(rhs.connectors),
      parent(0), // to be initialized later
      shapeManagers(), // to be initialized later
      toolDelegates(), // FIXME: how to initialize them?
      userData(rhs.userData ? rhs.userData->clone() : 0),
      stroke(rhs.stroke),
      fill(rhs.fill),
      dependees(), // FIXME: how to initialize them?
      shadow(0), // WARNING: not implemented in Krita
      border(0), // WARNING: not implemented in Krita
      clipPath(rhs.clipPath ? rhs.clipPath->clone() : 0),
      clipMask(rhs.clipMask ? rhs.clipMask->clone() : 0),
      additionalAttributes(rhs.additionalAttributes),
      additionalStyleAttributes(rhs.additionalStyleAttributes),
      filterEffectStack(0), // WARNING: not implemented in Krita
      transparency(rhs.transparency),
      hyperLink(rhs.hyperLink),

      zIndex(rhs.zIndex),
      runThrough(rhs.runThrough),
      visible(rhs.visible),
      printable(rhs.visible),
      geometryProtected(rhs.geometryProtected),
      keepAspect(rhs.keepAspect),
      selectable(rhs.selectable),
      detectCollision(rhs.detectCollision),
      protectContent(rhs.protectContent),

      textRunAroundSide(rhs.textRunAroundSide),
      textRunAroundDistanceLeft(rhs.textRunAroundDistanceLeft),
      textRunAroundDistanceTop(rhs.textRunAroundDistanceTop),
      textRunAroundDistanceRight(rhs.textRunAroundDistanceRight),
      textRunAroundDistanceBottom(rhs.textRunAroundDistanceBottom),
      textRunAroundThreshold(rhs.textRunAroundThreshold),
      textRunAroundContour(rhs.textRunAroundContour)
{
}

157 158
KoShapePrivate::~KoShapePrivate()
{
159
    Q_Q(KoShape);
160
    if (parent) {
161
        parent->removeShape(q);
162 163
    }

164
    Q_FOREACH (KoShapeManager *manager, shapeManagers) {
165
        manager->shapeInterface()->notifyShapeDestructed(q);
Thomas Zander's avatar
Thomas Zander committed
166
    }
167
    shapeManagers.clear();
168

169
    if (shadow && !shadow->deref())
170
        delete shadow;
171
    if (filterEffectStack && !filterEffectStack->deref())
172
        delete filterEffectStack;
173
}
Thomas Zander's avatar
Thomas Zander committed
174

175 176
void KoShapePrivate::shapeChanged(KoShape::ChangeType type)
{
177
    Q_Q(KoShape);
178
    if (parent)
179
        parent->model()->childChanged(q, type);
180

181
    q->shapeChanged(type);
182 183

    Q_FOREACH (KoShape * shape, dependees) {
184
        shape->shapeChanged(type, q);
185 186 187 188 189
    }

    Q_FOREACH (KoShape::ShapeChangeListener *listener, listeners) {
       listener->notifyShapeChangedImpl(type, q);
    }
190
}
191

192 193 194 195 196 197 198 199
void KoShapePrivate::addShapeManager(KoShapeManager *manager)
{
    shapeManagers.insert(manager);
}

void KoShapePrivate::removeShapeManager(KoShapeManager *manager)
{
    shapeManagers.remove(manager);
200 201
}

202
void KoShapePrivate::convertFromShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
203
{
Jan Hambrecht's avatar
Jan Hambrecht committed
204
    switch(point.alignment) {
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
        case KoConnectionPoint::AlignNone:
            point.position = KoFlake::toRelative(point.position, shapeSize);
            point.position.rx() = qBound<qreal>(0.0, point.position.x(), 1.0);
            point.position.ry() = qBound<qreal>(0.0, point.position.y(), 1.0);
            break;
        case KoConnectionPoint::AlignRight:
            point.position.rx() -= shapeSize.width();
        case KoConnectionPoint::AlignLeft:
            point.position.ry() = 0.5*shapeSize.height();
            break;
        case KoConnectionPoint::AlignBottom:
            point.position.ry() -= shapeSize.height();
        case KoConnectionPoint::AlignTop:
            point.position.rx() = 0.5*shapeSize.width();
            break;
        case KoConnectionPoint::AlignTopLeft:
            // nothing to do here
            break;
        case KoConnectionPoint::AlignTopRight:
            point.position.rx() -= shapeSize.width();
            break;
        case KoConnectionPoint::AlignBottomLeft:
            point.position.ry() -= shapeSize.height();
            break;
        case KoConnectionPoint::AlignBottomRight:
            point.position.rx() -= shapeSize.width();
            point.position.ry() -= shapeSize.height();
            break;
        case KoConnectionPoint::AlignCenter:
            point.position.rx() -= 0.5 * shapeSize.width();
            point.position.ry() -= 0.5 * shapeSize.height();
            break;
    }
}

void KoShapePrivate::convertToShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
{
Jan Hambrecht's avatar
Jan Hambrecht committed
242
    switch(point.alignment) {
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
        case KoConnectionPoint::AlignNone:
            point.position = KoFlake::toAbsolute(point.position, shapeSize);
            break;
        case KoConnectionPoint::AlignRight:
            point.position.rx() += shapeSize.width();
        case KoConnectionPoint::AlignLeft:
            point.position.ry() = 0.5*shapeSize.height();
            break;
        case KoConnectionPoint::AlignBottom:
            point.position.ry() += shapeSize.height();
        case KoConnectionPoint::AlignTop:
            point.position.rx() = 0.5*shapeSize.width();
            break;
        case KoConnectionPoint::AlignTopLeft:
            // nothing to do here
            break;
        case KoConnectionPoint::AlignTopRight:
            point.position.rx() += shapeSize.width();
            break;
        case KoConnectionPoint::AlignBottomLeft:
            point.position.ry() += shapeSize.height();
            break;
        case KoConnectionPoint::AlignBottomRight:
            point.position.rx() += shapeSize.width();
            point.position.ry() += shapeSize.height();
            break;
        case KoConnectionPoint::AlignCenter:
            point.position.rx() += 0.5 * shapeSize.width();
            point.position.ry() += 0.5 * shapeSize.height();
            break;
    }
274 275
}

276
// static
277
QString KoShapePrivate::getStyleProperty(const char *property, KoShapeLoadingContext &context)
278 279 280 281 282 283 284 285 286 287 288
{
    KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
    QString value;

    if (styleStack.hasProperty(KoXmlNS::draw, property)) {
        value = styleStack.property(KoXmlNS::draw, property);
    }

    return value;
}

289 290


291
// ======== KoShape
Thomas Zander's avatar
Thomas Zander committed
292
KoShape::KoShape()
293
    : d_ptr(new KoShapePrivate(this))
Thomas Zander's avatar
Thomas Zander committed
294
{
295
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
296 297
}

298 299
KoShape::KoShape(KoShapePrivate *dd)
    : d_ptr(dd)
300 301 302
{
}

Thomas Zander's avatar
Thomas Zander committed
303 304
KoShape::~KoShape()
{
305
    Q_D(KoShape);
306
    d->shapeChanged(Deleted);
307
    delete d_ptr;
Thomas Zander's avatar
Thomas Zander committed
308 309
}

310 311
KoShape *KoShape::cloneShape() const
{
312
    KIS_SAFE_ASSERT_RECOVER_NOOP(0 && "not implemented!");
313 314 315
    return 0;
}

Thomas Zander's avatar
Thomas Zander committed
316
void KoShape::scale(qreal sx, qreal sy)
Thomas Zander's avatar
Thomas Zander committed
317
{
318
    Q_D(KoShape);
319
    QPointF pos = position();
320
    QTransform scaleMatrix;
321 322 323
    scaleMatrix.translate(pos.x(), pos.y());
    scaleMatrix.scale(sx, sy);
    scaleMatrix.translate(-pos.x(), -pos.y());
324
    d->localMatrix = d->localMatrix * scaleMatrix;
325

326
    notifyChanged();
327
    d->shapeChanged(ScaleChanged);
Thomas Zander's avatar
Thomas Zander committed
328 329
}

330
void KoShape::rotate(qreal angle)
Thomas Zander's avatar
Thomas Zander committed
331
{
332
    Q_D(KoShape);
333
    QPointF center = d->localMatrix.map(QPointF(0.5 * size().width(), 0.5 * size().height()));
334
    QTransform rotateMatrix;
335 336 337
    rotateMatrix.translate(center.x(), center.y());
    rotateMatrix.rotate(angle);
    rotateMatrix.translate(-center.x(), -center.y());
338
    d->localMatrix = d->localMatrix * rotateMatrix;
339

340
    notifyChanged();
341
    d->shapeChanged(RotationChanged);
Thomas Zander's avatar
Thomas Zander committed
342 343
}

Thomas Zander's avatar
Thomas Zander committed
344
void KoShape::shear(qreal sx, qreal sy)
Thomas Zander's avatar
Thomas Zander committed
345
{
346
    Q_D(KoShape);
347
    QPointF pos = position();
348
    QTransform shearMatrix;
349 350 351
    shearMatrix.translate(pos.x(), pos.y());
    shearMatrix.shear(sx, sy);
    shearMatrix.translate(-pos.x(), -pos.y());
352
    d->localMatrix = d->localMatrix * shearMatrix;
353

354
    notifyChanged();
355
    d->shapeChanged(ShearChanged);
Thomas Zander's avatar
Thomas Zander committed
356 357
}

358
void KoShape::setSize(const QSizeF &newSize)
Thomas Zander's avatar
Thomas Zander committed
359
{
360
    Q_D(KoShape);
361
    QSizeF oldSize(size());
362

363
    // always set size, as d->size and size() may vary
Thomas Zander's avatar
Thomas Zander committed
364
    d->size = newSize;
365

366 367 368
    if (oldSize == newSize)
        return;

369
    notifyChanged();
370
    d->shapeChanged(SizeChanged);
Thomas Zander's avatar
Thomas Zander committed
371 372
}

373
void KoShape::setPosition(const QPointF &newPosition)
Thomas Zander's avatar
Thomas Zander committed
374
{
375
    Q_D(KoShape);
376
    QPointF currentPos = position();
377
    if (newPosition == currentPos)
Thomas Zander's avatar
Thomas Zander committed
378
        return;
379
    QTransform translateMatrix;
380
    translateMatrix.translate(newPosition.x() - currentPos.x(), newPosition.y() - currentPos.y());
381 382 383
    d->localMatrix = d->localMatrix * translateMatrix;

    notifyChanged();
384
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
385 386
}

387
bool KoShape::hitTest(const QPointF &position) const
Thomas Zander's avatar
Thomas Zander committed
388
{
389
    Q_D(const KoShape);
390
    if (d->parent && d->parent->isClipped(this) && !d->parent->hitTest(position))
Thomas Zander's avatar
Thomas Zander committed
391 392
        return false;

393
    QPointF point = absoluteTransformation(0).inverted().map(position);
394
    QRectF bb(QPointF(), size());
395
    if (d->stroke) {
396
        KoInsets insets;
397
        d->stroke->strokeInsets(this, insets);
398
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
399
    }
400 401 402
    if (bb.contains(point))
        return true;

403
    // if there is no shadow we can as well just leave
404
    if (! d->shadow)
405
        return false;
Thomas Zander's avatar
Thomas Zander committed
406

407 408
    // the shadow has an offset to the shape, so we simply
    // check if the position minus the shadow offset hits the shape
409
    point = absoluteTransformation(0).inverted().map(position - d->shadow->offset());
410

411
    return bb.contains(point);
Thomas Zander's avatar
Thomas Zander committed
412 413
}

414
QRectF KoShape::boundingRect() const
Thomas Zander's avatar
Thomas Zander committed
415
{
416
    Q_D(const KoShape);
Jan Hambrecht's avatar
Jan Hambrecht committed
417

418
    QTransform transform = absoluteTransformation(0);
419
    QRectF bb = outlineRect();
420
    if (d->stroke) {
421
        KoInsets insets;
422
        d->stroke->strokeInsets(this, insets);
423 424
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
425
    bb = transform.mapRect(bb);
426
    if (d->shadow) {
427
        KoInsets insets;
Thomas Zander's avatar
Thomas Zander committed
428
        d->shadow->insets(insets);
429 430
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
431
    if (d->filterEffectStack) {
432
        QRectF clipRect = d->filterEffectStack->clipRectForBoundingRect(outlineRect());
433 434
        bb |= transform.mapRect(clipRect);
    }
Thomas Zander's avatar
Thomas Zander committed
435

436
    return bb;
Thomas Zander's avatar
Thomas Zander committed
437 438
}

439 440 441 442 443 444 445 446 447
QRectF KoShape::boundingRect(const QList<KoShape *> &shapes)
{
    QRectF boundingRect;
    Q_FOREACH (KoShape *shape, shapes) {
        boundingRect |= shape->boundingRect();
    }
    return boundingRect;
}

448 449 450 451 452 453 454 455 456 457 458 459 460 461
QRectF KoShape::absoluteOutlineRect(KoViewConverter *converter) const
{
    return absoluteTransformation(converter).map(outline()).boundingRect();
}

QRectF KoShape::absoluteOutlineRect(const QList<KoShape *> &shapes, KoViewConverter *converter)
{
    QRectF absoluteOutlineRect;
    Q_FOREACH (KoShape *shape, shapes) {
        absoluteOutlineRect |= shape->absoluteOutlineRect(converter);
    }
    return absoluteOutlineRect;
}

462
QTransform KoShape::absoluteTransformation(const KoViewConverter *converter) const
463
{
464
    Q_D(const KoShape);
465
    QTransform matrix;
Thomas Zander's avatar
Thomas Zander committed
466
    // apply parents matrix to inherit any transformations done there.
467
    KoShapeContainer * container = d->parent;
468
    if (container) {
469
        if (container->inheritsTransform(this)) {
470 471 472 473
            // We do need to pass the converter here, otherwise the parent's
            // translation is not inherited.
            matrix = container->absoluteTransformation(converter);
        } else {
474 475 476 477
            QSizeF containerSize = container->size();
            QPointF containerPos = container->absolutePosition() - QPointF(0.5 * containerSize.width(), 0.5 * containerSize.height());
            if (converter)
                containerPos = converter->documentToView(containerPos);
478
            matrix.translate(containerPos.x(), containerPos.y());
Thomas Zander's avatar
Thomas Zander committed
479 480 481
        }
    }

482
    if (converter) {
483 484 485
        QPointF pos = d->localMatrix.map(QPointF());
        QPointF trans = converter->documentToView(pos) - pos;
        matrix.translate(trans.x(), trans.y());
Thomas Zander's avatar
Thomas Zander committed
486
    }
487 488

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
489 490
}

491
void KoShape::applyAbsoluteTransformation(const QTransform &matrix)
492
{
493
    QTransform globalMatrix = absoluteTransformation(0);
494 495
    // the transformation is relative to the global coordinate system
    // but we want to change the local matrix, so convert the matrix
Thomas Zander's avatar
Thomas Zander committed
496
    // to be relative to the local coordinate system
497
    QTransform transformMatrix = globalMatrix * matrix * globalMatrix.inverted();
498
    applyTransformation(transformMatrix);
499 500
}

501
void KoShape::applyTransformation(const QTransform &matrix)
502
{
503
    Q_D(KoShape);
504
    d->localMatrix = matrix * d->localMatrix;
505
    notifyChanged();
506
    d->shapeChanged(GenericMatrixChange);
507 508
}

509
void KoShape::setTransformation(const QTransform &matrix)
510
{
511
    Q_D(KoShape);
512 513
    d->localMatrix = matrix;
    notifyChanged();
514
    d->shapeChanged(GenericMatrixChange);
515
}
Thomas Zander's avatar
Thomas Zander committed
516

517
QTransform KoShape::transformation() const
518
{
519
    Q_D(const KoShape);
520 521 522
    return d->localMatrix;
}

523
KoShape::ChildZOrderPolicy KoShape::childZOrderPolicy()
524
{
525
    return ChildZDefault;
526 527
}

528 529
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
{
530 531 532 533 534 535 536 537 538 539 540 541 542
    /**
     * WARNING: Our definition of zIndex is not yet compatible with SVG2's
     *          definition. In SVG stacking context of groups with the same
     *          zIndex are **merged**, while in Krita the contents of groups
     *          is never merged. One group will always below than the other.
     *          Therefore, when zIndex of two groups inside the same parent
     *          coinside, the resulting painting order in Krita is
     *          **UNDEFINED**.
     *
     *          To avoid this trouble we use  KoShapeReorderCommand::mergeInShape()
     *          inside KoShapeCreateCommand.
     */

543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
    // First sort according to runThrough which is sort of a master level
    KoShape *parentShapeS1 = s1->parent();
    KoShape *parentShapeS2 = s2->parent();
    int runThrough1 = s1->runThrough();
    int runThrough2 = s2->runThrough();
    while (parentShapeS1) {
        if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) {
            runThrough1 = parentShapeS1->runThrough();
        } else {
            runThrough1 = runThrough1 + parentShapeS1->runThrough();
        }
        parentShapeS1 = parentShapeS1->parent();
    }

    while (parentShapeS2) {
        if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) {
            runThrough2 = parentShapeS2->runThrough();
        } else {
            runThrough2 = runThrough2 + parentShapeS2->runThrough();
        }
        parentShapeS2 = parentShapeS2->parent();
    }

    if (runThrough1 > runThrough2) {
        return false;
    }
    if (runThrough1 < runThrough2) {
        return true;
    }

    // If on the same runThrough level then the zIndex is all that matters.
    //
    // We basically walk up through the parents until we find a common base parent
    // To do that we need two loops where the inner loop walks up through the parents
    // of s2 every time we step up one parent level on s1
    //
    // We don't update the index value until after we have seen that it's not a common base
    // That way we ensure that two children of a common base are sorted according to their respective
    // z value
582
    bool foundCommonParent = false;
583 584 585 586
    int index1 = s1->zIndex();
    int index2 = s2->zIndex();
    parentShapeS1 = s1;
    parentShapeS2 = s2;
587 588 589 590 591 592 593 594
    while (parentShapeS1 && !foundCommonParent) {
        parentShapeS2 = s2;
        index2 = parentShapeS2->zIndex();
        while (parentShapeS2) {
            if (parentShapeS2 == parentShapeS1) {
                foundCommonParent = true;
                break;
            }
595
            if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) {
596 597
                index2 = parentShapeS2->zIndex();
            }
598
            parentShapeS2 = parentShapeS2->parent();
599
        }
600 601

        if (!foundCommonParent) {
602
            if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) {
603 604
                index1 = parentShapeS1->zIndex();
            }
605
            parentShapeS1 = parentShapeS1->parent();
606 607
        }
    }
608

609 610 611 612 613 614 615 616
    // If the one shape is a parent/child of the other then sort so.
    if (s1 == parentShapeS2) {
        return true;
    }
    if (s2 == parentShapeS1) {
        return false;
    }

617
    // If we went that far then the z-Index is used for sorting.
618
    return index1 < index2;
Thomas Zander's avatar
Thomas Zander committed
619 620
}

621 622
void KoShape::setParent(KoShapeContainer *parent)
{
623
    Q_D(KoShape);
624 625

    if (d->parent == parent) {
626
        return;
627 628
    }

629 630
    KoShapeContainer *oldParent = d->parent;
    d->parent = 0; // avoids recursive removing
631 632 633 634 635 636 637

    if (oldParent) {
        oldParent->shapeInterface()->removeShape(this);
    }

    KIS_SAFE_ASSERT_RECOVER_NOOP(parent != this);

Thomas Zander's avatar
Thomas Zander committed
638
    if (parent && parent != this) {
Thomas Zander's avatar
Thomas Zander committed
639
        d->parent = parent;
640
        parent->shapeInterface()->addShape(this);
641
    }
642

643
    notifyChanged();
644
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
645 646
}

647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668
bool KoShape::inheritsTransformFromAny(const QList<KoShape *> ancestorsInQuestion) const
{
    bool result = false;

    KoShape *shape = const_cast<KoShape*>(this);
    while (shape) {
        KoShapeContainer *parent = shape->parent();
        if (parent && !parent->inheritsTransform(shape)) {
            break;
        }

        if (ancestorsInQuestion.contains(shape)) {
            result = true;
            break;
        }

        shape = parent;
    }

    return result;
}

669 670
int KoShape::zIndex() const
{
671
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
672
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
673 674
}

675 676
void KoShape::update() const
{
677
    Q_D(const KoShape);
678

679 680
    if (!d->shapeManagers.empty()) {
        QRectF rect(boundingRect());
681
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
Jan Hambrecht's avatar
Jan Hambrecht committed
682
            manager->update(rect, this, true);
683
        }
Thomas Zander's avatar
Thomas Zander committed
684 685 686
    }
}

687
void KoShape::updateAbsolute(const QRectF &rect) const
688
{
689 690 691 692 693

    if (rect.isEmpty() && !rect.isNull()) {
        return;
    }

694
    Q_D(const KoShape);
695

696
    if (!d->shapeManagers.empty() && isVisible()) {
697
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
698
            manager->update(rect);
699 700
        }
    }
Thomas Zander's avatar
Thomas Zander committed
701 702
}

703
QPainterPath KoShape::outline() const
704
{
Thomas Zander's avatar
Thomas Zander committed
705
    QPainterPath path;
706
    path.addRect(outlineRect());
Thomas Zander's avatar
Thomas Zander committed
707 708 709
    return path;
}

710 711
QRectF KoShape::outlineRect() const
{
712
    const QSizeF s = size();
713 714 715 716 717 718 719
    return QRectF(QPointF(0, 0), QSizeF(qMax(s.width(),  qreal(0.0001)),
                                        qMax(s.height(), qreal(0.0001))));
}

QPainterPath KoShape::shadowOutline() const
{
    Q_D(const KoShape);
720
    if (d->fill) {
721
        return outline();
722
    }
723 724

    return QPainterPath();
725 726
}

727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753
QPointF KoShape::absolutePosition(KoFlake::AnchorPosition anchor) const
{
    const QRectF rc = outlineRect();

    QPointF point = rc.topLeft();

    bool valid = false;
    QPointF anchoredPoint = KoFlake::anchorToPoint(anchor, rc, &valid);
    if (valid) {
        point = anchoredPoint;
    }

    return absoluteTransformation(0).map(point);
}

void KoShape::setAbsolutePosition(const QPointF &newPosition, KoFlake::AnchorPosition anchor)
{
    Q_D(KoShape);
    QPointF currentAbsPosition = absolutePosition(anchor);
    QPointF translate = newPosition - currentAbsPosition;
    QTransform translateMatrix;
    translateMatrix.translate(translate.x(), translate.y());
    applyAbsoluteTransformation(translateMatrix);
    notifyChanged();
    d->shapeChanged(PositionChanged);
}

754 755
void KoShape::copySettings(const KoShape *shape)
{
756
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
757 758
    d->size = shape->size();
    d->connectors.clear();
759
    Q_FOREACH (const KoConnectionPoint &point, shape->connectionPoints())
Jan Hambrecht's avatar
Jan Hambrecht committed
760
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
761 762
    d->zIndex = shape->zIndex();
    d->visible = shape->isVisible();
763

764 765 766 767 768
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
769

770
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
771 772
    d->protectContent = shape->isContentProtected();
    d->selectable = shape->isSelectable();
Thomas Zander's avatar
Thomas Zander committed
773
    d->keepAspect = shape->keepAspectRatio();
774
    d->localMatrix = shape->d_ptr->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
775 776
}

Thomas Zander's avatar
Thomas Zander committed
777
void KoShape::notifyChanged()
778
{
779
    Q_D(KoShape);
780
    Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
781
        manager->notifyShapeChanged(this);
782 783 784
    }
}

785 786
void KoShape::setUserData(KoShapeUserData *userData)
{
787
    Q_D(KoShape);
788
    d->userData.reset(userData);
789 790
}

791 792
KoShapeUserData *KoShape::userData() const
{
793
    Q_D(const KoShape);
794
    return d->userData.data();
795 796
}

Thomas Zander's avatar
Thomas Zander committed
797
bool KoShape::hasTransparency() const
798
{
Thomas Zander's avatar
Thomas Zander committed
799
    Q_D(const KoShape);
800
    if (! d->fill)
Thomas Zander's avatar
Thomas Zander committed
801
        return true;
802
    else
803 804 805 806 807 808
        return d->fill->hasTransparency() || d->transparency > 0.0;
}

void KoShape::setTransparency(qreal transparency)
{
    Q_D(KoShape);
809
    d->transparency = qBound<qreal>(0.0, transparency, 1.0);
810 811 812

    d->shapeChanged(TransparencyChanged);
    notifyChanged();
813 814 815 816 817 818 819 820 821 822 823 824
}

qreal KoShape::transparency(bool recursive) const
{
    Q_D(const KoShape);
    if (!recursive || !parent()) {
        return d->transparency;
    } else {
        const qreal parentOpacity = 1.0-parent()->transparency(recursive);
        const qreal childOpacity = 1.0-d->transparency;
        return 1.0-(parentOpacity*childOpacity);
    }
Thomas Zander's avatar
Thomas Zander committed
825 826
}

827
KoInsets KoShape::strokeInsets() const
828
{
829
    Q_D(const KoShape);
830
    KoInsets answer;
831 832
    if (d->stroke)
        d->stroke->strokeInsets(this, answer);
833 834 835
    return answer;
}

836 837
qreal KoShape::rotation() const
{
838
    Q_D(const KoShape);
839
    // try to extract the rotation angle out of the local matrix
840 841 842
    // if it is a pure rotation matrix

    // check if the matrix has shearing mixed in
843
    if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
844
        return std::numeric_limits<qreal>::quiet_NaN();
845
    // check if the matrix has scaling mixed in
846
    if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
847
        return std::numeric_limits<qreal>::quiet_NaN();
848 849

    // calculate the angle from the matrix elements
850 851
    qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
    if (angle < 0.0)
852 853 854
        angle += 360.0;

    return angle;
Thomas Zander's avatar
Thomas Zander committed
855 856
}

857
QSizeF KoShape::size() const
858
{
859
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
860 861 862
    return d->size;
}

863 864
QPointF KoShape::position() const
{
865
    Q_D(const KoShape);
866
    QPointF center = outlineRect().center();
867
    return d->localMatrix.map(center) - center;
Thomas Zander's avatar
Thomas Zander committed
868 869
}

870
int KoShape::addConnectionPoint(const KoConnectionPoint &point)
871
{
872
    Q_D(KoShape);
873

874
    // get next glue point id
875
    int nextConnectionPointId = KoConnectionPoint::FirstCustomConnectionPoint;
Jan Hambrecht's avatar
Jan Hambrecht committed
876
    if (d->connectors.size())
877 878
        nextConnectionPointId = qMax(nextConnectionPointId, (--d->connectors.end()).key()+1);

879
    KoConnectionPoint p = point;
880 881
    d->convertFromShapeCoordinates(p, size());
    d->connectors[nextConnectionPointId] = p;
882 883 884 885

    return nextConnectionPointId;
}

886
bool KoShape::setConnectionPoint(int connectionPointId, const KoConnectionPoint &point)
887 888
{
    Q_D(KoShape);
889
    if (connectionPointId < 0)
890 891
        return false;

892 893
    const bool insertPoint = !hasConnectionPoint(connectionPointId);

894
    switch(connectionPointId) {
895 896 897 898 899 900
        case KoConnectionPoint::TopConnectionPoint:
        case KoConnectionPoint::RightConnectionPoint:
        case KoConnectionPoint::BottomConnectionPoint:
        case KoConnectionPoint::LeftConnectionPoint:
        {
            KoConnectionPoint::PointId id = static_cast<KoConnectionPoint::PointId>(connectionPointId);
901
            d->connectors[id] = KoConnectionPoint::defaultConnectionPoint(id);
902
            break;
903
        }
904
        default:
905
        {
906
            KoConnectionPoint p = point;
907 908
            d->convertFromShapeCoordinates(p, size());
            d->connectors[connectionPointId] = p;
909
            break;
910
        }
911 912
    }

913 914
    if(!insertPoint)
        d->shapeChanged(ConnectionPointChanged);
915 916 917 918

    return true;
}

919 920 921 922 923 924
bool KoShape::hasConnectionPoint(int connectionPointId) const
{
    Q_D(const KoShape);
    return d->connectors.contains(connectionPointId);
}

925
KoConnectionPoint KoShape::connectionPoint(int connectionPointId) const
926 927
{
    Q_D(const KoShape);
928
    KoConnectionPoint p = d->connectors.value(connectionPointId, KoConnectionPoint());
929
    // convert glue point to shape coordinates
930
    d->convertToShapeCoordinates(p, size());
931
    return p;
Thomas Zander's avatar
Thomas Zander committed
932 933
}

934
KoConnectionPoints KoShape::connectionPoints() const
935
{
936
    Q_D(const KoShape);
937
    QSizeF s = size();
938 939 940
    KoConnectionPoints points = d->connectors;
    KoConnectionPoints::iterator point = points.begin();
    KoConnectionPoints::iterator lastPoint = points.end();
941
    // convert glue points to shape coordinates
942
    for(; point != lastPoint; ++point) {
943
        d->convertToShapeCoordinates(point.value(), s);
944
    }
945 946

    return points;
947 948
}

949 950 951 952
void KoShape::removeConnectionPoint(int connectionPointId)
{
    Q_D(KoShape);
    d->connectors.remove(connectionPointId);
953
    d->shapeChanged(ConnectionPointChanged);
954 955 956 957 958 959 960 961
}

void KoShape::clearConnectionPoints()
{
    Q_D(KoShape);
    d->connectors.clear();
}

962 963 964 965 966 967
KoShape::TextRunAroundSide KoShape::textRunAroundSide() const
{
    Q_D(const KoShape);
    return d->textRunAroundSide;
}

968
void KoShape::setTextRunAroundSide(TextRunAroundSide side, RunThroughLevel runThrought)
969 970 971 972 973 974 975
{
    Q_D(KoShape);

    if (side == RunThrough) {
        if (runThrought == Background) {
            setRunThrough(-1);
        } else {