KoShape.cpp 74.2 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>
Boudewijn Rempt's avatar
Boudewijn Rempt committed
6
   CopyRight (C) 2010 Boudewijn Rempt <boud@valdyas.org>
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 25
#include <limits>

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

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

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

75 76
#include "kis_assert.h"

77
#include "KoOdfGradientBackground.h"
78
#include <KisHandlePainterHelper.h>
79

80
// KoShape::Private
81

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

114 115
KoShape::Private::Private(const Private &rhs)
    : QSharedData()
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
    , 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)
    , inheritBackground(rhs.inheritBackground)
    , inheritStroke(rhs.inheritStroke)
    , 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 159
{
}

160
KoShape::Private::~Private()
161
{
162
    if (shadow && !shadow->deref())
163
        delete shadow;
164
    if (filterEffectStack && !filterEffectStack->deref())
165
        delete filterEffectStack;
166
}
Thomas Zander's avatar
Thomas Zander committed
167

168
void KoShape::shapeChangedPriv(KoShape::ChangeType type)
169
{
170 171
    if (d->parent)
        d->parent->model()->childChanged(this, type);
172

173
    this->shapeChanged(type);
174

175 176
    Q_FOREACH (KoShape * shape, d->dependees) {
        shape->shapeChanged(type, this);
177 178
    }

179 180
    Q_FOREACH (KoShape::ShapeChangeListener *listener, d->listeners) {
        listener->notifyShapeChangedImpl(type, this);
181
    }
182
}
183

184
void KoShape::addShapeManager(KoShapeManager *manager)
185
{
186
    d->shapeManagers.insert(manager);
187 188
}

189
void KoShape::removeShapeManager(KoShapeManager *manager)
190
{
191
    d->shapeManagers.remove(manager);
192 193
}

194
void KoShape::Private::convertFromShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
195
{
Jan Hambrecht's avatar
Jan Hambrecht committed
196
    switch(point.alignment) {
197 198 199 200 201 202 203 204 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
    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();
        break;
    case KoConnectionPoint::AlignLeft:
        point.position.ry() = 0.5*shapeSize.height();
        break;
    case KoConnectionPoint::AlignBottom:
        point.position.ry() -= shapeSize.height();
        break;
    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;
231 232 233
    }
}

234
void KoShape::Private::convertToShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
235
{
Jan Hambrecht's avatar
Jan Hambrecht committed
236
    switch(point.alignment) {
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 264 265 266 267 268
    case KoConnectionPoint::AlignNone:
        point.position = KoFlake::toAbsolute(point.position, shapeSize);
        break;
    case KoConnectionPoint::AlignRight:
        point.position.rx() += shapeSize.width();
        break;
    case KoConnectionPoint::AlignLeft:
        point.position.ry() = 0.5*shapeSize.height();
        break;
    case KoConnectionPoint::AlignBottom:
        point.position.ry() += shapeSize.height();
        break;
    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;
269
    }
270 271
}

272
// static
273
QString KoShape::Private::getStyleProperty(const char *property, KoShapeLoadingContext &context)
274 275 276 277 278 279 280 281 282 283 284
{
    KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
    QString value;

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

    return value;
}

285 286


287
// ======== KoShape
288 289 290 291

const qint16 KoShape::maxZIndex = std::numeric_limits<qint16>::max();
const qint16 KoShape::minZIndex = std::numeric_limits<qint16>::min();

Thomas Zander's avatar
Thomas Zander committed
292
KoShape::KoShape()
293
    : d(new Private)
Thomas Zander's avatar
Thomas Zander committed
294
{
295
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
296 297
}

298 299
KoShape::KoShape(const KoShape &rhs)
    : d(rhs.d)
300 301 302
{
}

Thomas Zander's avatar
Thomas Zander committed
303 304
KoShape::~KoShape()
{
305
    shapeChangedPriv(Deleted);
306
    d->listeners.clear();
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
    /**
     * The shape must have already been detached from all the parents and
     * shape managers. Otherwise we migh accidentally request some RTTI
     * information, which is not available anymore (we are in d-tor).
     *
     * TL;DR: fix the code that caused this destruction without unparenting
     *        instead of trying to remove these assert!
     */
    KIS_SAFE_ASSERT_RECOVER (!d->parent) {
        d->parent->removeShape(this);
    }

    KIS_SAFE_ASSERT_RECOVER (d->shapeManagers.isEmpty()) {
        Q_FOREACH (KoShapeManager *manager, d->shapeManagers) {
            manager->shapeInterface()->notifyShapeDestructed(this);
        }
        d->shapeManagers.clear();
    }
Thomas Zander's avatar
Thomas Zander committed
325 326
}

