KoShape.cpp 74.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>
Thomas Zander's avatar
Thomas Zander committed
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@kogmbh.com>
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,
Boudewijn Rempt's avatar
Boudewijn Rempt committed
21
   Boston, MA 02110-1301, USA.
Thomas Zander's avatar
Thomas Zander committed
22 23 24
*/

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

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

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

73 74
#include "kis_assert.h"

75
#include <limits>
76
#include "KoOdfGradientBackground.h"
77
#include <KisHandlePainterHelper.h>
78

Boudewijn Rempt's avatar
Boudewijn Rempt committed
79 80
// KoShapePrivate

81
KoShapePrivate::KoShapePrivate(KoShape *shape)
82 83
    : q_ptr(shape),
      size(50, 50),
Boudewijn Rempt's avatar
Boudewijn Rempt committed
84 85
      parent(0),
      shadow(0),
86
      border(0),
Boudewijn Rempt's avatar
Boudewijn Rempt committed
87 88 89
      filterEffectStack(0),
      transparency(0.0),
      zIndex(0),
90
      runThrough(0),
Boudewijn Rempt's avatar
Boudewijn Rempt committed
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 116 117 118 119 120 121 122 123 124 125 126 127 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
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),
      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)
{
}

157 158
KoShapePrivate::~KoShapePrivate()
{
159
    Q_Q(KoShape);
160
    if (parent) {
Thomas Zander's avatar
Thomas Zander committed
161
        parent->removeShape(q);
162 163
    }

164
    Q_FOREACH (KoShapeManager *manager, shapeManagers) {
165
        manager->shapeInterface()->notifyShapeDestructed(q);
Thomas Zander's avatar
Thomas Zander committed
166
    }
167
    shapeManagers.clear();
168

169
    if (shadow && !shadow->deref())
170
        delete shadow;
171
    if (filterEffectStack && !filterEffectStack->deref())
172
        delete filterEffectStack;
173
}
Thomas Zander's avatar
Thomas Zander committed
174

