KoShape.cpp 74.2 KB
Newer Older
Thomas Zander's avatar
Thomas Zander committed
1
/* This file is part of the KDE project
C. Boemann's avatar
C. Boemann committed
2
   Copyright (C) 2006 C. Boemann Rasmussen <cbo@boemann.dk>
3
   Copyright (C) 2006-2010 Thomas Zander <zander@kde.org>
4
   Copyright (C) 2006-2010 Thorsten Zachmann <zachmann@kde.org>
5
   Copyright (C) 2007-2009,2011 Jan Hambrecht <jaham@gmx.net>
Boudewijn Rempt's avatar
Boudewijn Rempt committed
6
   CopyRight (C) 2010 Boudewijn Rempt <boud@valdyas.org>
Thomas Zander's avatar
Thomas Zander committed
7 8 9 10 11 12 13 14 15 16 17 18 19 20

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21
   Boston, MA 02110-1301, USA.
Thomas Zander's avatar
Thomas Zander committed
22 23
*/

24 25
#include <limits>

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

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

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

75 76
#include "kis_assert.h"

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

80
// KoShape::Private
81

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

113 114 115
KoShape::Private::Private(const Private &rhs)
    : QSharedData()
    , size(rhs.size),
116 117 118 119 120 121 122 123 124 125
      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),
126
      inheritBackground(rhs.inheritBackground),
127
      inheritStroke(rhs.inheritStroke),
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
      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)
{
}

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

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

172
    this->shapeChanged(type);
173

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

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

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

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

193
void KoShape::Private::convertFromShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
194
{
Jan Hambrecht's avatar
Jan Hambrecht committed
195
    switch(point.alignment) {
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
    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;
230 231 232
    }
}

233
void KoShape::Private::convertToShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
234
{
Jan Hambrecht's avatar
Jan Hambrecht committed
235
    switch(point.alignment) {
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
    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;
268
    }
269 270
}

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

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

    return value;
}

284 285


286
// ======== KoShape
287 288 289 290

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
291
KoShape::KoShape()
292
    : d(new Private)
Thomas Zander's avatar
Thomas Zander committed
293
{
294
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
295 296
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

549 550
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
{
551 552 553 554 555 556
    /**
     * WARNING: Our definition of zIndex is not yet compatible with SVG2's
     *          definition. In SVG stacking context of groups with the same
     *          zIndex are **merged**, while in Krita the contents of groups
     *          is never merged. One group will always below than the other.
     *          Therefore, when zIndex of two groups inside the same parent
Boudewijn Rempt's avatar
Boudewijn Rempt committed
557
     *          coincide, the resulting painting order in Krita is
558 559 560 561 562 563
     *          **UNDEFINED**.
     *
     *          To avoid this trouble we use  KoShapeReorderCommand::mergeInShape()
     *          inside KoShapeCreateCommand.
     */

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


572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
    // 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
611
    bool foundCommonParent = false;
612 613 614 615
    int index1 = s1->zIndex();
    int index2 = s2->zIndex();
    parentShapeS1 = s1;
    parentShapeS2 = s2;
616 617 618 619 620 621 622 623
    while (parentShapeS1 && !foundCommonParent) {
        parentShapeS2 = s2;
        index2 = parentShapeS2->zIndex();
        while (parentShapeS2) {
            if (parentShapeS2 == parentShapeS1) {
                foundCommonParent = true;
                break;
            }
624
            if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) {
625 626
                index2 = parentShapeS2->zIndex();
            }
627
            parentShapeS2 = parentShapeS2->parent();
628
        }
629 630

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

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

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

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

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

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

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

    KIS_SAFE_ASSERT_RECOVER_NOOP(parent != this);

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

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

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

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

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

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

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

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


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

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

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

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

    return QPainterPath();
767 768
}

769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
QPointF KoShape::absolutePosition(KoFlake::AnchorPosition anchor) const
{
    const QRectF rc = outlineRect();

    QPointF point = rc.topLeft();

    bool valid = false;
    QPointF anchoredPoint = KoFlake::anchorToPoint(anchor, rc, &valid);
    if (valid) {
        point = anchoredPoint;
    }

    return absoluteTransformation(0).map(point);
}

void KoShape::setAbsolutePosition(const QPointF &newPosition, KoFlake::AnchorPosition anchor)
{
    QPointF currentAbsPosition = absolutePosition(anchor);
    QPointF translate = newPosition - currentAbsPosition;
    QTransform translateMatrix;
    translateMatrix.translate(translate.x(), translate.y());
    applyAbsoluteTransformation(translateMatrix);
    notifyChanged();
792
    shapeChangedPriv(PositionChanged);
793 794
}

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

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

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

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

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

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

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

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

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

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

qreal KoShape::transparency(bool recursive) const
{
    if (!recursive || !parent()) {
        return d->transparency;
    } else {
        const qreal parentOpacity = 1.0-parent()->transparency(recursive);
        const qreal childOpacity = 1.0-d->transparency;
        return 1.0-(parentOpacity*childOpacity);
    }
Thomas Zander's avatar
Thomas Zander committed
858 859
}

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

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

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

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

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

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

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

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

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

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

    return nextConnectionPointId;
}

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

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

921
    switch(connectionPointId) {
922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937
    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;
    }
938 939
    }

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

    return true;
}

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

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

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

    return points;
971 972
}

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

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

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

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

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

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

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

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

1016
void KoShape::setTextRunAroundDistanceTop(qreal distance)
1017
{
1018 1019 1020 1021