327 328
KoShape *KoShape::cloneShape() const
{
329
    KIS_SAFE_ASSERT_RECOVER_NOOP(0 && "not implemented!");
330
    qWarning() << shapeId() << "cannot be cloned";
331 332 333
    return 0;
}

334 335 336 337 338 339 340 341 342
void KoShape::paintStroke(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintcontext)
{
    Q_UNUSED(paintcontext);

    if (stroke()) {
        stroke()->paint(this, painter, converter);
    }
}

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

352
    notifyChanged();
353
    shapeChangedPriv(ScaleChanged);
Thomas Zander's avatar
Thomas Zander committed
354 355
}

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

365
    notifyChanged();
366
    shapeChangedPriv(RotationChanged);
Thomas Zander's avatar
Thomas Zander committed
367 368
}

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

378
    notifyChanged();
379
    shapeChangedPriv(ShearChanged);
Thomas Zander's avatar
Thomas Zander committed
380 381
}

382
void KoShape::setSize(const QSizeF &newSize)
Thomas Zander's avatar
Thomas Zander committed
383
{
384
    QSizeF oldSize(size());
385

386
    // always set size, as d->size and size() may vary
387
    setSizeImpl(newSize);
388

389 390 391
    if (oldSize == newSize)
        return;

392
    notifyChanged();
393 394 395 396 397 398
    shapeChangedPriv(SizeChanged);
}

void KoShape::setSizeImpl(const QSizeF &size) const
{
    d->size = size;
Thomas Zander's avatar
Thomas Zander committed
399 400
}

401
void KoShape::setPosition(const QPointF &newPosition)
Thomas Zander's avatar
Thomas Zander committed
402
{
403
    QPointF currentPos = position();
404
    if (newPosition == currentPos)
Thomas Zander's avatar
Thomas Zander committed
405
        return;
406
    QTransform translateMatrix;
407
    translateMatrix.translate(newPosition.x() - currentPos.x(), newPosition.y() - currentPos.y());
408 409 410
    d->localMatrix = d->localMatrix * translateMatrix;

    notifyChanged();
411
    shapeChangedPriv(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
412 413
}

414
bool KoShape::hitTest(const QPointF &position) const
Thomas Zander's avatar
Thomas Zander committed
415
{
416
    if (d->parent && d->parent->isClipped(this) && !d->parent->hitTest(position))
Thomas Zander's avatar
Thomas Zander committed
417 418
        return false;

419
    QPointF point = absoluteTransformation(0).inverted().map(position);
420 421
    QRectF bb = outlineRect();

422
    if (d->stroke) {
423
        KoInsets insets;
424
        d->stroke->strokeInsets(this, insets);
425
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
426
    }
427 428 429
    if (bb.contains(point))
        return true;

430
    // if there is no shadow we can as well just leave
431
    if (! d->shadow)
432
        return false;
Thomas Zander's avatar
Thomas Zander committed
433

434 435
    // the shadow has an offset to the shape, so we simply
    // check if the position minus the shadow offset hits the shape
436
    point = absoluteTransformation(0).inverted().map(position - d->shadow->offset());
437

438
    return bb.contains(point);
Thomas Zander's avatar
Thomas Zander committed
439 440
}

441
QRectF KoShape::boundingRect() const
Thomas Zander's avatar
Thomas Zander committed
442
{
Jan Hambrecht's avatar
Jan Hambrecht committed
443

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

462
    return bb;
Thomas Zander's avatar
Thomas Zander committed
463 464
}

465 466 467 468 469 470 471 472 473
QRectF KoShape::boundingRect(const QList<KoShape *> &shapes)
{
    QRectF boundingRect;
    Q_FOREACH (KoShape *shape, shapes) {
        boundingRect |= shape->boundingRect();
    }
    return boundingRect;
}

474 475 476 477 478 479 480 481 482 483 484 485 486 487
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;
}

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

507
    if (converter) {
508 509 510
        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
511
    }
512 513

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
514 515
}

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

526
void KoShape::applyTransformation(const QTransform &matrix)
527 528
{
    d->localMatrix = matrix * d->localMatrix;
529
    notifyChanged();
530
    shapeChangedPriv(GenericMatrixChange);
531 532
}

