KoShape.cpp 75 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
    Q_FOREACH (KoShapeManager *manager, shapeManagers) {
163
        manager->remove(q);
Thomas Zander's avatar
Thomas Zander committed
164
    }
165

166
    if (shadow && !shadow->deref())
167
        delete shadow;
168
    if (filterEffectStack && !filterEffectStack->deref())
169
        delete filterEffectStack;
170
}
Thomas Zander's avatar
Thomas Zander committed
171

172 173
void KoShapePrivate::shapeChanged(KoShape::ChangeType type)
{
174
    Q_Q(KoShape);
175
    if (parent)
176
        parent->model()->childChanged(q, type);
177

178
    q->shapeChanged(type);
179 180

    Q_FOREACH (KoShape * shape, dependees) {
181
        shape->shapeChanged(type, q);
182 183 184 185 186
    }

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

189
void KoShapePrivate::updateStroke()
190
{
191
    Q_Q(KoShape);
192 193
    if (!stroke) return;

194
    KoInsets insets;
195
    stroke->strokeInsets(q, insets);
196 197 198
    QSizeF inner = q->size();
    // update left
    q->update(QRectF(-insets.left, -insets.top, insets.left,
199
                     inner.height() + insets.top + insets.bottom));
200 201
    // update top
    q->update(QRectF(-insets.left, -insets.top,
202
                     inner.width() + insets.left + insets.right, insets.top));
203 204
    // update right
    q->update(QRectF(inner.width(), -insets.top, insets.right,
205
                     inner.height() + insets.top + insets.bottom));
206 207
    // update bottom
    q->update(QRectF(-insets.left, inner.height(),
208
                     inner.width() + insets.left + insets.right, insets.bottom));
209
}
Thomas Zander's avatar
Thomas Zander committed
210

211 212 213 214 215 216 217 218
void KoShapePrivate::addShapeManager(KoShapeManager *manager)
{
    shapeManagers.insert(manager);
}

void KoShapePrivate::removeShapeManager(KoShapeManager *manager)
{
    shapeManagers.remove(manager);
219 220
}

221
void KoShapePrivate::convertFromShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
222
{
Jan Hambrecht's avatar
Jan Hambrecht committed
223
    switch(point.alignment) {
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
        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
261
    switch(point.alignment) {
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
        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;
    }
293 294
}

295
// static
296
QString KoShapePrivate::getStyleProperty(const char *property, KoShapeLoadingContext &context)
297 298 299 300 301 302 303 304 305 306 307
{
    KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
    QString value;

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

    return value;
}

308 309


310
// ======== KoShape
Thomas Zander's avatar
Thomas Zander committed
311
KoShape::KoShape()
312
    : d_ptr(new KoShapePrivate(this))
Thomas Zander's avatar
Thomas Zander committed
313
{
314
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
315 316
}

317 318
KoShape::KoShape(KoShapePrivate *dd)
    : d_ptr(dd)
319 320 321
{
}

Thomas Zander's avatar
Thomas Zander committed
322 323
KoShape::~KoShape()
{
324
    Q_D(KoShape);
325
    d->shapeChanged(Deleted);
326
    delete d_ptr;
Thomas Zander's avatar
Thomas Zander committed
327 328
}

329 330 331 332 333
KoShape *KoShape::cloneShape() const
{
    return 0;
}

Thomas Zander's avatar
Thomas Zander committed
334
void KoShape::scale(qreal sx, qreal sy)
Thomas Zander's avatar
Thomas Zander committed
335
{
336
    Q_D(KoShape);
337
    QPointF pos = position();
338
    QTransform scaleMatrix;
339 340 341
    scaleMatrix.translate(pos.x(), pos.y());
    scaleMatrix.scale(sx, sy);
    scaleMatrix.translate(-pos.x(), -pos.y());
342
    d->localMatrix = d->localMatrix * scaleMatrix;
343

344
    notifyChanged();
345
    d->shapeChanged(ScaleChanged);
Thomas Zander's avatar
Thomas Zander committed
346 347
}

348
void KoShape::rotate(qreal angle)
Thomas Zander's avatar
Thomas Zander committed
349
{
350
    Q_D(KoShape);
351
    QPointF center = d->localMatrix.map(QPointF(0.5 * size().width(), 0.5 * size().height()));
352
    QTransform rotateMatrix;
353 354 355
    rotateMatrix.translate(center.x(), center.y());
    rotateMatrix.rotate(angle);
    rotateMatrix.translate(-center.x(), -center.y());
356
    d->localMatrix = d->localMatrix * rotateMatrix;
357

358
    notifyChanged();
359
    d->shapeChanged(RotationChanged);
Thomas Zander's avatar
Thomas Zander committed
360 361
}

Thomas Zander's avatar
Thomas Zander committed
362
void KoShape::shear(qreal sx, qreal sy)
Thomas Zander's avatar
Thomas Zander committed
363
{
364
    Q_D(KoShape);
365
    QPointF pos = position();
366
    QTransform shearMatrix;
367 368 369
    shearMatrix.translate(pos.x(), pos.y());
    shearMatrix.shear(sx, sy);
    shearMatrix.translate(-pos.x(), -pos.y());
370
    d->localMatrix = d->localMatrix * shearMatrix;
371

372
    notifyChanged();
373
    d->shapeChanged(ShearChanged);
Thomas Zander's avatar
Thomas Zander committed
374 375
}

376
void KoShape::setSize(const QSizeF &newSize)
Thomas Zander's avatar
Thomas Zander committed
377
{
378
    Q_D(KoShape);
379
    QSizeF oldSize(size());
380

381
    // always set size, as d->size and size() may vary
Thomas Zander's avatar
Thomas Zander committed
382
    d->size = newSize;
383

384 385 386
    if (oldSize == newSize)
        return;

387
    notifyChanged();
388
    d->shapeChanged(SizeChanged);
Thomas Zander's avatar
Thomas Zander committed
389 390
}

391
void KoShape::setPosition(const QPointF &newPosition)
Thomas Zander's avatar
Thomas Zander committed
392
{
393
    Q_D(KoShape);
394
    QPointF currentPos = position();
395
    if (newPosition == currentPos)
Thomas Zander's avatar
Thomas Zander committed
396
        return;
397
    QTransform translateMatrix;
398
    translateMatrix.translate(newPosition.x() - currentPos.x(), newPosition.y() - currentPos.y());
399 400 401
    d->localMatrix = d->localMatrix * translateMatrix;

    notifyChanged();
402
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
403 404
}

405
bool KoShape::hitTest(const QPointF &position) const
Thomas Zander's avatar
Thomas Zander committed
406
{
407
    Q_D(const KoShape);
408
    if (d->parent && d->parent->isClipped(this) && !d->parent->hitTest(position))
Thomas Zander's avatar
Thomas Zander committed
409 410
        return false;

411
    QPointF point = absoluteTransformation(0).inverted().map(position);
412
    QRectF bb(QPointF(), size());
413
    if (d->stroke) {
414
        KoInsets insets;
415
        d->stroke->strokeInsets(this, insets);
416
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
417
    }
418 419 420
    if (bb.contains(point))
        return true;

421
    // if there is no shadow we can as well just leave
422
    if (! d->shadow)
423
        return false;
Thomas Zander's avatar
Thomas Zander committed
424

425 426
    // the shadow has an offset to the shape, so we simply
    // check if the position minus the shadow offset hits the shape
427
    point = absoluteTransformation(0).inverted().map(position - d->shadow->offset());
428

429
    return bb.contains(point);
Thomas Zander's avatar
Thomas Zander committed
430 431
}

432
QRectF KoShape::boundingRect() const
Thomas Zander's avatar
Thomas Zander committed
433
{
434
    Q_D(const KoShape);
Jan Hambrecht's avatar
Jan Hambrecht committed
435

436
    QTransform transform = absoluteTransformation(0);
437
    QRectF bb = outlineRect();
438
    if (d->stroke) {
439
        KoInsets insets;
440
        d->stroke->strokeInsets(this, insets);
441 442
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
443
    bb = transform.mapRect(bb);
444
    if (d->shadow) {
445
        KoInsets insets;
Thomas Zander's avatar
Thomas Zander committed
446
        d->shadow->insets(insets);
447 448
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
449
    if (d->filterEffectStack) {
450
        QRectF clipRect = d->filterEffectStack->clipRectForBoundingRect(outlineRect());
451 452
        bb |= transform.mapRect(clipRect);
    }
Thomas Zander's avatar
Thomas Zander committed
453

454
    return bb;
Thomas Zander's avatar
Thomas Zander committed
455 456
}

457 458 459 460 461 462 463 464 465
QRectF KoShape::boundingRect(const QList<KoShape *> &shapes)
{
    QRectF boundingRect;
    Q_FOREACH (KoShape *shape, shapes) {
        boundingRect |= shape->boundingRect();
    }
    return boundingRect;
}

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

486
    if (converter) {
487 488 489
        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
490
    }
491 492

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
493 494
}

495
void KoShape::applyAbsoluteTransformation(const QTransform &matrix)
496
{
497
    QTransform globalMatrix = absoluteTransformation(0);
498 499
    // 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
500
    // to be relative to the local coordinate system
501
    QTransform transformMatrix = globalMatrix * matrix * globalMatrix.inverted();
502
    applyTransformation(transformMatrix);
503 504
}

505
void KoShape::applyTransformation(const QTransform &matrix)
506
{
507
    Q_D(KoShape);
508
    d->localMatrix = matrix * d->localMatrix;
509
    notifyChanged();
510
    d->shapeChanged(GenericMatrixChange);
511 512
}

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

521
QTransform KoShape::transformation() const
522
{
523
    Q_D(const KoShape);
524 525 526
    return d->localMatrix;
}

527
KoShape::ChildZOrderPolicy KoShape::childZOrderPolicy()
528
{
529
    return ChildZDefault;
530 531
}

532 533
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
{
534 535 536 537 538 539 540 541 542 543 544 545 546
    /**
     * 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.
     */

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 582 583 584 585
    // 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
586
    bool foundCommonParent = false;
587 588 589 590
    int index1 = s1->zIndex();
    int index2 = s2->zIndex();
    parentShapeS1 = s1;
    parentShapeS2 = s2;
591 592 593 594 595 596 597 598
    while (parentShapeS1 && !foundCommonParent) {
        parentShapeS2 = s2;
        index2 = parentShapeS2->zIndex();
        while (parentShapeS2) {
            if (parentShapeS2 == parentShapeS1) {
                foundCommonParent = true;
                break;
            }
599
            if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) {
600 601
                index2 = parentShapeS2->zIndex();
            }
602
            parentShapeS2 = parentShapeS2->parent();
603
        }
604 605

        if (!foundCommonParent) {
606
            if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) {
607 608
                index1 = parentShapeS1->zIndex();
            }
609
            parentShapeS1 = parentShapeS1->parent();
610 611
        }
    }
612

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

621
    // If we went that far then the z-Index is used for sorting.
622
    return index1 < index2;
Thomas Zander's avatar
Thomas Zander committed
623 624
}

625 626
void KoShape::setParent(KoShapeContainer *parent)
{
627
    Q_D(KoShape);
628 629

    if (d->parent == parent) {
630
        return;
631 632
    }

633 634
    KoShapeContainer *oldParent = d->parent;
    d->parent = 0; // avoids recursive removing
635 636 637 638 639 640 641

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

    KIS_SAFE_ASSERT_RECOVER_NOOP(parent != this);

Thomas Zander's avatar
Thomas Zander committed
642
    if (parent && parent != this) {
Thomas Zander's avatar
Thomas Zander committed
643
        d->parent = parent;
644
        parent->shapeInterface()->addShape(this);
645
    }
646

647
    notifyChanged();
648
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
649 650
}

651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672
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;
}

