KoShape.cpp 75.7 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 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
void KoShapePrivate::updateStroke()
193
{
194
    Q_Q(KoShape);
195 196
    if (!stroke) return;

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

214 215 216 217 218 219 220 221
void KoShapePrivate::addShapeManager(KoShapeManager *manager)
{
    shapeManagers.insert(manager);
}

void KoShapePrivate::removeShapeManager(KoShapeManager *manager)
{
    shapeManagers.remove(manager);
222 223
}

224
void KoShapePrivate::convertFromShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
225
{
Jan Hambrecht's avatar
Jan Hambrecht committed
226
    switch(point.alignment) {
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 261 262 263
        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
264
    switch(point.alignment) {
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 293 294 295
        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;
    }
296 297
}

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

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

    return value;
}

311 312


313
// ======== KoShape
Thomas Zander's avatar
Thomas Zander committed
314
KoShape::KoShape()
315
    : d_ptr(new KoShapePrivate(this))
Thomas Zander's avatar
Thomas Zander committed
316
{
317
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
318 319
}

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

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

332 333 334 335 336
KoShape *KoShape::cloneShape() const
{
    return 0;
}

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

356
    notifyChanged();
357
    d->shapeChanged(ScaleChanged);
Thomas Zander's avatar
Thomas Zander committed
358 359
}

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

370
    notifyChanged();
371
    d->shapeChanged(RotationChanged);
Thomas Zander's avatar
Thomas Zander committed
372 373
}

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

384
    notifyChanged();
385
    d->shapeChanged(ShearChanged);
Thomas Zander's avatar
Thomas Zander committed
386 387
}

388
void KoShape::setSize(const QSizeF &newSize)
Thomas Zander's avatar
Thomas Zander committed
389
{
390
    Q_D(KoShape);
391
    QSizeF oldSize(size());
392

393
    // always set size, as d->size and size() may vary
Thomas Zander's avatar
Thomas Zander committed
394
    d->size = newSize;
395

396 397 398
    if (oldSize == newSize)
        return;

399
    notifyChanged();
400
    d->shapeChanged(SizeChanged);
Thomas Zander's avatar
Thomas Zander committed
401 402
}

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

    notifyChanged();
414
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
415 416
}

417
bool KoShape::hitTest(const QPointF &position) const
Thomas Zander's avatar
Thomas Zander committed
418
{
419
    Q_D(const KoShape);
420
    if (d->parent && d->parent->isClipped(this) && !d->parent->hitTest(position))
Thomas Zander's avatar
Thomas Zander committed
421 422
        return false;

423
    QPointF point = absoluteTransformation(0).inverted().map(position);
424
    QRectF bb(QPointF(), size());
425
    if (d->stroke) {
426
        KoInsets insets;
427
        d->stroke->strokeInsets(this, insets);
428
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
429
    }
430 431 432
    if (bb.contains(point))
        return true;

433
    // if there is no shadow we can as well just leave
434
    if (! d->shadow)
435
        return false;
Thomas Zander's avatar
Thomas Zander committed
436

437 438
    // the shadow has an offset to the shape, so we simply
    // check if the position minus the shadow offset hits the shape
439
    point = absoluteTransformation(0).inverted().map(position - d->shadow->offset());
440

441
    return bb.contains(point);
Thomas Zander's avatar
Thomas Zander committed
442 443
}

444
QRectF KoShape::boundingRect() const
Thomas Zander's avatar
Thomas Zander committed
445
{
446
    Q_D(const KoShape);
Jan Hambrecht's avatar
Jan Hambrecht committed
447

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

466
    return bb;
Thomas Zander's avatar
Thomas Zander committed
467 468
}

469 470 471 472 473 474 475 476 477
QRectF KoShape::boundingRect(const QList<KoShape *> &shapes)
{
    QRectF boundingRect;
    Q_FOREACH (KoShape *shape, shapes) {
        boundingRect |= shape->boundingRect();
    }
    return boundingRect;
}

478 479 480 481 482 483 484 485 486 487 488 489 490 491
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;
}

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

512
    if (converter) {
513 514 515
        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
516
    }
517 518

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
519 520
}

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

531
void KoShape::applyTransformation(const QTransform &matrix)
532
{
533
    Q_D(KoShape);
534
    d->localMatrix = matrix * d->localMatrix;
535
    notifyChanged();
536
    d->shapeChanged(GenericMatrixChange);
537 538
}

539
void KoShape::setTransformation(const QTransform &matrix)
540
{
541
    Q_D(KoShape);
542 543
    d->localMatrix = matrix;
    notifyChanged();
544
    d->shapeChanged(GenericMatrixChange);
545
}
Thomas Zander's avatar
Thomas Zander committed
546

547
QTransform KoShape::transformation() const
548
{
549
    Q_D(const KoShape);
550 551 552
    return d->localMatrix;
}

553
KoShape::ChildZOrderPolicy KoShape::childZOrderPolicy()
554
{
555
    return ChildZDefault;
556 557
}

558 559
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
{
560 561 562 563 564 565 566 567 568 569 570 571 572
    /**
     * 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.
     */

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

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

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

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

651 652
void KoShape::setParent(KoShapeContainer *parent)
{
653
    Q_D(KoShape);
654 655

    if (d->parent == parent) {
656
        return;
657 658
    }

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

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

    KIS_SAFE_ASSERT_RECOVER_NOOP(parent != this);

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

673
    notifyChanged();
674
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
675 676
}

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

699 700
int KoShape::zIndex() const
{
701
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
702
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
703 704
}

705 706
void KoShape::update() const
{
707
    Q_D(const KoShape);
708

709 710
    if (!d->shapeManagers.empty()) {
        QRectF rect(boundingRect());
711
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
Jan Hambrecht's avatar
Jan Hambrecht committed
712
            manager->update(rect, this, true);
713
        }
Thomas Zander's avatar
Thomas Zander committed
714 715 716
    }
}