533
void KoShape::setTransformation(const QTransform &matrix)
534 535 536
{
    d->localMatrix = matrix;
    notifyChanged();
537
    shapeChangedPriv(GenericMatrixChange);
538
}
Thomas Zander's avatar
Thomas Zander committed
539

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

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

550 551
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
{
552 553 554 555 556 557
    /**
     * 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
Boudewijn Rempt's avatar
Boudewijn Rempt committed
558
     *          coincide, the resulting painting order in Krita is
559 560 561 562 563 564
     *          **UNDEFINED**.
     *
     *          To avoid this trouble we use  KoShapeReorderCommand::mergeInShape()
     *          inside KoShapeCreateCommand.
     */

565
    /**
Boudewijn Rempt's avatar
Boudewijn Rempt committed
566 567
     * The algorithm below doesn't correctly handle the case when the two pointers actually
     * point to the same shape. So just check it in advance to guarantee strict weak ordering
568 569 570 571 572
     * relation requirement
     */
    if (s1 == s2) return false;


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 603 604 605 606 607 608 609 610 611
    // 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
612
    bool foundCommonParent = false;
613 614 615 616
    int index1 = s1->zIndex();
    int index2 = s2->zIndex();
    parentShapeS1 = s1;
    parentShapeS2 = s2;
617 618 619 620 621 622 623 624
    while (parentShapeS1 && !foundCommonParent) {
        parentShapeS2 = s2;
        index2 = parentShapeS2->zIndex();
        while (parentShapeS2) {
            if (parentShapeS2 == parentShapeS1) {
                foundCommonParent = true;
                break;
            }
625
            if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) {
626 627
                index2 = parentShapeS2->zIndex();
            }
628
            parentShapeS2 = parentShapeS2->parent();
629
        }
630 631

        if (!foundCommonParent) {
632
            if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) {
633 634
                index1 = parentShapeS1->zIndex();
            }
635
            parentShapeS1 = parentShapeS1->parent();
636 637
        }
    }
638

639 640 641 642 643 644 645 646
    // If the one shape is a parent/child of the other then sort so.
    if (s1 == parentShapeS2) {
        return true;
    }
    if (s2 == parentShapeS1) {
        return false;
    }

647
    // If we went that far then the z-Index is used for sorting.
648
    return index1 < index2;
Thomas Zander's avatar
Thomas Zander committed
649 650
}

651 652
void KoShape::setParent(KoShapeContainer *parent)
{
653 654

    if (d->parent == parent) {
655
        return;
656 657
    }

658 659
    KoShapeContainer *oldParent = d->parent;
    d->parent = 0; // avoids recursive removing
660 661 662 663 664 665 666

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

    KIS_SAFE_ASSERT_RECOVER_NOOP(parent != this);

Thomas Zander's avatar
Thomas Zander committed
667
    if (parent && parent != this) {
Thomas Zander's avatar
Thomas Zander committed
668
        d->parent = parent;
669
        parent->shapeInterface()->addShape(this);
670
    }
671

672
    notifyChanged();
673
    shapeChangedPriv(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
674 675
}

676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697
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;
}

698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
bool KoShape::hasCommonParent(const KoShape *shape) const
{
    const KoShape *thisShape = this;
    while (thisShape) {

        const KoShape *otherShape = shape;
        while (otherShape) {
            if (thisShape == otherShape) {
                return true;
            }
            otherShape = otherShape->parent();
        }

        thisShape = thisShape->parent();
    }

    return false;
}

717
qint16 KoShape::zIndex() const
718
{
Thomas Zander's avatar
Thomas Zander committed
719
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
720 721
}

722 723
void KoShape::update() const
{
724

725 726
    if (!d->shapeManagers.empty()) {
        QRectF rect(boundingRect());
727
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
Jan Hambrecht's avatar
Jan Hambrecht committed
728
            manager->update(rect, this, true);
729
        }
Thomas Zander's avatar
Thomas Zander committed
730 731 732
    }
}

733
void KoShape::updateAbsolute(const QRectF &rect) const
734
{
735 736 737 738 739
    if (rect.isEmpty() && !rect.isNull()) {
        return;
    }


740
    if (!d->shapeManagers.empty() && isVisible()) {
741
        Q_FOREACH (KoShapeManager *manager, d->shapeManagers) {
742
            manager->update(rect);
743 744
        }
    }
Thomas Zander's avatar
Thomas Zander committed
745 746
}

