KoShape.cpp 76.4 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
    }

    Q_FOREACH (KoShape::ShapeChangeListener *listener, listeners) {
203
        listener->notifyShapeChangedImpl(type, q);
204
    }
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
    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;
254 255 256 257 258
    }
}

void KoShapePrivate::convertToShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
{
Jan Hambrecht's avatar
Jan Hambrecht committed
259
    switch(point.alignment) {
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 289 290 291
    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;
292
    }
293 294
}

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

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

    return value;
}

308 309


310
// ======== KoShape
311 312 313 314

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
315
KoShape::KoShape()
316
    : d_ptr(new KoShapePrivate(this))
Thomas Zander's avatar
Thomas Zander committed
317
{
318
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
319 320
}

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

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

334 335
KoShape *KoShape::cloneShape() const
{
336
    KIS_SAFE_ASSERT_RECOVER_NOOP(0 && "not implemented!");
337
    qWarning() << shapeId() << "cannot be cloned";
338 339 340
    return 0;
}

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

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

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

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

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

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

392
void KoShape::setSize(const QSizeF &newSize)
Thomas Zander's avatar
Thomas Zander committed
393
{
394
    Q_D(KoShape);
395
    QSizeF oldSize(size());
396

397
    // always set size, as d->size and size() may vary
Thomas Zander's avatar
Thomas Zander committed
398
    d->size = newSize;
399

400 401 402
    if (oldSize == newSize)
        return;

403
    notifyChanged();
404
    d->shapeChanged(SizeChanged);
Thomas Zander's avatar
Thomas Zander committed
405 406
}

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

    notifyChanged();
418
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
419 420
}

421
bool KoShape::hitTest(const QPointF &position) const
Thomas Zander's avatar
Thomas Zander committed
422
{
423
    Q_D(const KoShape);
424
    if (d->parent && d->parent->isClipped(this) && !d->parent->hitTest(position))
Thomas Zander's avatar
Thomas Zander committed
425 426
        return false;

427
    QPointF point = absoluteTransformation(0).inverted().map(position);
428 429
    QRectF bb = outlineRect();

430
    if (d->stroke) {
431
        KoInsets insets;
432
        d->stroke->strokeInsets(this, insets);
433
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
434
    }
435 436 437
    if (bb.contains(point))
        return true;

438
    // if there is no shadow we can as well just leave
439
    if (! d->shadow)
440
        return false;
Thomas Zander's avatar
Thomas Zander committed
441

442 443
    // the shadow has an offset to the shape, so we simply
    // check if the position minus the shadow offset hits the shape
444
    point = absoluteTransformation(0).inverted().map(position - d->shadow->offset());
445

446
    return bb.contains(point);
Thomas Zander's avatar
Thomas Zander committed
447 448
}

449
QRectF KoShape::boundingRect() const
Thomas Zander's avatar
Thomas Zander committed
450
{
451
    Q_D(const KoShape);
Jan Hambrecht's avatar
Jan Hambrecht committed
452

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

471
    return bb;
Thomas Zander's avatar
Thomas Zander committed
472 473
}

474 475 476 477 478 479 480 481 482
QRectF KoShape::boundingRect(const QList<KoShape *> &shapes)
{
    QRectF boundingRect;
    Q_FOREACH (KoShape *shape, shapes) {
        boundingRect |= shape->boundingRect();
    }
    return boundingRect;
}

483 484 485 486 487 488 489 490 491 492 493 494 495 496
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;
}

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

517
    if (converter) {
518 519 520
        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
521
    }
522 523

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
524 525
}

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

536
void KoShape::applyTransformation(const QTransform &matrix)
537
{
538
    Q_D(KoShape);
539
    d->localMatrix = matrix * d->localMatrix;
540
    notifyChanged();
541
    d->shapeChanged(GenericMatrixChange);
542 543
}

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

552
QTransform KoShape::transformation() const
553
{
554
    Q_D(const KoShape);
555 556 557
    return d->localMatrix;
}

558
KoShape::ChildZOrderPolicy KoShape::childZOrderPolicy()
559
{
560
    return ChildZDefault;
561 562
}

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

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

        if (!foundCommonParent) {
637
            if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) {
638 639
                index1 = parentShapeS1->zIndex();
            }
640
            parentShapeS1 = parentShapeS1->parent();
641 642
        }
    }
643

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

652
    // If we went that far then the z-Index is used for sorting.
653
    return index1 < index2;
Thomas Zander's avatar
Thomas Zander committed
654 655
}

656 657
void KoShape::setParent(KoShapeContainer *parent)
{
658
    Q_D(KoShape);
659 660

    if (d->parent == parent) {
661
        return;
662 663
    }

664 665
    KoShapeContainer *oldParent = d->parent;
    d->parent = 0; // avoids recursive removing
666 667 668 669 670 671 672

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

    KIS_SAFE_ASSERT_RECOVER_NOOP(parent != this);

Thomas Zander's avatar
Thomas Zander committed
673
    if (parent && parent != this) {
Thomas Zander's avatar
Thomas Zander committed
674
        d->parent = parent;
675
        parent->shapeInterface()->addShape(this);
676
    }
677

678
    notifyChanged();
679
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
680 681
}

