KoShape.cpp 73.9 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>
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,
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

79 80
// KoShapePrivate

81
KoShapePrivate::KoShapePrivate(KoShape *shape)
82 83
    : q_ptr(shape),
      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 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)
161
        parent->removeShape(q);
162
    Q_FOREACH (KoShapeManager *manager, shapeManagers) {
163
        manager->remove(q);
Thomas Zander's avatar
Thomas Zander committed
164
    }
165

166
    if (shadow && !shadow->deref())
167
        delete shadow;
168
    if (filterEffectStack && !filterEffectStack->deref())
169
        delete filterEffectStack;
170
}
Thomas Zander's avatar
Thomas Zander committed
171

172 173
void KoShapePrivate::shapeChanged(KoShape::ChangeType type)
{
174
    Q_Q(KoShape);
175
    if (parent)
176
        parent->model()->childChanged(q, type);
177

178
    q->shapeChanged(type);
179 180

    Q_FOREACH (KoShape * shape, dependees) {
181
        shape->shapeChanged(type, q);
182 183 184 185 186
    }

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

189
void KoShapePrivate::updateStroke()
190
{
191
    Q_Q(KoShape);
192 193
    if (!stroke) return;

194
    KoInsets insets;
195
    stroke->strokeInsets(q, insets);
196 197 198
    QSizeF inner = q->size();
    // update left
    q->update(QRectF(-insets.left, -insets.top, insets.left,
199
                     inner.height() + insets.top + insets.bottom));
200 201
    // update top
    q->update(QRectF(-insets.left, -insets.top,
202
                     inner.width() + insets.left + insets.right, insets.top));
203 204
    // update right
    q->update(QRectF(inner.width(), -insets.top, insets.right,
205
                     inner.height() + insets.top + insets.bottom));
206 207
    // update bottom
    q->update(QRectF(-insets.left, inner.height(),
208
                     inner.width() + insets.left + insets.right, insets.bottom));
209
}
Thomas Zander's avatar
Thomas Zander committed
210

211 212 213 214 215 216 217 218
void KoShapePrivate::addShapeManager(KoShapeManager *manager)
{
    shapeManagers.insert(manager);
}

void KoShapePrivate::removeShapeManager(KoShapeManager *manager)
{
    shapeManagers.remove(manager);
219 220
}

221
void KoShapePrivate::convertFromShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
222
{
Jan Hambrecht's avatar
Jan Hambrecht committed
223
    switch(point.alignment) {
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
        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
261
    switch(point.alignment) {
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 292
        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;
    }
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
Thomas Zander's avatar
Thomas Zander committed
311
KoShape::KoShape()
312
    : d_ptr(new KoShapePrivate(this))
Thomas Zander's avatar
Thomas Zander committed
313
{
314
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
315 316
}

317 318
KoShape::KoShape(KoShapePrivate *dd)
    : d_ptr(dd)
319 320 321
{
}

Thomas Zander's avatar
Thomas Zander committed
322 323
KoShape::~KoShape()
{
324
    Q_D(KoShape);
325
    d->shapeChanged(Deleted);
326
    delete d_ptr;
Thomas Zander's avatar
Thomas Zander committed
327 328
}

329 330 331 332 333
KoShape *KoShape::cloneShape() const
{
    return 0;
}

Thomas Zander's avatar
Thomas Zander committed
334
void KoShape::scale(qreal sx, qreal sy)
Thomas Zander's avatar
Thomas Zander committed
335
{
336
    Q_D(KoShape);
337
    QPointF pos = position();
338
    QTransform scaleMatrix;
339 340 341
    scaleMatrix.translate(pos.x(), pos.y());
    scaleMatrix.scale(sx, sy);
    scaleMatrix.translate(-pos.x(), -pos.y());
342
    d->localMatrix = d->localMatrix * scaleMatrix;
343

344
    notifyChanged();
345
    d->shapeChanged(ScaleChanged);
Thomas Zander's avatar
Thomas Zander committed
346 347
}

348
void KoShape::rotate(qreal angle)
Thomas Zander's avatar
Thomas Zander committed
349
{
350
    Q_D(KoShape);
351
    QPointF center = d->localMatrix.map(QPointF(0.5 * size().width(), 0.5 * size().height()));
352
    QTransform rotateMatrix;
353 354 355
    rotateMatrix.translate(center.x(), center.y());
    rotateMatrix.rotate(angle);
    rotateMatrix.translate(-center.x(), -center.y());
356
    d->localMatrix = d->localMatrix * rotateMatrix;
357

358
    notifyChanged();
359
    d->shapeChanged(RotationChanged);
Thomas Zander's avatar
Thomas Zander committed
360 361
}

Thomas Zander's avatar
Thomas Zander committed
362
void KoShape::shear(qreal sx, qreal sy)
Thomas Zander's avatar
Thomas Zander committed
363
{
364
    Q_D(KoShape);
365
    QPointF pos = position();
366
    QTransform shearMatrix;
367 368 369
    shearMatrix.translate(pos.x(), pos.y());
    shearMatrix.shear(sx, sy);
    shearMatrix.translate(-pos.x(), -pos.y());
370
    d->localMatrix = d->localMatrix * shearMatrix;
371

372
    notifyChanged();
373
    d->shapeChanged(ShearChanged);
Thomas Zander's avatar
Thomas Zander committed
374 375
}

376
void KoShape::setSize(const QSizeF &newSize)
Thomas Zander's avatar
Thomas Zander committed
377
{
378
    Q_D(KoShape);
379
    QSizeF oldSize(size());
380

381
    // always set size, as d->size and size() may vary
Thomas Zander's avatar
Thomas Zander committed
382
    d->size = newSize;
383

384 385 386
    if (oldSize == newSize)
        return;

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

391
void KoShape::setPosition(const QPointF &newPosition)
Thomas Zander's avatar
Thomas Zander committed
392
{
393
    Q_D(KoShape);
394
    QPointF currentPos = position();
395
    if (newPosition == currentPos)
Thomas Zander's avatar
Thomas Zander committed
396
        return;
397
    QTransform translateMatrix;
398
    translateMatrix.translate(newPosition.x() - currentPos.x(), newPosition.y() - currentPos.y());
399 400 401
    d->localMatrix = d->localMatrix * translateMatrix;

    notifyChanged();
402
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
403 404
}

405
bool KoShape::hitTest(const QPointF &position) const
Thomas Zander's avatar
Thomas Zander committed
406
{
407
    Q_D(const KoShape);
408
    if (d->parent && d->parent->isClipped(this) && !d->parent->hitTest(position))
Thomas Zander's avatar
Thomas Zander committed
409 410
        return false;

411
    QPointF point = absoluteTransformation(0).inverted().map(position);
412
    QRectF bb(QPointF(), size());
413
    if (d->stroke) {
414
        KoInsets insets;
415
        d->stroke->strokeInsets(this, insets);
416
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
417
    }
418 419 420
    if (bb.contains(point))
        return true;

421
    // if there is no shadow we can as well just leave
422
    if (! d->shadow)
423
        return false;
Thomas Zander's avatar
Thomas Zander committed
424

425 426
    // the shadow has an offset to the shape, so we simply
    // check if the position minus the shadow offset hits the shape
427
    point = absoluteTransformation(0).inverted().map(position - d->shadow->offset());
428

429
    return bb.contains(point);
Thomas Zander's avatar
Thomas Zander committed
430 431
}

432
QRectF KoShape::boundingRect() const
Thomas Zander's avatar
Thomas Zander committed
433
{
434
    Q_D(const KoShape);
Jan Hambrecht's avatar
Jan Hambrecht committed
435

436
    QTransform transform = absoluteTransformation(0);
437
    QRectF bb = outlineRect();
438
    if (d->stroke) {
439
        KoInsets insets;
440
        d->stroke->strokeInsets(this, insets);
441 442
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
443
    bb = transform.mapRect(bb);
444
    if (d->shadow) {
445
        KoInsets insets;
Thomas Zander's avatar
Thomas Zander committed
446
        d->shadow->insets(insets);
447 448
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
449
    if (d->filterEffectStack) {
450
        QRectF clipRect = d->filterEffectStack->clipRectForBoundingRect(outlineRect());
451 452
        bb |= transform.mapRect(clipRect);
    }
Thomas Zander's avatar
Thomas Zander committed
453

454
    return bb;
Thomas Zander's avatar
Thomas Zander committed
455 456
}

457 458 459 460 461 462 463 464 465
QRectF KoShape::boundingRect(const QList<KoShape *> &shapes)
{
    QRectF boundingRect;
    Q_FOREACH (KoShape *shape, shapes) {
        boundingRect |= shape->boundingRect();
    }
    return boundingRect;
}

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

486
    if (converter) {
487 488 489
        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
490
    }
491 492

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
493 494
}

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

505
void KoShape::applyTransformation(const QTransform &matrix)
506
{
507
    Q_D(KoShape);
508
    d->localMatrix = matrix * d->localMatrix;
509
    notifyChanged();
510
    d->shapeChanged(GenericMatrixChange);
511 512
}

513
void KoShape::setTransformation(const QTransform &matrix)
514
{
515
    Q_D(KoShape);
516 517
    d->localMatrix = matrix;
    notifyChanged();
518
    d->shapeChanged(GenericMatrixChange);
519
}
Thomas Zander's avatar
Thomas Zander committed
520

521
QTransform KoShape::transformation() const
522
{
523
    Q_D(const KoShape);
524 525 526
    return d->localMatrix;
}

527
KoShape::ChildZOrderPolicy KoShape::childZOrderPolicy()
528
{
529
    return ChildZDefault;
530 531
}

532 533
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
{
534 535 536 537 538 539 540 541 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
    // 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
573
    bool foundCommonParent = false;
574 575 576 577
    int index1 = s1->zIndex();
    int index2 = s2->zIndex();
    parentShapeS1 = s1;
    parentShapeS2 = s2;
578 579 580 581 582 583 584 585
    while (parentShapeS1 && !foundCommonParent) {
        parentShapeS2 = s2;
        index2 = parentShapeS2->zIndex();
        while (parentShapeS2) {
            if (parentShapeS2 == parentShapeS1) {
                foundCommonParent = true;
                break;
            }
586
            if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) {
587 588
                index2 = parentShapeS2->zIndex();
            }
589
            parentShapeS2 = parentShapeS2->parent();
590
        }
591 592

        if (!foundCommonParent) {
593
            if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) {
594 595
                index1 = parentShapeS1->zIndex();
            }
596
            parentShapeS1 = parentShapeS1->parent();
597 598
        }
    }
599

600 601 602 603 604 605 606 607
    // If the one shape is a parent/child of the other then sort so.
    if (s1 == parentShapeS2) {
        return true;
    }
    if (s2 == parentShapeS1) {
        return false;
    }

608
    // If we went that far then the z-Index is used for sorting.
609
    return index1 < index2;
Thomas Zander's avatar
Thomas Zander committed
610 611
}

612 613
void KoShape::setParent(KoShapeContainer *parent)
{
614
    Q_D(KoShape);
615 616

    if (d->parent == parent) {
617
        return;
618 619
    }

620 621
    KoShapeContainer *oldParent = d->parent;
    d->parent = 0; // avoids recursive removing
622 623 624 625 626 627 628

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

    KIS_SAFE_ASSERT_RECOVER_NOOP(parent != this);

Thomas Zander's avatar
Thomas Zander committed
629
    if (parent && parent != this) {
Thomas Zander's avatar
Thomas Zander committed
630
        d->parent = parent;
631
        parent->shapeInterface()->addShape(this);
632
    }
633

634
    notifyChanged();
635
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
636 637
}

638 639
int KoShape::zIndex() const
{
640
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
641
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
642 643
}

644 645
void KoShape::update() const
{
646
    Q_D(const KoShape);
647

648 649
    if (!d->shapeManagers.empty()) {
        QRectF rect(boundingRect());
650
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
Jan Hambrecht's avatar
Jan Hambrecht committed
651
            manager->update(rect, this, true);
652
        }
Thomas Zander's avatar
Thomas Zander committed
653 654 655
    }
}

656
void KoShape::update(const QRectF &rect) const
657
{
658 659 660 661 662

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

663
    Q_D(const KoShape);
664

665
    if (!d->shapeManagers.empty() && isVisible()) {
666
        QRectF rc(absoluteTransformation(0).mapRect(rect));
667
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
668
            manager->update(rc);
669 670
        }
    }
Thomas Zander's avatar
Thomas Zander committed
671 672
}

673
QPainterPath KoShape::outline() const
674
{
Thomas Zander's avatar
Thomas Zander committed
675
    QPainterPath path;
676
    path.addRect(outlineRect());
Thomas Zander's avatar
Thomas Zander committed
677 678 679
    return path;
}

680 681
QRectF KoShape::outlineRect() const
{
682
    const QSizeF s = size();
683 684 685 686 687 688 689
    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);
690
    if (d->fill) {
691
        return outline();
692
    }
693 694

    return QPainterPath();
695 696
}

