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
    }

    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
    /**
     * The algorithm below doesn't correctly handel the case when the two pointers actually
     * point to the same shape. So just check it is advance to guarantee strict weak ordering
     * relation requirement
     */
    if (s1 == s2) return false;


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

        if (!foundCommonParent) {
645
            if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) {
646 647
                index1 = parentShapeS1->zIndex();
            }
648
            parentShapeS1 = parentShapeS1->parent();
649 650
        }
    }
651

652 653 654 655 656 657 658 659
    // If the one shape is a parent/child of the other then sort so.
    if (s1 == parentShapeS2) {
        return true;
    }
    if (s2 == parentShapeS1) {
        return false;
    }

660
    // If we went that far then the z-Index is used for sorting.
661
    return index1 < index2;
Thomas Zander's avatar
Thomas Zander committed
662 663
}

664 665
void KoShape::setParent(KoShapeContainer *parent)
{
666
    Q_D(KoShape);
667 668

    if (d->parent == parent) {
669
        return;
670 671
    }

672 673
    KoShapeContainer *oldParent = d->parent;
    d->parent = 0; // avoids recursive removing
674 675 676 677 678 679 680

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

    KIS_SAFE_ASSERT_RECOVER_NOOP(parent != this);

Thomas Zander's avatar
Thomas Zander committed
681
    if (parent && parent != this) {
Thomas Zander's avatar
Thomas Zander committed
682
        d->parent = parent;
683
        parent->shapeInterface()->addShape(this);
684
    }
685

686
    notifyChanged();
687
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
688 689
}

690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711
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;
}

712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730
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;
}

731
qint16 KoShape::zIndex() const
732
{
733
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
734
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
735 736
}

737 738
void KoShape::update() const
{
739
    Q_D(const KoShape);
740

741 742
    if (!d->shapeManagers.empty()) {
        QRectF rect(boundingRect());
743
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
Jan Hambrecht's avatar
Jan Hambrecht committed
744
            manager->update(rect, this, true);
745
        }
Thomas Zander's avatar
Thomas Zander committed
746 747 748
    }
}

749
void KoShape::updateAbsolute(const QRectF &rect) const
750
{
751 752 753 754 755

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

756
    Q_D(const KoShape);
757

758
    if (!d->shapeManagers.empty() && isVisible()) {
759
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
760
            manager->update(rect);
761 762
        }
    }
Thomas Zander's avatar
Thomas Zander committed
763 764
}

765
QPainterPath KoShape::outline() const
766
{
Thomas Zander's avatar
Thomas Zander committed
767
    QPainterPath path;
768
    path.addRect(outlineRect());
Thomas Zander's avatar
Thomas Zander committed
769 770 771
    return path;
}

772 773
QRectF KoShape::outlineRect() const
{
774
    const QSizeF s = size();
775 776 777 778 779 780 781
    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);
782
    if (background()) {
783
        return outline();
784
    }
785 786

    return QPainterPath();
787 788
}

789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815
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);
}

816 817
void KoShape::copySettings(const KoShape *shape)
{
818
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
819 820
    d->size = shape->size();
    d->connectors.clear();
821
    Q_FOREACH (const KoConnectionPoint &point, shape->connectionPoints())
Jan Hambrecht's avatar
Jan Hambrecht committed
822
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
823
    d->zIndex = shape->zIndex();
824
    d->visible = shape->isVisible(false);
825

826 827 828 829 830
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
831

832
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
833 834
    d->protectContent = shape->isContentProtected();
    d->selectable = shape->isSelectable();
Thomas Zander's avatar
Thomas Zander committed
835
    d->keepAspect = shape->keepAspectRatio();
836
    d->localMatrix = shape->d_ptr->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
837 838
}

Thomas Zander's avatar
Thomas Zander committed
839
void KoShape::notifyChanged()
840
{
841
    Q_D(KoShape);
842
    Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
843
        manager->notifyShapeChanged(this);
844 845 846
    }
}

847 848
void KoShape::setUserData(KoShapeUserData *userData)
{
849
    Q_D(KoShape);
850
    d->userData.reset(userData);
851 852
}

853 854
KoShapeUserData *KoShape::userData() const
{
855
    Q_D(const KoShape);
856
    return d->userData.data();
857 858
}