747
QPainterPath KoShape::outline() const
748
{
Thomas Zander's avatar
Thomas Zander committed
749
    QPainterPath path;
750
    path.addRect(outlineRect());
Thomas Zander's avatar
Thomas Zander committed
751 752 753
    return path;
}

754 755
QRectF KoShape::outlineRect() const
{
756
    const QSizeF s = size();
757 758 759 760 761 762
    return QRectF(QPointF(0, 0), QSizeF(qMax(s.width(),  qreal(0.0001)),
                                        qMax(s.height(), qreal(0.0001))));
}

QPainterPath KoShape::shadowOutline() const
{
763
    if (background()) {
764
        return outline();
765
    }
766 767

    return QPainterPath();
768 769
}

770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792
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)
{
    QPointF currentAbsPosition = absolutePosition(anchor);
    QPointF translate = newPosition - currentAbsPosition;
    QTransform translateMatrix;
    translateMatrix.translate(translate.x(), translate.y());
    applyAbsoluteTransformation(translateMatrix);
    notifyChanged();
793
    shapeChangedPriv(PositionChanged);
794 795
}

796 797
void KoShape::copySettings(const KoShape *shape)
{
Thomas Zander's avatar
Thomas Zander committed
798 799
    d->size = shape->size();
    d->connectors.clear();
800
    Q_FOREACH (const KoConnectionPoint &point, shape->connectionPoints())
Jan Hambrecht's avatar
Jan Hambrecht committed
801
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
802
    d->zIndex = shape->zIndex();
803
    d->visible = shape->isVisible(false);
804

805 806 807 808 809
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
810

811
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
812 813
    d->protectContent = shape->isContentProtected();
    d->selectable = shape->isSelectable();
Thomas Zander's avatar
Thomas Zander committed
814
    d->keepAspect = shape->keepAspectRatio();
815
    d->localMatrix = shape->d->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
816 817
}

Thomas Zander's avatar
Thomas Zander committed
818
void KoShape::notifyChanged()
819
{
820
    Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
821
        manager->notifyShapeChanged(this);
822 823 824
    }
}

825 826
void KoShape::setUserData(KoShapeUserData *userData)
{
827
    d->userData.reset(userData);
828 829
}

830 831
KoShapeUserData *KoShape::userData() const
{
832
    return d->userData.data();
833 834
}

Thomas Zander's avatar
Thomas Zander committed
835
bool KoShape::hasTransparency() const
836
{
837 838 839
    QSharedPointer<KoShapeBackground> bg = background();

    return !bg || bg->hasTransparency() || d->transparency > 0.0;
840 841 842 843
}

void KoShape::setTransparency(qreal transparency)
{
844
    d->transparency = qBound<qreal>(0.0, transparency, 1.0);
845

846
    shapeChangedPriv(TransparencyChanged);
847
    notifyChanged();
848 849 850 851 852 853 854 855 856 857 858
}

qreal KoShape::transparency(bool recursive) const
{
    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
859 860
}

861
KoInsets KoShape::strokeInsets() const
862
{
863
    KoInsets answer;
864 865
    if (d->stroke)
        d->stroke->strokeInsets(this, answer);
866 867 868
    return answer;
}

869 870
qreal KoShape::rotation() const
{
871
    // try to extract the rotation angle out of the local matrix
872 873 874
    // if it is a pure rotation matrix

    // check if the matrix has shearing mixed in
875
    if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
876
        return std::numeric_limits<qreal>::quiet_NaN();
877
    // check if the matrix has scaling mixed in
878
    if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
879
        return std::numeric_limits<qreal>::quiet_NaN();
880 881

    // calculate the angle from the matrix elements
882 883
    qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
    if (angle < 0.0)
884 885 886
        angle += 360.0;

    return angle;
Thomas Zander's avatar
Thomas Zander committed
887 888
}

889
QSizeF KoShape::size() const
890
{
Thomas Zander's avatar
Thomas Zander committed
891 892 893
    return d->size;
}

894 895
QPointF KoShape::position() const
{
896
    QPointF center = outlineRect().center();
897
    return d->localMatrix.map(center) - center;
Thomas Zander's avatar
Thomas Zander committed
898 899
}