697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723
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);
}

724 725
void KoShape::copySettings(const KoShape *shape)
{
726
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
727 728
    d->size = shape->size();
    d->connectors.clear();
729
    Q_FOREACH (const KoConnectionPoint &point, shape->connectionPoints())
Jan Hambrecht's avatar
Jan Hambrecht committed
730
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
731 732
    d->zIndex = shape->zIndex();
    d->visible = shape->isVisible();
733

734 735 736 737 738
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
739

740
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
741 742
    d->protectContent = shape->isContentProtected();
    d->selectable = shape->isSelectable();
Thomas Zander's avatar
Thomas Zander committed
743
    d->keepAspect = shape->keepAspectRatio();
744
    d->localMatrix = shape->d_ptr->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
745 746
}

Thomas Zander's avatar
Thomas Zander committed
747
void KoShape::notifyChanged()
748
{
749
    Q_D(KoShape);
750
    Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
751
        manager->notifyShapeChanged(this);
752 753 754
    }
}

755 756
void KoShape::setUserData(KoShapeUserData *userData)
{
757
    Q_D(KoShape);
758
    d->userData.reset(userData);
759 760
}

761 762
KoShapeUserData *KoShape::userData() const
{
763
    Q_D(const KoShape);
764
    return d->userData.data();
765 766
}

