KoShape.cpp 77 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 <limits>
78
#include "KoOdfGradientBackground.h"
79
#include <KisHandlePainterHelper.h>
80

81 82
// KoShapePrivate

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

115 116 117 118 119 120 121 122 123 124 125 126 127
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),
128
      inheritBackground(rhs.inheritBackground),
129
      inheritStroke(rhs.inheritStroke),
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 157 158 159 160
      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)
{
}

161 162
KoShapePrivate::~KoShapePrivate()
{
163
    Q_Q(KoShape);
164 165 166 167 168 169 170 171 172 173

    /**
     * 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 (!parent) {
174
        parent->removeShape(q);
175 176
    }

177 178 179 180 181
    KIS_SAFE_ASSERT_RECOVER (shapeManagers.isEmpty()) {
        Q_FOREACH (KoShapeManager *manager, shapeManagers) {
            manager->shapeInterface()->notifyShapeDestructed(q);
        }
        shapeManagers.clear();
Thomas Zander's avatar
Thomas Zander committed
182
    }
183

184
    if (shadow && !shadow->deref())
185
        delete shadow;
186
    if (filterEffectStack && !filterEffectStack->deref())
187
        delete filterEffectStack;
188
}
Thomas Zander's avatar
Thomas Zander committed
189

190 191
void KoShapePrivate::shapeChanged(KoShape::ChangeType type)
{
192
    Q_Q(KoShape);
193
    if (parent)
194
        parent->model()->childChanged(q, type);
195

196
    q->shapeChanged(type);
197 198

    Q_FOREACH (KoShape * shape, dependees) {
199
        shape->shapeChanged(type, q);
200 201 202 203 204
    }

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

207 208 209 210 211 212 213 214
void KoShapePrivate::addShapeManager(KoShapeManager *manager)
{
    shapeManagers.insert(manager);
}

void KoShapePrivate::removeShapeManager(KoShapeManager *manager)
{
    shapeManagers.remove(manager);
215 216
}

217
void KoShapePrivate::convertFromShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
218
{
Jan Hambrecht's avatar
Jan Hambrecht committed
219
    switch(point.alignment) {
220 221 222 223 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
        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
257
    switch(point.alignment) {
258 259 260 261 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
        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;
    }
289 290
}

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

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

    return value;
}

304 305


306
// ======== KoShape
307 308 309 310

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
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
    d->listeners.clear();
327
    delete d_ptr;
Thomas Zander's avatar
Thomas Zander committed
328 329
}

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

337 338 339 340 341 342 343 344 345
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
346
void KoShape::scale(qreal sx, qreal sy)
Thomas Zander's avatar
Thomas Zander committed
347
{
348
    Q_D(KoShape);
349
    QPointF pos = position();
350
    QTransform scaleMatrix;
351 352 353
    scaleMatrix.translate(pos.x(), pos.y());
    scaleMatrix.scale(sx, sy);
    scaleMatrix.translate(-pos.x(), -pos.y());
354
    d->localMatrix = d->localMatrix * scaleMatrix;
355

356
    notifyChanged();
357
    d->shapeChanged(ScaleChanged);
Thomas Zander's avatar
Thomas Zander committed
358 359
}

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

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

Thomas Zander's avatar
Thomas Zander committed
374
void KoShape::shear(qreal sx, qreal sy)
Thomas Zander's avatar
Thomas Zander committed
375
{
376
    Q_D(KoShape);
377
    QPointF pos = position();
378
    QTransform shearMatrix;
379 380 381
    shearMatrix.translate(pos.x(), pos.y());
    shearMatrix.shear(sx, sy);
    shearMatrix.translate(-pos.x(), -pos.y());
382
    d->localMatrix = d->localMatrix * shearMatrix;
383

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

388
void KoShape::setSize(const QSizeF &newSize)
Thomas Zander's avatar
Thomas Zander committed
389
{
390
    Q_D(KoShape);
391
    QSizeF oldSize(size());
392

393
    // always set size, as d->size and size() may vary
Thomas Zander's avatar
Thomas Zander committed
394
    d->size = newSize;
395

396 397 398
    if (oldSize == newSize)
        return;

399
    notifyChanged();
400
    d->shapeChanged(SizeChanged);
Thomas Zander's avatar
Thomas Zander committed
401 402
}

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

    notifyChanged();
414
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
415 416
}

417
bool KoShape::hitTest(const QPointF &position) const
Thomas Zander's avatar
Thomas Zander committed
418
{
419
    Q_D(const KoShape);
420
    if (d->parent && d->parent->isClipped(this) && !d->parent->hitTest(position))
Thomas Zander's avatar
Thomas Zander committed
421 422
        return false;

423
    QPointF point = absoluteTransformation(0).inverted().map(position);
424 425
    QRectF bb = outlineRect();

426
    if (d->stroke) {
427
        KoInsets insets;
428
        d->stroke->strokeInsets(this, insets);
429
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
430
    }
431 432 433
    if (bb.contains(point))
        return true;

434
    // if there is no shadow we can as well just leave
435
    if (! d->shadow)
436
        return false;
Thomas Zander's avatar
Thomas Zander committed
437

438 439
    // the shadow has an offset to the shape, so we simply
    // check if the position minus the shadow offset hits the shape
440
    point = absoluteTransformation(0).inverted().map(position - d->shadow->offset());
441

442
    return bb.contains(point);
Thomas Zander's avatar
Thomas Zander committed
443 444
}

445
QRectF KoShape::boundingRect() const
Thomas Zander's avatar
Thomas Zander committed
446
{
447
    Q_D(const KoShape);
Jan Hambrecht's avatar
Jan Hambrecht committed
448

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

467
    return bb;
Thomas Zander's avatar
Thomas Zander committed
468 469
}

470 471 472 473 474 475 476 477 478
QRectF KoShape::boundingRect(const QList<KoShape *> &shapes)
{
    QRectF boundingRect;
    Q_FOREACH (KoShape *shape, shapes) {
        boundingRect |= shape->boundingRect();
    }
    return boundingRect;
}

479 480 481 482 483 484 485 486 487 488 489 490 491 492
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;
}

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

513
    if (converter) {
514 515 516
        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
517
    }
518 519

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
520 521
}

522
void KoShape::applyAbsoluteTransformation(const QTransform &matrix)
523
{
524
    QTransform globalMatrix = absoluteTransformation(0);
525 526
    // 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
527
    // to be relative to the local coordinate system
528
    QTransform transformMatrix = globalMatrix * matrix * globalMatrix.inverted();
529
    applyTransformation(transformMatrix);
530 531
}

532
void KoShape::applyTransformation(const QTransform &matrix)
533
{
534
    Q_D(KoShape);
535
    d->localMatrix = matrix * d->localMatrix;
536
    notifyChanged();
537
    d->shapeChanged(GenericMatrixChange);
538 539
}

540
void KoShape::setTransformation(const QTransform &matrix)
541
{
542
    Q_D(KoShape);
543 544
    d->localMatrix = matrix;
    notifyChanged();
545
    d->shapeChanged(GenericMatrixChange);
546
}
Thomas Zander's avatar
Thomas Zander committed
547

548
QTransform KoShape::transformation() const
549
{
550
    Q_D(const KoShape);
551 552 553
    return d->localMatrix;
}

554
KoShape::ChildZOrderPolicy KoShape::childZOrderPolicy()
555
{
556
    return ChildZDefault;
557 558
}

559 560
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
{
561 562 563 564 565 566 567 568 569 570 571 572 573
    /**
     * 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.
     */

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

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

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

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

652 653
void KoShape::setParent(KoShapeContainer *parent)
{
654
    Q_D(KoShape);
655 656

    if (d->parent == parent) {
657
        return;
658 659
    }

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

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

    KIS_SAFE_ASSERT_RECOVER_NOOP(parent != this);

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

674
    notifyChanged();
675
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
676 677
}

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

700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718
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;
}