175 176
void KoShapePrivate::shapeChanged(KoShape::ChangeType type)
{
177
    Q_Q(KoShape);
178
    if (parent)
179
        parent->model()->childChanged(q, type);
180

181
    q->shapeChanged(type);
182 183

    Q_FOREACH (KoShape * shape, dependees) {
184
        shape->shapeChanged(type, q);
185 186 187 188 189
    }

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

192 193 194 195 196 197 198 199
void KoShapePrivate::addShapeManager(KoShapeManager *manager)
{
    shapeManagers.insert(manager);
}

void KoShapePrivate::removeShapeManager(KoShapeManager *manager)
{
    shapeManagers.remove(manager);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
200 201
}

202
void KoShapePrivate::convertFromShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
203
{
Jan Hambrecht's avatar
Jan Hambrecht committed
204
    switch(point.alignment) {
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 230 231 232 233 234 235 236 237 238 239 240 241
        case KoConnectionPoint::AlignNone:
            point.position = KoFlake::toRelative(point.position, shapeSize);
            point.position.rx() = qBound<qreal>(0.0, point.position.x(), 1.0);
            point.position.ry() = qBound<qreal>(0.0, point.position.y(), 1.0);
            break;
        case KoConnectionPoint::AlignRight:
            point.position.rx() -= shapeSize.width();
        case KoConnectionPoint::AlignLeft:
            point.position.ry() = 0.5*shapeSize.height();
            break;
        case KoConnectionPoint::AlignBottom:
            point.position.ry() -= shapeSize.height();
        case KoConnectionPoint::AlignTop:
            point.position.rx() = 0.5*shapeSize.width();
            break;
        case KoConnectionPoint::AlignTopLeft:
            // nothing to do here
            break;
        case KoConnectionPoint::AlignTopRight:
            point.position.rx() -= shapeSize.width();
            break;
        case KoConnectionPoint::AlignBottomLeft:
            point.position.ry() -= shapeSize.height();
            break;
        case KoConnectionPoint::AlignBottomRight:
            point.position.rx() -= shapeSize.width();
            point.position.ry() -= shapeSize.height();
            break;
        case KoConnectionPoint::AlignCenter:
            point.position.rx() -= 0.5 * shapeSize.width();
            point.position.ry() -= 0.5 * shapeSize.height();
            break;
    }
}

void KoShapePrivate::convertToShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
{
Jan Hambrecht's avatar
Jan Hambrecht committed
242
    switch(point.alignment) {
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 268 269 270 271 272 273
        case KoConnectionPoint::AlignNone:
            point.position = KoFlake::toAbsolute(point.position, shapeSize);
            break;
        case KoConnectionPoint::AlignRight:
            point.position.rx() += shapeSize.width();
        case KoConnectionPoint::AlignLeft:
            point.position.ry() = 0.5*shapeSize.height();
            break;
        case KoConnectionPoint::AlignBottom:
            point.position.ry() += shapeSize.height();
        case KoConnectionPoint::AlignTop:
            point.position.rx() = 0.5*shapeSize.width();
            break;
        case KoConnectionPoint::AlignTopLeft:
            // nothing to do here
            break;
        case KoConnectionPoint::AlignTopRight:
            point.position.rx() += shapeSize.width();
            break;
        case KoConnectionPoint::AlignBottomLeft:
            point.position.ry() += shapeSize.height();
            break;
        case KoConnectionPoint::AlignBottomRight:
            point.position.rx() += shapeSize.width();
            point.position.ry() += shapeSize.height();
            break;
        case KoConnectionPoint::AlignCenter:
            point.position.rx() += 0.5 * shapeSize.width();
            point.position.ry() += 0.5 * shapeSize.height();
            break;
    }
274 275
}

276
// static
277
QString KoShapePrivate::getStyleProperty(const char *property, KoShapeLoadingContext &context)
278 279 280 281 282 283 284 285 286 287 288
{
    KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
    QString value;

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

    return value;
}

289 290


291
// ======== KoShape
Thomas Zander's avatar
Thomas Zander committed
292
KoShape::KoShape()
Boudewijn Rempt's avatar
Boudewijn Rempt committed
293
    : d_ptr(new KoShapePrivate(this))
Thomas Zander's avatar
Thomas Zander committed
294
{
295
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
296 297
}

298 299
KoShape::KoShape(KoShapePrivate *dd)
    : d_ptr(dd)
300 301 302
{
}

Thomas Zander's avatar
Thomas Zander committed
303 304
KoShape::~KoShape()
{
305
    Q_D(KoShape);
306
    d->shapeChanged(Deleted);
307
    delete d_ptr;
Thomas Zander's avatar
Thomas Zander committed
308 309
}

310 311 312 313 314
KoShape *KoShape::cloneShape() const
{
    return 0;
}

Thomas Zander's avatar
Thomas Zander committed
315
void KoShape::scale(qreal sx, qreal sy)
Thomas Zander's avatar
Thomas Zander committed
316
{
317
    Q_D(KoShape);
318
    QPointF pos = position();
319
    QTransform scaleMatrix;
320 321 322
    scaleMatrix.translate(pos.x(), pos.y());
    scaleMatrix.scale(sx, sy);
    scaleMatrix.translate(-pos.x(), -pos.y());
323
    d->localMatrix = d->localMatrix * scaleMatrix;
324

325
    notifyChanged();
326
    d->shapeChanged(ScaleChanged);
Thomas Zander's avatar
Thomas Zander committed
327 328
}

329
void KoShape::rotate(qreal angle)
Thomas Zander's avatar
Thomas Zander committed
330
{
331
    Q_D(KoShape);
332
    QPointF center = d->localMatrix.map(QPointF(0.5 * size().width(), 0.5 * size().height()));
333
    QTransform rotateMatrix;
334 335 336
    rotateMatrix.translate(center.x(), center.y());
    rotateMatrix.rotate(angle);
    rotateMatrix.translate(-center.x(), -center.y());
337
    d->localMatrix = d->localMatrix * rotateMatrix;
338

339
    notifyChanged();
340
    d->shapeChanged(RotationChanged);
Thomas Zander's avatar
Thomas Zander committed
341 342
}

Thomas Zander's avatar
Thomas Zander committed
343
void KoShape::shear(qreal sx, qreal sy)
Thomas Zander's avatar
Thomas Zander committed
344
{
345
    Q_D(KoShape);
346
    QPointF pos = position();
347
    QTransform shearMatrix;
348 349 350
    shearMatrix.translate(pos.x(), pos.y());
    shearMatrix.shear(sx, sy);
    shearMatrix.translate(-pos.x(), -pos.y());
351
    d->localMatrix = d->localMatrix * shearMatrix;
352

353
    notifyChanged();
354
    d->shapeChanged(ShearChanged);
Thomas Zander's avatar
Thomas Zander committed
355 356
}

357
void KoShape::setSize(const QSizeF &newSize)
Thomas Zander's avatar
Thomas Zander committed
358
{
359
    Q_D(KoShape);
360
    QSizeF oldSize(size());
361

362
    // always set size, as d->size and size() may vary
Thomas Zander's avatar
Thomas Zander committed
363
    d->size = newSize;
364

365 366 367
    if (oldSize == newSize)
        return;

368
    notifyChanged();
369
    d->shapeChanged(SizeChanged);
Thomas Zander's avatar
Thomas Zander committed
370 371
}

372
void KoShape::setPosition(const QPointF &newPosition)
Thomas Zander's avatar
Thomas Zander committed
373
{
374
    Q_D(KoShape);
375
    QPointF currentPos = position();
376
    if (newPosition == currentPos)
Thomas Zander's avatar
Thomas Zander committed
377
        return;
378
    QTransform translateMatrix;
379
    translateMatrix.translate(newPosition.x() - currentPos.x(), newPosition.y() - currentPos.y());
380 381 382
    d->localMatrix = d->localMatrix * translateMatrix;

    notifyChanged();
383
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
384 385
}

386
bool KoShape::hitTest(const QPointF &position) const
Thomas Zander's avatar
Thomas Zander committed
387
{
388
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
389
    if (d->parent && d->parent->isClipped(this) && !d->parent->hitTest(position))
Thomas Zander's avatar
Thomas Zander committed
390 391
        return false;

392
    QPointF point = absoluteTransformation(0).inverted().map(position);
393
    QRectF bb(QPointF(), size());
394
    if (d->stroke) {
395
        KoInsets insets;
396
        d->stroke->strokeInsets(this, insets);
397
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
398
    }
399 400 401
    if (bb.contains(point))
        return true;

402
    // if there is no shadow we can as well just leave
403
    if (! d->shadow)
404
        return false;
Thomas Zander's avatar
Thomas Zander committed
405

406 407
    // the shadow has an offset to the shape, so we simply
    // check if the position minus the shadow offset hits the shape
408
    point = absoluteTransformation(0).inverted().map(position - d->shadow->offset());
409

410
    return bb.contains(point);
Thomas Zander's avatar
Thomas Zander committed
411 412
}

413
QRectF KoShape::boundingRect() const
Thomas Zander's avatar
Thomas Zander committed
414
{
415
    Q_D(const KoShape);
Jan Hambrecht's avatar
Jan Hambrecht committed
416

417
    QTransform transform = absoluteTransformation(0);
418
    QRectF bb = outlineRect();
419
    if (d->stroke) {
420
        KoInsets insets;
421
        d->stroke->strokeInsets(this, insets);
422 423
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
424
    bb = transform.mapRect(bb);
425
    if (d->shadow) {
426
        KoInsets insets;
Thomas Zander's avatar
Thomas Zander committed
427
        d->shadow->insets(insets);
428 429
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
430
    if (d->filterEffectStack) {
431
        QRectF clipRect = d->filterEffectStack->clipRectForBoundingRect(outlineRect());
432 433
        bb |= transform.mapRect(clipRect);
    }
Thomas Zander's avatar
Thomas Zander committed
434

435
    return bb;
Thomas Zander's avatar
Thomas Zander committed
436 437
}

438 439 440 441 442 443 444 445 446
QRectF KoShape::boundingRect(const QList<KoShape *> &shapes)
{
    QRectF boundingRect;
    Q_FOREACH (KoShape *shape, shapes) {
        boundingRect |= shape->boundingRect();
    }
    return boundingRect;
}

447 448 449 450 451 452 453 454 455 456 457 458 459 460
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;
}

461
QTransform KoShape::absoluteTransformation(const KoViewConverter *converter) const
462
{
463
    Q_D(const KoShape);
464
    QTransform matrix;
Thomas Zander's avatar
Thomas Zander committed
465
    // apply parents matrix to inherit any transformations done there.
466
    KoShapeContainer * container = d->parent;
467
    if (container) {
468
        if (container->inheritsTransform(this)) {
469 470 471 472
            // We do need to pass the converter here, otherwise the parent's
            // translation is not inherited.
            matrix = container->absoluteTransformation(converter);
        } else {
473 474 475 476
            QSizeF containerSize = container->size();
            QPointF containerPos = container->absolutePosition() - QPointF(0.5 * containerSize.width(), 0.5 * containerSize.height());
            if (converter)
                containerPos = converter->documentToView(containerPos);
477
            matrix.translate(containerPos.x(), containerPos.y());
Thomas Zander's avatar
Thomas Zander committed
478 479 480
        }
    }

481
    if (converter) {
482 483 484
        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
485
    }
486 487

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
488 489
}

490
void KoShape::applyAbsoluteTransformation(const QTransform &matrix)
491
{
492
    QTransform globalMatrix = absoluteTransformation(0);
493 494
    // 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
495
    // to be relative to the local coordinate system
496
    QTransform transformMatrix = globalMatrix * matrix * globalMatrix.inverted();
497
    applyTransformation(transformMatrix);
498 499
}

500
void KoShape::applyTransformation(const QTransform &matrix)
501
{
502
    Q_D(KoShape);
503
    d->localMatrix = matrix * d->localMatrix;
504
    notifyChanged();
505
    d->shapeChanged(GenericMatrixChange);
506 507
}

508
void KoShape::setTransformation(const QTransform &matrix)
509
{
510
    Q_D(KoShape);
511 512
    d->localMatrix = matrix;
    notifyChanged();
513
    d->shapeChanged(GenericMatrixChange);
514
}
Thomas Zander's avatar
Thomas Zander committed
515

516
QTransform KoShape::transformation() const
517
{
518
    Q_D(const KoShape);
519 520 521
    return d->localMatrix;
}

522
KoShape::ChildZOrderPolicy KoShape::childZOrderPolicy()
523
{
524
    return ChildZDefault;
525 526
}

527 528
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
{
529 530 531 532 533 534 535 536 537 538 539 540 541
    /**
     * 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.
     */

542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580
    // 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
581
    bool foundCommonParent = false;
582 583 584 585
    int index1 = s1->zIndex();
    int index2 = s2->zIndex();
    parentShapeS1 = s1;
    parentShapeS2 = s2;
586 587 588 589 590 591 592 593
    while (parentShapeS1 && !foundCommonParent) {
        parentShapeS2 = s2;
        index2 = parentShapeS2->zIndex();
        while (parentShapeS2) {
            if (parentShapeS2 == parentShapeS1) {
                foundCommonParent = true;
                break;
            }
594
            if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) {
595 596
                index2 = parentShapeS2->zIndex();
            }
597
            parentShapeS2 = parentShapeS2->parent();
598
        }
599 600

        if (!foundCommonParent) {
601
            if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) {
602 603
                index1 = parentShapeS1->zIndex();
            }
604
            parentShapeS1 = parentShapeS1->parent();
605 606
        }
    }
607

608 609 610 611 612 613 614 615
    // If the one shape is a parent/child of the other then sort so.
    if (s1 == parentShapeS2) {
        return true;
    }
    if (s2 == parentShapeS1) {
        return false;
    }

616
    // If we went that far then the z-Index is used for sorting.
617
    return index1 < index2;
Thomas Zander's avatar
Thomas Zander committed
618 619
}