Thomas Zander's avatar
Thomas Zander committed
767
bool KoShape::hasTransparency() const
768
{
Thomas Zander's avatar
Thomas Zander committed
769
    Q_D(const KoShape);
770
    if (! d->fill)
Thomas Zander's avatar
Thomas Zander committed
771
        return true;
772
    else
773 774 775 776 777 778
        return d->fill->hasTransparency() || d->transparency > 0.0;
}

void KoShape::setTransparency(qreal transparency)
{
    Q_D(KoShape);
779
    d->transparency = qBound<qreal>(0.0, transparency, 1.0);
780 781 782

    d->shapeChanged(TransparencyChanged);
    notifyChanged();
783 784 785 786 787 788 789 790 791 792 793 794
}

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
795 796
}

797
KoInsets KoShape::strokeInsets() const
798
{
799
    Q_D(const KoShape);
800
    KoInsets answer;
801 802
    if (d->stroke)
        d->stroke->strokeInsets(this, answer);
803 804 805
    return answer;
}

806 807
qreal KoShape::rotation() const
{
808
    Q_D(const KoShape);
809
    // try to extract the rotation angle out of the local matrix
810 811 812
    // if it is a pure rotation matrix

    // check if the matrix has shearing mixed in
813
    if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
814
        return std::numeric_limits<qreal>::quiet_NaN();
815
    // check if the matrix has scaling mixed in
816
    if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
817
        return std::numeric_limits<qreal>::quiet_NaN();
818 819

    // calculate the angle from the matrix elements
820 821
    qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
    if (angle < 0.0)
822 823 824
        angle += 360.0;

    return angle;
Thomas Zander's avatar
Thomas Zander committed
825 826
}