673 674
int KoShape::zIndex() const
{
675
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
676
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
677 678
}

679 680
void KoShape::update() const
{
681
    Q_D(const KoShape);
682

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

691
void KoShape::update(const QRectF &rect) const
692
{
693 694 695 696 697

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

698
    Q_D(const KoShape);
699

700
    if (!d->shapeManagers.empty() && isVisible()) {
701
        QRectF rc(absoluteTransformation(0).mapRect(rect));
702
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
703
            manager->update(rc);
704 705
        }
    }
Thomas Zander's avatar
Thomas Zander committed
706 707
}

708
QPainterPath KoShape::outline() const
709
{
Thomas Zander's avatar
Thomas Zander committed
710
    QPainterPath path;
711
    path.addRect(outlineRect());
Thomas Zander's avatar
Thomas Zander committed
712 713 714
    return path;
}

715 716
QRectF KoShape::outlineRect() const
{
717
    const QSizeF s = size();
718 719 720 721 722 723 724
    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);
725
    if (d->fill) {
726
        return outline();
727
    }
728 729

    return QPainterPath();
730 731
}

732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
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);
}

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

769 770 771 772 773
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
774

775
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
776 777
    d->protectContent = shape->isContentProtected();
    d->selectable = shape->isSelectable();
Thomas Zander's avatar
Thomas Zander committed
778
    d->keepAspect = shape->keepAspectRatio();
