KoShape.cpp 75.5 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
void KoShapePrivate::updateStroke()
193
{
194
    Q_Q(KoShape);
195 196
    if (!stroke) return;

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

214 215 216 217 218 219 220 221
void KoShapePrivate::addShapeManager(KoShapeManager *manager)
{
    shapeManagers.insert(manager);
}

void KoShapePrivate::removeShapeManager(KoShapeManager *manager)
{
    shapeManagers.remove(manager);
222 223
}

224
void KoShapePrivate::convertFromShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
225
{
Jan Hambrecht's avatar
Jan Hambrecht committed
226
    switch(point.alignment) {
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 261 262 263
        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
264
    switch(point.alignment) {
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 293 294 295
        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;
    }
296 297
}

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

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

    return value;
}

311 312


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

320 321
KoShape::KoShape(KoShapePrivate *dd)
    : d_ptr(dd)
322 323 324
{
}

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

332 333 334 335 336
KoShape *KoShape::cloneShape() const
{
    return 0;
}

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

347
    notifyChanged();
348
    d->shapeChanged(ScaleChanged);
Thomas Zander's avatar
Thomas Zander committed
349 350
}

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

361
    notifyChanged();
362
    d->shapeChanged(RotationChanged);
Thomas Zander's avatar
Thomas Zander committed
363 364
}

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

375
    notifyChanged();
376
    d->shapeChanged(ShearChanged);
Thomas Zander's avatar
Thomas Zander committed
377 378
}

379
void KoShape::setSize(const QSizeF &newSize)
Thomas Zander's avatar
Thomas Zander committed
380
{
381
    Q_D(KoShape);
382
    QSizeF oldSize(size());
383

384
    // always set size, as d->size and size() may vary
Thomas Zander's avatar
Thomas Zander committed
385
    d->size = newSize;
386

387 388 389
    if (oldSize == newSize)
        return;

390
    notifyChanged();
391
    d->shapeChanged(SizeChanged);
Thomas Zander's avatar
Thomas Zander committed
392 393
}

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

    notifyChanged();
405
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
406 407
}

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

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

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

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

432
    return bb.contains(point);
Thomas Zander's avatar
Thomas Zander committed
433 434
}

435
QRectF KoShape::boundingRect() const
Thomas Zander's avatar
Thomas Zander committed
436
{
437
    Q_D(const KoShape);
Jan Hambrecht's avatar
Jan Hambrecht committed
438

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

457
    return bb;
Thomas Zander's avatar
Thomas Zander committed
458 459
}

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

469 470 471 472 473 474 475 476 477 478 479 480 481 482
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;
}

483
QTransform KoShape::absoluteTransformation(const KoViewConverter *converter) const
484
{
485
    Q_D(const KoShape);
486
    QTransform matrix;
Thomas Zander's avatar
Thomas Zander committed
487
    // apply parents matrix to inherit any transformations done there.
488
    KoShapeContainer * container = d->parent;
489
    if (container) {
490
        if (container->inheritsTransform(this)) {
491 492 493 494
            // We do need to pass the converter here, otherwise the parent's
            // translation is not inherited.
            matrix = container->absoluteTransformation(converter);
        } else {
495 496 497 498
            QSizeF containerSize = container->size();
            QPointF containerPos = container->absolutePosition() - QPointF(0.5 * containerSize.width(), 0.5 * containerSize.height());
            if (converter)
                containerPos = converter->documentToView(containerPos);
499
            matrix.translate(containerPos.x(), containerPos.y());
Thomas Zander's avatar
Thomas Zander committed
500 501 502
        }
    }

503
    if (converter) {
504 505 506
        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
507
    }
508 509

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
510 511
}

512
void KoShape::applyAbsoluteTransformation(const QTransform &matrix)
513
{
514
    QTransform globalMatrix = absoluteTransformation(0);
515 516
    // 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
517
    // to be relative to the local coordinate system
518
    QTransform transformMatrix = globalMatrix * matrix * globalMatrix.inverted();
519
    applyTransformation(transformMatrix);
520 521
}

522
void KoShape::applyTransformation(const QTransform &matrix)
523
{
524
    Q_D(KoShape);
525
    d->localMatrix = matrix * d->localMatrix;
526
    notifyChanged();
527
    d->shapeChanged(GenericMatrixChange);
528 529
}

530
void KoShape::setTransformation(const QTransform &matrix)
531
{
532
    Q_D(KoShape);
533 534
    d->localMatrix = matrix;
    notifyChanged();
535
    d->shapeChanged(GenericMatrixChange);
536
}
Thomas Zander's avatar
Thomas Zander committed
537

538
QTransform KoShape::transformation() const
539
{
540
    Q_D(const KoShape);
541 542 543
    return d->localMatrix;
}