827
QSizeF KoShape::size() const
828
{
829
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
830 831 832
    return d->size;
}

833 834
QPointF KoShape::position() const
{
835
    Q_D(const KoShape);
836
    QPointF center = outlineRect().center();
837
    return d->localMatrix.map(center) - center;
Thomas Zander's avatar
Thomas Zander committed
838 839
}

840
int KoShape::addConnectionPoint(const KoConnectionPoint &point)
841
{
842
    Q_D(KoShape);
843

844
    // get next glue point id
845
    int nextConnectionPointId = KoConnectionPoint::FirstCustomConnectionPoint;
Jan Hambrecht's avatar
Jan Hambrecht committed
846
    if (d->connectors.size())
847 848
        nextConnectionPointId = qMax(nextConnectionPointId, (--d->connectors.end()).key()+1);

849
    KoConnectionPoint p = point;
850 851
    d->convertFromShapeCoordinates(p, size());
    d->connectors[nextConnectionPointId] = p;
852 853 854 855

    return nextConnectionPointId;
}

856
bool KoShape::setConnectionPoint(int connectionPointId, const KoConnectionPoint &point)
857 858
{
    Q_D(KoShape);
859
    if (connectionPointId < 0)
860 861
        return false;

862 863
    const bool insertPoint = !hasConnectionPoint(connectionPointId);

864
    switch(connectionPointId) {
865 866 867 868 869 870
        case KoConnectionPoint::TopConnectionPoint:
        case KoConnectionPoint::RightConnectionPoint:
        case KoConnectionPoint::BottomConnectionPoint:
        case KoConnectionPoint::LeftConnectionPoint:
        {
            KoConnectionPoint::PointId id = static_cast<KoConnectionPoint::PointId>(connectionPointId);
871
            d->connectors[id] = KoConnectionPoint::defaultConnectionPoint(id);
872
            break;
873
        }
874
        default:
875
        {
876
            KoConnectionPoint p = point;
877 878
            d->convertFromShapeCoordinates(p, size());
            d->connectors[connectionPointId] = p;
879
            break;
880
        }
881 882
    }

883 884
    if(!insertPoint)
        d->shapeChanged(ConnectionPointChanged);
885 886 887 888

    return true;
}