Thomas Zander's avatar
Thomas Zander committed
859
bool KoShape::hasTransparency() const
860
{
Thomas Zander's avatar
Thomas Zander committed
861
    Q_D(const KoShape);
862 863 864
    QSharedPointer<KoShapeBackground> bg = background();

    return !bg || bg->hasTransparency() || d->transparency > 0.0;
865 866 867 868 869
}

void KoShape::setTransparency(qreal transparency)
{
    Q_D(KoShape);
870
    d->transparency = qBound<qreal>(0.0, transparency, 1.0);
871 872 873

    d->shapeChanged(TransparencyChanged);
    notifyChanged();
874 875 876 877 878 879 880 881 882 883 884 885
}

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
886 887
}

888
KoInsets KoShape::strokeInsets() const
889
{
890
    Q_D(const KoShape);
891
    KoInsets answer;
892 893
    if (d->stroke)
        d->stroke->strokeInsets(this, answer);
894 895 896
    return answer;
}

897 898
qreal KoShape::rotation() const
{
899
    Q_D(const KoShape);
900
    // try to extract the rotation angle out of the local matrix
901 902 903
    // if it is a pure rotation matrix

    // check if the matrix has shearing mixed in
904
    if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
905
        return std::numeric_limits<qreal>::quiet_NaN();
906
    // check if the matrix has scaling mixed in
907
    if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
908
        return std::numeric_limits<qreal>::quiet_NaN();
909 910

    // calculate the angle from the matrix elements
911 912
    qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
    if (angle < 0.0)
913 914 915
        angle += 360.0;

    return angle;
Thomas Zander's avatar
Thomas Zander committed
916 917
}

918
QSizeF KoShape::size() const
919
{
920
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
921 922 923
    return d->size;
}

924 925
QPointF KoShape::position() const
{
926
    Q_D(const KoShape);
927
    QPointF center = outlineRect().center();
928
    return d->localMatrix.map(center) - center;
Thomas Zander's avatar
Thomas Zander committed
929 930
}

931
int KoShape::addConnectionPoint(const KoConnectionPoint &point)
932
{
933
    Q_D(KoShape);
934

935
    // get next glue point id
936
    int nextConnectionPointId = KoConnectionPoint::FirstCustomConnectionPoint;
Jan Hambrecht's avatar
Jan Hambrecht committed
937
    if (d->connectors.size())
938 939
        nextConnectionPointId = qMax(nextConnectionPointId, (--d->connectors.end()).key()+1);

940
    KoConnectionPoint p = point;
941 942
    d->convertFromShapeCoordinates(p, size());
    d->connectors[nextConnectionPointId] = p;
943 944 945 946

    return nextConnectionPointId;
}

947
bool KoShape::setConnectionPoint(int connectionPointId, const KoConnectionPoint &point)
948 949
{
    Q_D(KoShape);
950
    if (connectionPointId < 0)
951 952
        return false;

953 954
    const bool insertPoint = !hasConnectionPoint(connectionPointId);

955
    switch(connectionPointId) {
956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971
    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;
    }
972 973
    }

974 975
    if(!insertPoint)
        d->shapeChanged(ConnectionPointChanged);
976 977 978 979

    return true;
}

980 981 982 983 984 985
bool KoShape::hasConnectionPoint(int connectionPointId) const
{
    Q_D(const KoShape);
    return d->connectors.contains(connectionPointId);
}

986
KoConnectionPoint KoShape::connectionPoint(int connectionPointId) const
987 988
{
    Q_D(const KoShape);
989
    KoConnectionPoint p = d->connectors.value(connectionPointId, KoConnectionPoint());
990
    // convert glue point to shape coordinates
991
    d->convertToShapeCoordinates(p, size());
992
    return p;
Thomas Zander's avatar
Thomas Zander committed
993 994
}

995
KoConnectionPoints KoShape::connectionPoints() const
996
{
997
    Q_D(const KoShape);
998
    QSizeF s = size();
999 1000 1001
    KoConnectionPoints points = d->connectors;
    KoConnectionPoints::iterator point = points.begin();
    KoConnectionPoints::iterator lastPoint = points.end();
1002
    // convert glue points to shape coordinates
1003
    for(; point != lastPoint; ++point) {
1004
        d->convertToShapeCoordinates(point.value(), s);