719
qint16 KoShape::zIndex() const
720
{
721
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
722
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
723 724
}

725 726
void KoShape::update() const
{
727
    Q_D(const KoShape);
728

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

737
void KoShape::updateAbsolute(const QRectF &rect) const
738
{
739 740 741 742 743

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

744
    Q_D(const KoShape);
745

746
    if (!d->shapeManagers.empty() && isVisible()) {
747
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
748
            manager->update(rect);
749 750
        }
    }
Thomas Zander's avatar
Thomas Zander committed
751 752
}

753
QPainterPath KoShape::outline() const
754
{
Thomas Zander's avatar
Thomas Zander committed
755
    QPainterPath path;
756
    path.addRect(outlineRect());
Thomas Zander's avatar
Thomas Zander committed
757 758 759
    return path;
}

760 761
QRectF KoShape::outlineRect() const
{
762
    const QSizeF s = size();
763 764 765 766 767 768 769
    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);
770
    if (background()) {
771
        return outline();
772
    }
773 774

    return QPainterPath();
775 776
}

777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803
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);
}

804 805
void KoShape::copySettings(const KoShape *shape)
{
806
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
807 808
    d->size = shape->size();
    d->connectors.clear();
809
    Q_FOREACH (const KoConnectionPoint &point, shape->connectionPoints())
Jan Hambrecht's avatar
Jan Hambrecht committed
810
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
811 812
    d->zIndex = shape->zIndex();
    d->visible = shape->isVisible();
813

814 815 816 817 818
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
819

820
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
821 822
    d->protectContent = shape->isContentProtected();
    d->selectable = shape->isSelectable();
Thomas Zander's avatar
Thomas Zander committed
823
    d->keepAspect = shape->keepAspectRatio();
824
    d->localMatrix = shape->d_ptr->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
825 826
}

Thomas Zander's avatar
Thomas Zander committed
827
void KoShape::notifyChanged()
828
{
829
    Q_D(KoShape);
830
    Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
831
        manager->notifyShapeChanged(this);
832 833 834
    }
}

835 836
void KoShape::setUserData(KoShapeUserData *userData)
{
837
    Q_D(KoShape);
838
    d->userData.reset(userData);
839 840
}