889 890 891 892 893 894
bool KoShape::hasConnectionPoint(int connectionPointId) const
{
    Q_D(const KoShape);
    return d->connectors.contains(connectionPointId);
}

895
KoConnectionPoint KoShape::connectionPoint(int connectionPointId) const
896 897
{
    Q_D(const KoShape);
898
    KoConnectionPoint p = d->connectors.value(connectionPointId, KoConnectionPoint());
899
    // convert glue point to shape coordinates
900
    d->convertToShapeCoordinates(p, size());
901
    return p;
Thomas Zander's avatar
Thomas Zander committed
902 903
}

904
KoConnectionPoints KoShape::connectionPoints() const
905
{
906
    Q_D(const KoShape);
907
    QSizeF s = size();
908 909 910
    KoConnectionPoints points = d->connectors;
    KoConnectionPoints::iterator point = points.begin();
    KoConnectionPoints::iterator lastPoint = points.end();
911
    // convert glue points to shape coordinates
912
    for(; point != lastPoint; ++point) {
913
        d->convertToShapeCoordinates(point.value(), s);
914
    }
915 916

    return points;
917 918
}

919 920 921 922
void KoShape::removeConnectionPoint(int connectionPointId)
{
    Q_D(KoShape);
    d->connectors.remove(connectionPointId);
923
    d->shapeChanged(ConnectionPointChanged);
924 925 926 927 928 929 930 931
}

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

932 933 934 935 936 937
KoShape::TextRunAroundSide KoShape::textRunAroundSide()