620 621
void KoShape::setParent(KoShapeContainer *parent)
{
622
    Q_D(KoShape);
623 624

    if (d->parent == parent) {
625
        return;
626 627
    }

628 629
    KoShapeContainer *oldParent = d->parent;
    d->parent = 0; // avoids recursive removing
630 631 632 633 634 635 636

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

    KIS_SAFE_ASSERT_RECOVER_NOOP(parent != this);

Thomas Zander's avatar
Thomas Zander committed
637
    if (parent && parent != this) {
Thomas Zander's avatar
Thomas Zander committed
638
        d->parent = parent;
639
        parent->shapeInterface()->addShape(this);
640
    }
641

642
    notifyChanged();
643
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
644 645
}

646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667
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;
}

668 669
int KoShape::zIndex() const
{
670
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
671
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
672 673
}

674 675
void KoShape::update() const
{
676
    Q_D(const KoShape);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
677

678 679
    if (!d->shapeManagers.empty()) {
        QRectF rect(boundingRect());
680
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
Jan Hambrecht's avatar
Jan Hambrecht committed
681
            manager->update(rect, this, true);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
682
        }
Thomas Zander's avatar
Thomas Zander committed
683 684 685
    }
}

686
void KoShape::updateAbsolute(const QRectF &rect) const
687
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
688 689 690 691 692

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