544
KoShape::ChildZOrderPolicy KoShape::childZOrderPolicy()
545
{
546
    return ChildZDefault;
547 548
}

549 550
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
{
551 552 553 554 555 556 557 558 559 560 561 562 563
    /**
     * 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.
     */

564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602
    // 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
603
    bool foundCommonParent = false;
604 605 606 607
    int index1 = s1->zIndex();
    int index2 = s2->zIndex();
    parentShapeS1 = s1;
    parentShapeS2 = s2;
608 609 610 611 612 613 614 615
    while (parentShapeS1 && !foundCommonParent) {
        parentShapeS2 = s2;
        index2 = parentShapeS2->zIndex();
        while (parentShapeS2) {
            if (parentShapeS2 == parentShapeS1) {
                foundCommonParent = true;
                break;
            }
616
            if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) {
617 618
                index2 = parentShapeS2->zIndex();
            }
619
            parentShapeS2 = parentShapeS2->parent();
620
        }
621 622

        if (!foundCommonParent) {
623
            if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) {
624 625
                index1 = parentShapeS1->zIndex();
            }
626
            parentShapeS1 = parentShapeS1->parent();
627 628
        }
    }
629

630 631 632 633 634 635 636 637
    // If the one shape is a parent/child of the other then sort so.
    if (s1 == parentShapeS2) {
        return true;
    }
    if (s2 == parentShapeS1) {
        return false;
    }

638
    // If we went that far then the z-Index is used for sorting.
639
    return index1 < index2;
Thomas Zander's avatar
Thomas Zander committed
640 641
}

642 643
void KoShape::setParent(KoShapeContainer *parent)
{
644
    Q_D(KoShape);
645 646

    if (d->parent == parent) {
647
        return;
648 649
    }

650 651
    KoShapeContainer *oldParent = d->parent;
    d->parent = 0; // avoids recursive removing
652 653 654 655 656 657 658

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

    KIS_SAFE_ASSERT_RECOVER_NOOP(parent != this);

Thomas Zander's avatar
Thomas Zander committed
659
    if (parent && parent != this) {
Thomas Zander's avatar
Thomas Zander committed
660
        d->parent = parent;
661
        parent->shapeInterface()->addShape(this);
662
    }
663

664
    notifyChanged();
665
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
666 667
}

668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689
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;
}

690 691
int KoShape::zIndex() const
{
692
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
693
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
694 695
}

696 697
void KoShape::update() const
{
698
    Q_D(const KoShape);
699

700 701
    if (!d->shapeManagers.empty()) {
        QRectF rect(boundingRect());
702
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
Jan Hambrecht's avatar
Jan Hambrecht committed
703
            manager->update(rect, this, true);
704
        }
Thomas Zander's avatar
Thomas Zander committed
705 706 707
    }
}

708
void KoShape::update(const QRectF &rect) const
709
{
710 711 712 713 714

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

715
    Q_D(const KoShape);
716

717
    if (!d->shapeManagers.empty() && isVisible()) {
718
        QRectF rc(absoluteTransformation(0).mapRect(rect));
719
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
720
            manager->update(rc);
721 722
        }
    }
Thomas Zander's avatar
Thomas Zander committed
723 724
}

725
QPainterPath KoShape::outline() const
726
{
Thomas Zander's avatar
Thomas Zander committed
727
    QPainterPath path;
728
    path.addRect(outlineRect());
Thomas Zander's avatar
Thomas Zander committed
729 730 731
    return path;
}

732 733
QRectF KoShape::outlineRect() const
{
734
    const QSizeF s = size();
735 736 737 738 739 740 741
    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);
742
    if (d->fill) {
743
        return outline();
744
    }
745 746

    return QPainterPath();
747 748
}

749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775
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);
}

776 777
void KoShape::copySettings(const KoShape *shape)
{
778
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
779 780
    d->size = shape->size();
    d->connectors.clear();
781
    Q_FOREACH (const KoConnectionPoint &point, shape->connectionPoints())
Jan Hambrecht's avatar
Jan Hambrecht committed
782
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
783 784
    d->zIndex = shape->zIndex();
    d->visible = shape->isVisible();
785

786 787 788 789 790
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
791

792
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
793 794
    d->protectContent = shape->isContentProtected();
    d->selectable = shape->isSelectable();
Thomas Zander's avatar
Thomas Zander committed
795
    d->keepAspect = shape->keepAspectRatio();
796
    d->localMatrix = shape->d_ptr->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
797 798
}

Thomas Zander's avatar
Thomas Zander committed
799
void KoShape::notifyChanged()
800
{
801
    Q_D(KoShape);
802
    Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
803
        manager->notifyShapeChanged(this);
804 805 806
    }
}

807 808
void KoShape::setUserData(KoShapeUserData *userData)
{
809
    Q_D(KoShape);
810
    d->userData.reset(userData);
811 812
}