779
    d->localMatrix = shape->d_ptr->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
780 781
}

Thomas Zander's avatar
Thomas Zander committed
782
void KoShape::notifyChanged()
783
{
784
    Q_D(KoShape);
785
    Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
786
        manager->notifyShapeChanged(this);
787 788 789
    }
}

790 791
void KoShape::setUserData(KoShapeUserData *userData)
{
792
    Q_D(KoShape);
793
    d->userData.reset(userData);
794 795
}

796 797
KoShapeUserData *KoShape::userData() const
{
798
    Q_D(const KoShape);
799
    return d->userData.data();
800 801
}

Thomas Zander's avatar
Thomas Zander committed
802
bool KoShape::hasTransparency() const
803
{
Thomas Zander's avatar
Thomas Zander committed
804
    Q_D(const KoShape);
805
    if (! d->fill)
Thomas Zander's avatar
Thomas Zander committed
806
        return true;
807
    else
808 809 810 811 812 813
        return d->fill->hasTransparency() || d->transparency > 0.0;
}

void KoShape::setTransparency(qreal transparency)
{
    Q_D(KoShape);
814
    d->transparency = qBound<qreal>(0.0, transparency, 1.0);
815 816 817

    d->shapeChanged(TransparencyChanged);
    notifyChanged();
818 819 820 821 822 823 824 825 826 827 828 829
}

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
830 831
}