693
    Q_D(const KoShape);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
694

695
    if (!d->shapeManagers.empty() && isVisible()) {
696
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
697
            manager->update(rect);
698 699
        }
    }
Thomas Zander's avatar
Thomas Zander committed
700 701
}

702
QPainterPath KoShape::outline() const
703
{
Thomas Zander's avatar
Thomas Zander committed
704
    QPainterPath path;
705
    path.addRect(outlineRect());
Thomas Zander's avatar
Thomas Zander committed
706 707 708
    return path;
}

709 710
QRectF KoShape::outlineRect() const
{
711
    const QSizeF s = size();
712 713 714 715 716 717 718
    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);
Inge Wallin's avatar
Inge Wallin committed
719
    if (d->fill) {
720
        return outline();
Inge Wallin's avatar
Inge Wallin committed
721
    }
722 723

    return QPainterPath();
724 725
}

726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752
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);
}

753 754
void KoShape::copySettings(const KoShape *shape)
{
755
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
756 757
    d->size = shape->size();
    d->connectors.clear();
758
    Q_FOREACH (const KoConnectionPoint &point, shape->connectionPoints())
Jan Hambrecht's avatar
Jan Hambrecht committed
759
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
760 761
    d->zIndex = shape->zIndex();
    d->visible = shape->isVisible();
762

763 764 765 766 767
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
768

769
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
770 771
    d->protectContent = shape->isContentProtected();
    d->selectable = shape->isSelectable();
Thomas Zander's avatar
Thomas Zander committed
772
    d->keepAspect = shape->keepAspectRatio();
773
    d->localMatrix = shape->d_ptr->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
774 775
}

