KoShape.cpp 76.6 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
qint16 KoShape::zIndex() const
701
{
702
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
703
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
704 705
}

706 707
void KoShape::update() const
{
708
    Q_D(const KoShape);
709

710 711
    if (!d->shapeManagers.empty()) {
        QRectF rect(boundingRect());
712
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
Jan Hambrecht's avatar
Jan Hambrecht committed
713
            manager->update(rect, this, true);
714
        }
Thomas Zander's avatar
Thomas Zander committed
715 716 717
    }
}

718
void KoShape::updateAbsolute(const QRectF &rect) const
719
{
720 721 722 723 724

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

725
    Q_D(const KoShape);
726

727
    if (!d->shapeManagers.empty() && isVisible()) {
728
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
729
            manager->update(rect);
730 731
        }
    }
Thomas Zander's avatar
Thomas Zander committed
732 733
}

734
QPainterPath KoShape::outline() const
735
{
Thomas Zander's avatar
Thomas Zander committed
736
    QPainterPath path;
737
    path.addRect(outlineRect());
Thomas Zander's avatar
Thomas Zander committed
738 739 740
    return path;
}

741 742
QRectF KoShape::outlineRect() const
{
743
    const QSizeF s = size();
744 745 746 747 748 749 750
    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);
751
    if (background()) {
752
        return outline();
753
    }
754 755

    return QPainterPath();
756 757
}

758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784
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);
}

785 786
void KoShape::copySettings(const KoShape *shape)
{
787
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
788 789
    d->size = shape->size();
    d->connectors.clear();
790
    Q_FOREACH (const KoConnectionPoint &point, shape->connectionPoints())
Jan Hambrecht's avatar
Jan Hambrecht committed
791
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
792 793
    d->zIndex = shape->zIndex();
    d->visible = shape->isVisible();
794

795 796 797 798 799
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
800

801
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
802 803
    d->protectContent = shape->isContentProtected();
    d->selectable = shape->isSelectable();
Thomas Zander's avatar
Thomas Zander committed
804
    d->keepAspect = shape->keepAspectRatio();
805
    d->localMatrix = shape->d_ptr->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
806 807
}

Thomas Zander's avatar
Thomas Zander committed
808
void KoShape::notifyChanged()
809
{
810
    Q_D(KoShape);
811
    Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
812
        manager->notifyShapeChanged(this);
813 814 815
    }
}

816 817
void KoShape::setUserData(KoShapeUserData *userData)
{
818
    Q_D(KoShape);
819
    d->userData.reset(userData);
820 821
}

822 823
KoShapeUserData *KoShape::userData() const
{
824
    Q_D(const KoShape);
825
    return d->userData.data();
826 827
}

Thomas Zander's avatar
Thomas Zander committed
828
bool KoShape::hasTransparency() const
829
{
Thomas Zander's avatar
Thomas Zander committed
830
    Q_D(const KoShape);
831 832 833
    QSharedPointer<KoShapeBackground> bg = background();

    return !bg || bg->hasTransparency() || d->transparency > 0.0;
834 835 836 837 838
}

void KoShape::setTransparency(qreal transparency)
{
    Q_D(KoShape);
839
    d->transparency = qBound<qreal>(0.0, transparency, 1.0);
840 841 842

    d->shapeChanged(TransparencyChanged);
    notifyChanged();
843 844 845 846 847 848 849 850 851 852 853 854
}

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
855 856
}

857
KoInsets KoShape::strokeInsets() const
858
{
859
    Q_D(const KoShape);
860
    KoInsets answer;
861 862
    if (d->stroke)
        d->stroke->strokeInsets(this, answer);
863 864 865
    return answer;
}

866 867
qreal KoShape::rotation() const
{
868
    Q_D(const KoShape);
869
    // try to extract the rotation angle out of the local matrix
870 871 872
    // if it is a pure rotation matrix

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

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

    return angle;
Thomas Zander's avatar
Thomas Zander committed
885 886
}

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

893 894
QPointF KoShape::position() const
{
895
    Q_D(const KoShape);
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
    Q_D(KoShape);
903

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

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

    return nextConnectionPointId;
}

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

922 923
    const bool insertPoint = !hasConnectionPoint(connectionPointId);

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

943 944
    if(!insertPoint)
        d->shapeChanged(ConnectionPointChanged);
945 946 947 948

    return true;
}

949 950 951 952 953 954
bool KoShape::hasConnectionPoint(int connectionPointId) const
{
    Q_D(const KoShape);
    return d->connectors.contains(connectionPointId);
}

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

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

    return points;
977 978
}

Jan Hambrecht's avatar