832
KoInsets KoShape::strokeInsets() const
833
{
834
    Q_D(const KoShape);
835
    KoInsets answer;
836 837
    if (d->stroke)
        d->stroke->strokeInsets(this, answer);
838 839 840
    return answer;
}

841 842
qreal KoShape::rotation() const
{
843
    Q_D(const KoShape);
844
    // try to extract the rotation angle out of the local matrix
845 846 847
    // if it is a pure rotation matrix

    // check if the matrix has shearing mixed in
848
    if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
849
        return std::numeric_limits<qreal>::quiet_NaN();
850
    // check if the matrix has scaling mixed in
851
    if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
852
        return std::numeric_limits<qreal>::quiet_NaN();
853 854

    // calculate the angle from the matrix elements
855 856
    qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
    if (angle < 0.0)
857 858 859
        angle += 360.0;

    return angle;
Thomas Zander's avatar
Thomas Zander committed
860 861
}

862
QSizeF KoShape::size() const
863
{
864
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
865 866 867
    return d->size;
}

868 869
QPointF KoShape::position() const
{
870
    Q_D(const KoShape);
871
    QPointF center = outlineRect().center();
872
    return d->localMatrix.map(center) - center;
Thomas Zander's avatar
Thomas Zander committed
873 874
}

875
int KoShape::addConnectionPoint(const KoConnectionPoint &point)
876
{
877
    Q_D(KoShape);
878

879
    // get next glue point id
880
    int nextConnectionPointId = KoConnectionPoint::FirstCustomConnectionPoint;
Jan Hambrecht's avatar
Jan Hambrecht committed
881
    if (d->connectors.size())
882 883
        nextConnectionPointId = qMax(nextConnectionPointId, (--d->connectors.end()).key()+1);

884
    KoConnectionPoint p = point;
885 886
    d->convertFromShapeCoordinates(p, size());
    d->connectors[nextConnectionPointId] = p;
887 888 889 890

    return nextConnectionPointId;
}

891
bool KoShape::setConnectionPoint(int connectionPointId, const KoConnectionPoint &point)
892 893
{
    Q_D(KoShape);
894
    if (connectionPointId < 0)
895 896
        return false;

897 898
    const bool insertPoint = !hasConnectionPoint(connectionPointId);

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

918 919
    if(!insertPoint)
        d->shapeChanged(ConnectionPointChanged);
920 921 922 923

    return true;
}

924 925 926 927 928 929
bool KoShape::hasConnectionPoint(int connectionPointId) const
{
    Q_D(const KoShape);
    return d->connectors.contains(connectionPointId);
}

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

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