Thomas Zander's avatar
Thomas Zander committed
776
void KoShape::notifyChanged()
777
{
778
    Q_D(KoShape);
779
    Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
780
        manager->notifyShapeChanged(this);
781 782 783
    }
}

784 785
void KoShape::setUserData(KoShapeUserData *userData)
{
786
    Q_D(KoShape);
787
    d->userData.reset(userData);
788 789
}

790 791
KoShapeUserData *KoShape::userData() const
{
792
    Q_D(const KoShape);
793
    return d->userData.data();
794 795
}

Thomas Zander's avatar
Thomas Zander committed
796
bool KoShape::hasTransparency() const
797
{
Thomas Zander's avatar
Thomas Zander committed
798
    Q_D(const KoShape);
799
    if (! d->fill)
Thomas Zander's avatar
Thomas Zander committed
800
        return true;
801
    else
802 803 804 805 806 807
        return d->fill->hasTransparency() || d->transparency > 0.0;
}

void KoShape::setTransparency(qreal transparency)
{
    Q_D(KoShape);
808
    d->transparency = qBound<qreal>(0.0, transparency, 1.0);
809 810 811

    d->shapeChanged(TransparencyChanged);
    notifyChanged();
812 813 814 815 816 817 818 819 820 821 822 823
}

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
824 825
}

826
KoInsets KoShape::strokeInsets() const
827
{
828
    Q_D(const KoShape);
829
    KoInsets answer;
830 831
    if (d->stroke)
        d->stroke->strokeInsets(this, answer);
832 833 834
    return answer;
}

835 836
qreal KoShape::rotation() const
{
837
    Q_D(const KoShape);
838
    // try to extract the rotation angle out of the local matrix
839 840 841
    // if it is a pure rotation matrix

    // check if the matrix has shearing mixed in
842
    if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
843
        return std::numeric_limits<qreal>::quiet_NaN();
844
    // check if the matrix has scaling mixed in
845
    if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
846
        return std::numeric_limits<qreal>::quiet_NaN();
847 848

    // calculate the angle from the matrix elements
849 850
    qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
    if (angle < 0.0)
851 852 853
        angle += 360.0;

    return angle;
Thomas Zander's avatar
Thomas Zander committed
854 855
}