682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703
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;
}

704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
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;
}

723
qint16 KoShape::zIndex() const
724
{
725
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
726
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
727 728
}

729 730
void KoShape::update() const
{
731
    Q_D(const KoShape);
732

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

741
void KoShape::updateAbsolute(const QRectF &rect) const
742
{
743 744 745 746 747

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

748
    Q_D(const KoShape);
749

750
    if (!d->shapeManagers.empty() && isVisible()) {
751
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
752
            manager->update(rect);
753 754
        }
    }
Thomas Zander's avatar
Thomas Zander committed
755 756
}

757
QPainterPath KoShape::outline() const
758
{
Thomas Zander's avatar
Thomas Zander committed
759
    QPainterPath path;
760
    path.addRect(outlineRect());
Thomas Zander's avatar
Thomas Zander committed
761 762 763
    return path;
}

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

    return QPainterPath();
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 804 805 806 807
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);
}

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

818 819 820 821 822
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
823

824
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
825 826
    d->protectContent = shape->isContentProtected();
    d->selectable = shape->isSelectable();
Thomas Zander's avatar
Thomas Zander committed
827
    d->keepAspect = shape->keepAspectRatio();
828
    d->localMatrix = shape->d_ptr->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
829 830
}

Thomas Zander's avatar
Thomas Zander committed
831
void KoShape::notifyChanged()
832
{
833
    Q_D(KoShape);
834
    Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
835
        manager->notifyShapeChanged(this);
836 837 838
    }
}

839 840
void KoShape::setUserData(KoShapeUserData *userData)
{
841
    Q_D(KoShape);
842
    d->userData.reset(userData);
843 844
}

845 846
KoShapeUserData *KoShape::userData() const
{
847
    Q_D(const KoShape);
848
    return d->userData.data();
849 850
}

Thomas Zander's avatar
Thomas Zander committed
851
bool KoShape::hasTransparency() const
852
{
Thomas Zander's avatar
Thomas Zander committed
853
    Q_D(const KoShape);
854 855 856
    QSharedPointer<KoShapeBackground> bg = background();

    return !bg || bg->hasTransparency() || d->transparency > 0.0;
857 858 859 860 861
}

void KoShape::setTransparency(qreal transparency)
{
    Q_D(KoShape);
862
    d->transparency = qBound<qreal>(0.0, transparency, 1.0);
863 864 865

    d->shapeChanged(TransparencyChanged);
    notifyChanged();
866 867 868 869 870 871 872 873 874 875 876 877
}

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
878 879
}

880
KoInsets KoShape::strokeInsets() const
881
{
882
    Q_D(const KoShape);
883
    KoInsets answer;
884 885
    if (d->stroke)
        d->stroke->strokeInsets(this, answer);
886 887 888
    return answer;
}

889 890
qreal KoShape::rotation() const
{
891
    Q_D(const KoShape);
892
    // try to extract the rotation angle out of the local matrix
893 894 895
    // if it is a pure rotation matrix

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

    // calculate the angle from the matrix elements
903 904
    qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
    if (angle < 0.0)
905 906 907
        angle += 360.0;

    return angle;
Thomas Zander's avatar
Thomas Zander committed
908 909
}

910
QSizeF KoShape::size() const
911
{
912
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
913 914 915
    return d->size;
}

916 917
QPointF KoShape::position() const
{
918
    Q_D(const KoShape);
919
    QPointF center = outlineRect().center();
920
    return d->localMatrix.map(center) - center;
Thomas Zander's avatar
Thomas Zander committed
921 922
}

923
int KoShape::addConnectionPoint(const KoConnectionPoint &point)
924
{
925
    Q_D(KoShape);
926

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

932
    KoConnectionPoint p = point;
933 934
    d->convertFromShapeCoordinates(p, size());
    d->connectors[nextConnectionPointId] = p;
935 936 937 938

    return nextConnectionPointId;
}

939
bool KoShape::setConnectionPoint(int connectionPointId, const KoConnectionPoint &point)
940 941
{
    Q_D(KoShape);
942
    if (connectionPointId < 0)
943 944
        return false;

945 946
    const bool insertPoint = !hasConnectionPoint(connectionPointId);

947
    switch(connectionPointId) {
948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963
    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;
    }
964 965
    }

966 967
    if(!insertPoint)
        d->shapeChanged(ConnectionPointChanged);
968 969 970 971

    return true;
}

972 973 974 975 976 977
bool KoShape::hasConnectionPoint(int connectionPointId) const
{
    Q_D(const KoShape);
    return d->connectors.contains(connectionPointId);
}

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

987
KoConnectionPoints KoShape::connectionPoints() const
988
{
989
    Q_D(const KoShape);
990
    QSizeF s = size();
991 992 993
    KoConnectionPoints points = d->connectors;
    KoConnectionPoints::iterator point = points.begin();
    KoConnectionPoints::iterator lastPoint = points.end();
994
    // convert glue points to shape coordinates
995
    for(; point != lastPoint; ++point) {
996
        d->convertToShapeCoordinates(point.value(), s);
997
    }
998 999

    return points;