900
int KoShape::addConnectionPoint(const KoConnectionPoint &point)
901
{
902

903
    // get next glue point id
904
    int nextConnectionPointId = KoConnectionPoint::FirstCustomConnectionPoint;
Jan Hambrecht's avatar
Jan Hambrecht committed
905
    if (d->connectors.size())
906 907
        nextConnectionPointId = qMax(nextConnectionPointId, (--d->connectors.end()).key()+1);

908
    KoConnectionPoint p = point;
909 910
    d->convertFromShapeCoordinates(p, size());
    d->connectors[nextConnectionPointId] = p;
911 912 913 914

    return nextConnectionPointId;
}

915
bool KoShape::setConnectionPoint(int connectionPointId, const KoConnectionPoint &point)
916
{
917
    if (connectionPointId < 0)
918 919
        return false;

920 921
    const bool insertPoint = !hasConnectionPoint(connectionPointId);

922
    switch(connectionPointId) {
923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938
    case KoConnectionPoint::TopConnectionPoint:
    case KoConnectionPoint::RightConnectionPoint:
    case KoConnectionPoint::BottomConnectionPoint:
    case KoConnectionPoint::LeftConnectionPoint:
    {
        KoConnectionPoint::PointId id = static_cast<KoConnectionPoint::PointId>(connectionPointId);
        d->connectors[id] = KoConnectionPoint::defaultConnectionPoint(id);
        break;
    }
    default:
    {
        KoConnectionPoint p = point;
        d->convertFromShapeCoordinates(p, size());
        d->connectors[connectionPointId] = p;
        break;
    }
939 940
    }

941
    if(!insertPoint)
942
        shapeChangedPriv(ConnectionPointChanged);
943 944 945 946

    return true;
}

947 948 949 950 951
bool KoShape::hasConnectionPoint(int connectionPointId) const
{
    return d->connectors.contains(connectionPointId);
}

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

960
KoConnectionPoints KoShape::connectionPoints() const
961
{
962
    QSizeF s = size();
963 964 965
    KoConnectionPoints points = d->connectors;
    KoConnectionPoints::iterator point = points.begin();
    KoConnectionPoints::iterator lastPoint = points.end();
966
    // convert glue points to shape coordinates
967
    for(; point != lastPoint; ++point) {
968
        d->convertToShapeCoordinates(point.value(), s);
969
    }
970 971

    return points;
972 973
}

974 975 976
void KoShape::removeConnectionPoint(int connectionPointId)
{
    d->connectors.remove(connectionPointId);
977
    shapeChangedPriv(ConnectionPointChanged);
978 979 980 981 982 983 984
}

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

985 986 987 988 989
KoShape::TextRunAroundSide KoShape::textRunAroundSide() const
{
    return d->textRunAroundSide;
}

990
void KoShape::setTextRunAroundSide(TextRunAroundSide side, RunThroughLevel runThrought)
991 992 993 994 995 996
{

    if (side == RunThrough) {
        if (runThrought == Background) {
            setRunThrough(-1);
        } else {
997
            setRunThrough(1);
998 999 1000 1001
        }
    } else {
        setRunThrough(0);
    }
1002 1003 1004 1005 1006 1007 1008

    if ( d->textRunAroundSide == side) {
        return;
    }

    d->textRunAroundSide = side;
    notifyChanged();
1009
    shapeChangedPriv(TextRunAroundChanged);
1010 1011
}

1012
qreal KoShape::textRunAroundDistanceTop() const
1013
{
1014
    return d->textRunAroundDistanceTop;
1015 1016
}

1017
void KoShape::setTextRunAroundDistanceTop(qreal distance)
1018
{
1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049
    d->textRunAroundDistanceTop = distance;
}

qreal KoShape::textRunAroundDistanceLeft() const
{
    return d->textRunAroundDistanceLeft;
}

void KoShape::setTextRunAroundDistanceLeft(qreal distance)
{
    d->textRunAroundDistanceLeft = distance;
}

qreal KoShape::textRunAroundDistanceRight() const
{
    return d->textRunAroundDistanceRight;
}

void KoShape::setTextRunAroundDistanceRight(qreal distance)
{
    d->textRunAroundDistanceRight = distance;
}

qreal KoShape::textRunAroundDistanceBottom() const
{
    return d->textRunAroundDistanceBottom;
}

void KoShape::setTextRunAroundDistanceBottom(qreal distance)
{
    d->textRunAroundDistanceBottom = distance;
1050 1051
}

1052 1053 1054 1055 1056 1057 1058 1059 1060 1061
qreal KoShape::textRunAroundThreshold() const
{
    return d->textRunAroundThreshold;
}

void KoShape::setTextRunAroundThreshold(qreal threshold)
{
    d->textRunAroundThreshold = threshold;
}