717
void KoShape::update(const QRectF &rect) const
718
{
719 720 721 722 723

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

724
    Q_D(const KoShape);
725

726
    if (!d->shapeManagers.empty() && isVisible()) {
727
        QRectF rc(absoluteTransformation(0).mapRect(rect));
728
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
729
            manager->update(rc);
730 731
        }
    }
Thomas Zander's avatar
Thomas Zander committed
732 733
}

734
QPainterPath KoShape::outline() const
735
{
Thomas Zander's avatar
Thomas Zander committed
736
    QPainterPath path;
737
    path.addRect(outlineRect());
Thomas Zander's avatar
Thomas Zander committed
738 739 740
    return path;
}

741 742
QRectF KoShape::outlineRect() const
{
743
    const QSizeF s = size();
744 745 746 747 748 749 750
    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);
751
    if (d->fill) {
752
        return outline();
753
    }
754 755

    return QPainterPath();
756 757
}

758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784
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);
}

785 786
void KoShape::copySettings(const KoShape *shape)
{
787
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
788 789
    d->size = shape->size();
    d->connectors.clear();
790
    Q_FOREACH (const KoConnectionPoint &point, shape->connectionPoints())
Jan Hambrecht's avatar
Jan Hambrecht committed
791
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
792 793
    d->zIndex = shape->zIndex();
    d->visible = shape->isVisible();
794

795 796 797 798 799
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
800

801
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
802 803
    d->protectContent = shape->isContentProtected();
    d->selectable = shape->isSelectable();
Thomas Zander's avatar
Thomas Zander committed
804
    d->keepAspect = shape->keepAspectRatio();
805
    d->localMatrix = shape->d_ptr->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
806 807
}

Thomas Zander's avatar
Thomas Zander committed
808
void KoShape::notifyChanged()
809
{
810
    Q_D(KoShape);
811
    Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
812
        manager->notifyShapeChanged(this);
813 814 815
    }
}

816 817
void KoShape::setUserData(KoShapeUserData *userData)
{
818
    Q_D(KoShape);
819
    d->userData.reset(userData);
820 821
}

822 823
KoShapeUserData *KoShape::userData() const
{
824
    Q_D(const KoShape);
825
    return d->userData.data();
826 827
}

Thomas Zander's avatar
Thomas Zander committed
828
bool KoShape::hasTransparency() const
829
{
Thomas Zander's avatar
Thomas Zander committed
830
    Q_D(const KoShape);
831
    if (! d->fill)
Thomas Zander's avatar
Thomas Zander committed
832
        return true;
833
    else
834 835 836 837 838 839
        return d->fill->hasTransparency() || d->transparency > 0.0;
}

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

    d->shapeChanged(TransparencyChanged);
    notifyChanged();
844 845 846 847 848 849 850 851 852 853 854 855
}

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
856 857
}

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

867 868
qreal KoShape::rotation() const
{
869
    Q_D(const KoShape);
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
{
890
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
891 892 893
    return d->size;
}

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

901
int KoShape::addConnectionPoint(const KoConnectionPoint &point)
902
{
903
    Q_D(KoShape);
904

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

910
    KoConnectionPoint p = point;
911 912
    d->convertFromShapeCoordinates(p, size());
    d->connectors[nextConnectionPointId] = p;
913 914 915 916

    return nextConnectionPointId;
}

917
bool KoShape::setConnectionPoint(int connectionPointId, const KoConnectionPoint &point)
918 919
{
    Q_D(KoShape);
920
    if (connectionPointId < 0)
921 922
        return false;

923 924
    const bool insertPoint = !hasConnectionPoint(connectionPointId);

925
    switch(connectionPointId) {
926 927 928 929 930 931
        case KoConnectionPoint::TopConnectionPoint:
        case KoConnectionPoint::RightConnectionPoint:
        case KoConnectionPoint::BottomConnectionPoint:
        case KoConnectionPoint::LeftConnectionPoint:
        {
            KoConnectionPoint::PointId id = static_cast<KoConnectionPoint::PointId>(connectionPointId);
932
            d->connectors[id] = KoConnectionPoint::defaultConnectionPoint(id);
933
            break;
934
        }
935
        default:
936
        {
937
            KoConnectionPoint p = point;
938 939
            d->convertFromShapeCoordinates(p, size());
            d->connectors[connectionPointId] = p;
940
            break;
941
        }
942 943
    }

944 945
    if(!insertPoint)
        d->shapeChanged(ConnectionPointChanged);
946 947 948 949

    return true;
}

950 951 952 953 954 955
bool KoShape::hasConnectionPoint(int connectionPointId) const
{
    Q_D(const KoShape);
    return d->connectors.contains(connectionPointId);
}

956
KoConnectionPoint KoShape::connectionPoint(int connectionPointId) const
957 958
{
    Q_D(const KoShape);
959
    KoConnectionPoint p = d->connectors.value(connectionPointId, KoConnectionPoint());
960
    // convert glue point to shape coordinates
961
    d->convertToShapeCoordinates(p, size());
962
    return p;
Thomas Zander's avatar
Thomas Zander committed
963 964
}

965
KoConnectionPoints KoShape::connectionPoints() const
966
{