856
QSizeF KoShape::size() const
857
{
858
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
859 860 861
    return d->size;
}

862 863
QPointF KoShape::position() const
{
864
    Q_D(const KoShape);
865
    QPointF center = outlineRect().center();
866
    return d->localMatrix.map(center) - center;
Thomas Zander's avatar
Thomas Zander committed
867 868
}

869
int KoShape::addConnectionPoint(const KoConnectionPoint &point)
870
{
871
    Q_D(KoShape);
872

873
    // get next glue point id
874
    int nextConnectionPointId = KoConnectionPoint::FirstCustomConnectionPoint;
Jan Hambrecht's avatar
Jan Hambrecht committed
875
    if (d->connectors.size())
876 877
        nextConnectionPointId = qMax(nextConnectionPointId, (--d->connectors.end()).key()+1);

878
    KoConnectionPoint p = point;
879 880
    d->convertFromShapeCoordinates(p, size());
    d->connectors[nextConnectionPointId] = p;
881 882 883 884

    return nextConnectionPointId;
}

885
bool KoShape::setConnectionPoint(int connectionPointId, const KoConnectionPoint &point)
886 887
{
    Q_D(KoShape);
888
    if (connectionPointId < 0)
889 890
        return false;

891 892
    const bool insertPoint = !hasConnectionPoint(connectionPointId);

893
    switch(connectionPointId) {
894 895 896 897 898 899
        case KoConnectionPoint::TopConnectionPoint:
        case KoConnectionPoint::RightConnectionPoint:
        case KoConnectionPoint::BottomConnectionPoint:
        case KoConnectionPoint::LeftConnectionPoint:
        {
            KoConnectionPoint::PointId id = static_cast<KoConnectionPoint::PointId>(connectionPointId);
900
            d->connectors[id] = KoConnectionPoint::defaultConnectionPoint(id);
901
            break;
902
        }
903
        default:
904
        {
905
            KoConnectionPoint p = point;
906 907
            d->convertFromShapeCoordinates(p, size());
            d->connectors[connectionPointId] = p;
908
            break;
909
        }
910 911
    }

912 913
    if(!insertPoint)
        d->shapeChanged(ConnectionPointChanged);
914 915 916 917

    return true;
}

918 919 920 921 922 923
bool KoShape::hasConnectionPoint(int connectionPointId) const
{
    Q_D(const KoShape);
    return d->connectors.contains(connectionPointId);
}

924
KoConnectionPoint KoShape::connectionPoint(int connectionPointId) const
925 926
{
    Q_D(const KoShape);
927
    KoConnectionPoint p = d->connectors.value(connectionPointId, KoConnectionPoint());
928
    // convert glue point to shape coordinates
929
    d->convertToShapeCoordinates(p, size());
930
    return p;
Thomas Zander's avatar
Thomas Zander committed
931 932
}

933
KoConnectionPoints KoShape::connectionPoints() const
934
{
935
    Q_D(const KoShape);
936
    QSizeF s = size();
937 938 939
    KoConnectionPoints points = d->connectors;
    KoConnectionPoints::iterator point = points.begin();
    KoConnectionPoints::iterator lastPoint = points.end();
940
    // convert glue points to shape coordinates
941
    for(; point != lastPoint; ++point) {
942
        d->convertToShapeCoordinates(point.value(), s);
943
    }
944 945

    return points;
946 947
}

948 949 950 951
void KoShape::removeConnectionPoint(int connectionPointId)
{
    Q_D(KoShape);
    d->connectors.remove(connectionPointId);
952
    d->shapeChanged(ConnectionPointChanged);
953 954 955 956 957 958 959 960
}

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

961 962 963 964 965 966
KoShape::TextRunAroundSide KoShape::textRunAroundSide() const
{
    Q_D(const KoShape);
    return d->textRunAroundSide;
}

967
void KoShape::setTextRunAroundSide(TextRunAroundSide side, RunThroughLevel runThrought)
968 969 970 971 972 973 974
{
    Q_D(KoShape);

    if (side == RunThrough) {
        if (runThrought == Background) {
            setRunThrough(-1);
        } else {
975
            setRunThrough(1);
976