841 842
KoShapeUserData *KoShape::userData() const
{
843
    Q_D(const KoShape);
844
    return d->userData.data();
845 846
}

Thomas Zander's avatar
Thomas Zander committed
847
bool KoShape::hasTransparency() const
848
{
Thomas Zander's avatar
Thomas Zander committed
849
    Q_D(const KoShape);
850 851 852
    QSharedPointer<KoShapeBackground> bg = background();

    return !bg || bg->hasTransparency() || d->transparency > 0.0;
853 854 855 856 857
}

void KoShape::setTransparency(qreal transparency)
{
    Q_D(KoShape);
858
    d->transparency = qBound<qreal>(0.0, transparency, 1.0);
859 860 861

    d->shapeChanged(TransparencyChanged);
    notifyChanged();
862 863 864 865 866 867 868 869 870 871 872 873
}

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
874 875
}

876
KoInsets KoShape::strokeInsets() const
877
{
878
    Q_D(const KoShape);
879
    KoInsets answer;
880 881
    if (d->stroke)
        d->stroke->strokeInsets(this, answer);
882 883 884
    return answer;
}

885 886
qreal KoShape::rotation() const
{
887
    Q_D(const KoShape);
888
    // try to extract the rotation angle out of the local matrix
889 890 891
    // if it is a pure rotation matrix

    // check if the matrix has shearing mixed in
892
    if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
893
        return std::numeric_limits<qreal>::quiet_NaN();
894
    // check if the matrix has scaling mixed in
895
    if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
896
        return std::numeric_limits<qreal>::quiet_NaN();
897 898

    // calculate the angle from the matrix elements
899 900
    qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
    if (angle < 0.0)
901 902 903
        angle += 360.0;

    return angle;
Thomas Zander's avatar
Thomas Zander committed
904 905
}

906
QSizeF KoShape::size() const
907
{
908
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
909 910 911
    return d->size;
}

912 913
QPointF KoShape::position() const
{
914
    Q_D(const KoShape);
915
    QPointF center = outlineRect().center();
916
    return d->localMatrix.map(center) - center;
Thomas Zander's avatar
Thomas Zander committed
917 918
}

919
int KoShape::addConnectionPoint(const KoConnectionPoint &point)
920
{
921
    Q_D(KoShape);
922

923
    // get next glue point id
924
    int nextConnectionPointId = KoConnectionPoint::FirstCustomConnectionPoint;
Jan Hambrecht's avatar
Jan Hambrecht committed
925
    if (d->connectors.size())
926 927
        nextConnectionPointId = qMax(nextConnectionPointId, (--d->connectors.end()).key()+1);

928
    KoConnectionPoint p = point;
929 930
    d->convertFromShapeCoordinates(p, size());
    d->connectors[nextConnectionPointId] = p;
931 932 933 934

    return nextConnectionPointId;
}

935
bool KoShape::setConnectionPoint(int connectionPointId, const KoConnectionPoint &point)
936 937
{
    Q_D(KoShape);
938
    if (connectionPointId < 0)
939 940
        return false;

941 942
    const bool insertPoint = !hasConnectionPoint(connectionPointId);

943
    switch(connectionPointId) {
944 945 946 947 948 949
        case KoConnectionPoint::TopConnectionPoint:
        case KoConnectionPoint::RightConnectionPoint:
        case KoConnectionPoint::BottomConnectionPoint:
        case KoConnectionPoint::LeftConnectionPoint:
        {
            KoConnectionPoint::PointId id = static_cast<KoConnectionPoint::PointId>(connectionPointId);
950
            d->connectors[id] = KoConnectionPoint::defaultConnectionPoint(id);
951
            break;
952
        }
953
        default:
954
        {
955
            KoConnectionPoint p = point;
956 957
            d->convertFromShapeCoordinates(p, size());
            d->connectors[connectionPointId] = p;
958
            break;
959
        }
960 961
    }

962 963
    if(!insertPoint)
        d->shapeChanged(ConnectionPointChanged);
964 965 966 967

    return true;
}

968 969 970 971 972 973
bool KoShape::hasConnectionPoint(int connectionPointId) const
{
    Q_D(const KoShape);
    return d->connectors.contains(connectionPointId);
}

974
KoConnectionPoint KoShape::connectionPoint(int connectionPointId) const
975 976
{
    Q_D(const KoShape);
977
    KoConnectionPoint p = d->connectors.value(connectionPointId, KoConnectionPoint());
978
    // convert glue point to shape coordinates
979
    d->convertToShapeCoordinates(p, size());
980
    return p;
Thomas Zander's avatar
Thomas Zander committed
981 982
}

983
KoConnectionPoints KoShape::connectionPoints() const
984
{
985
    Q_D(const KoShape);
986
    QSizeF s = size();
987 988 989
    KoConnectionPoints points = d->connectors;
    KoConnectionPoints::iterator point = points.begin();
    KoConnectionPoints::iterator lastPoint = points.end();
990
    // convert glue points to shape coordinates
991
    for(; point != lastPoint; ++point) {