813 814
KoShapeUserData *KoShape::userData() const
{
815
    Q_D(const KoShape);
816
    return d->userData.data();
817 818
}

Thomas Zander's avatar
Thomas Zander committed
819
bool KoShape::hasTransparency() const
820
{
Thomas Zander's avatar
Thomas Zander committed
821
    Q_D(const KoShape);
822
    if (! d->fill)
Thomas Zander's avatar
Thomas Zander committed
823
        return true;
824
    else
825 826 827 828 829 830
        return d->fill->hasTransparency() || d->transparency > 0.0;
}

void KoShape::setTransparency(qreal transparency)
{
    Q_D(KoShape);
831
    d->transparency = qBound<qreal>(0.0, transparency, 1.0);
832 833 834

    d->shapeChanged(TransparencyChanged);
    notifyChanged();
835 836 837 838 839 840 841 842 843 844 845 846
}

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
847 848
}

849
KoInsets KoShape::strokeInsets() const
850
{
851
    Q_D(const KoShape);
852
    KoInsets answer;
853 854
    if (d->stroke)
        d->stroke->strokeInsets(this, answer);
855 856 857
    return answer;
}

858 859
qreal KoShape::rotation() const
{
860
    Q_D(const KoShape);
861
    // try to extract the rotation angle out of the local matrix
862 863 864
    // if it is a pure rotation matrix

    // check if the matrix has shearing mixed in
865
    if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
866
        return std::numeric_limits<qreal>::quiet_NaN();
867
    // check if the matrix has scaling mixed in
868
    if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
869
        return std::numeric_limits<qreal>::quiet_NaN();
870 871

    // calculate the angle from the matrix elements
872 873
    qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
    if (angle < 0.0)
874 875 876
        angle += 360.0;

    return angle;
Thomas Zander's avatar
Thomas Zander committed
877 878
}

879
QSizeF KoShape::size() const
880
{
881
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
882 883 884
    return d->size;
}

885 886
QPointF KoShape::position() const
{
887
    Q_D(const KoShape);
888
    QPointF center = outlineRect().center();
889
    return d->localMatrix.map(center) - center;
Thomas Zander's avatar
Thomas Zander committed
890 891
}

892
int KoShape::addConnectionPoint(const KoConnectionPoint &point)
893
{
894
    Q_D(KoShape);
895

896
    // get next glue point id
897
    int nextConnectionPointId = KoConnectionPoint::FirstCustomConnectionPoint;
Jan Hambrecht's avatar
Jan Hambrecht committed
898
    if (d->connectors.size())
899 900
        nextConnectionPointId = qMax(nextConnectionPointId, (--d->connectors.end()).key()+1);

901
    KoConnectionPoint p = point;
902 903
    d->convertFromShapeCoordinates(p, size());
    d->connectors[nextConnectionPointId] = p;
904 905 906 907

    return nextConnectionPointId;
}

908
bool KoShape::setConnectionPoint(int connectionPointId, const KoConnectionPoint &point)
909 910
{
    Q_D(KoShape);
911
    if (connectionPointId < 0)
912 913
        return false;

914 915
    const bool insertPoint = !hasConnectionPoint(connectionPointId);

916
    switch(connectionPointId) {
917 918 919 920 921 922
        case KoConnectionPoint::TopConnectionPoint:
        case KoConnectionPoint::RightConnectionPoint:
        case KoConnectionPoint::BottomConnectionPoint:
        case KoConnectionPoint::LeftConnectionPoint:
        {
            KoConnectionPoint::PointId id = static_cast<KoConnectionPoint::PointId>(connectionPointId);
923
            d->connectors[id] = KoConnectionPoint::defaultConnectionPoint(id);
924
            break;
925
        }
926
        default:
927
        {
928
            KoConnectionPoint p = point;
929 930
            d->convertFromShapeCoordinates(p, size());
            d->connectors[connectionPointId] = p;
931
            break;
932
        }
933 934
    }

935 936
    if(!insertPoint)
        d->shapeChanged(ConnectionPointChanged);
937 938 939 940

    return true;
}

941 942 943 944 945 946
bool KoShape::hasConnectionPoint(int connectionPointId) const
{
    Q_D(const KoShape);
    return d->connectors.contains(connectionPointId);
}

947
KoConnectionPoint KoShape::connectionPoint(int connectionPointId) const
948 949
{
    Q_D(const KoShape);
950
    KoConnectionPoint p = d->connectors.value(connectionPointId, KoConnectionPoint());
951
    // convert glue point to shape coordinates
952
    d->convertToShapeCoordinates(p, size());
953
    return p;
Thomas Zander's avatar
Thomas Zander committed
954 955
}

956
KoConnectionPoints KoShape::connectionPoints() const
957
{
958
    Q_D(const KoShape);
959
    QSizeF s = size();
960 961 962
    KoConnectionPoints points =