KoShape.cpp 74.5 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
    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,
Boudewijn Rempt's avatar
Boudewijn Rempt committed
199
                     inner.height() + insets.top + insets.bottom));
200 201
    // update top
    q->update(QRectF(-insets.left, -insets.top,
Boudewijn Rempt's avatar
Boudewijn Rempt committed
202
                     inner.width() + insets.left + insets.right, insets.top));
203 204
    // update right
    q->update(QRectF(inner.width(), -insets.top, insets.right,
Boudewijn Rempt's avatar
Boudewijn Rempt committed
205
                     inner.height() + insets.top + insets.bottom));
206 207
    // update bottom
    q->update(QRectF(-insets.left, inner.height(),
Boudewijn Rempt's avatar
Boudewijn Rempt committed
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);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
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()
Boudewijn Rempt's avatar
Boudewijn Rempt committed
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);
Thomas Zander's avatar
Thomas Zander committed
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
    /**
     * 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.
     */

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 581 582 583 584 585
    // 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
586
    bool foundCommonParent = false;
587 588 589 590
    int index1 = s1->zIndex();
    int index2 = s2->zIndex();
    parentShapeS1 = s1;
    parentShapeS2 = s2;
591 592 593 594 595 596 597 598
    while (parentShapeS1 && !foundCommonParent) {
        parentShapeS2 = s2;
        index2 = parentShapeS2->zIndex();
        while (parentShapeS2) {
            if (parentShapeS2 == parentShapeS1) {
                foundCommonParent = true;
                break;
            }
599
            if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) {
600 601
                index2 = parentShapeS2->zIndex();
            }
602
            parentShapeS2 = parentShapeS2->parent();
603
        }
604 605

        if (!foundCommonParent) {
606
            if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) {
607 608
                index1 = parentShapeS1->zIndex();
            }
609
            parentShapeS1 = parentShapeS1->parent();
610 611
        }
    }
612

613 614 615 616 617 618 619 620
    // If the one shape is a parent/child of the other then sort so.
    if (s1 == parentShapeS2) {
        return true;
    }
    if (s2 == parentShapeS1) {
        return false;
    }

621
    // If we went that far then the z-Index is used for sorting.
622
    return index1 < index2;
Thomas Zander's avatar
Thomas Zander committed
623 624
}

625 626
void KoShape::setParent(KoShapeContainer *parent)
{
627
    Q_D(KoShape);
628 629

    if (d->parent == parent) {
630
        return;
631 632
    }

633 634
    KoShapeContainer *oldParent = d->parent;
    d->parent = 0; // avoids recursive removing
635 636 637 638 639 640 641

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

    KIS_SAFE_ASSERT_RECOVER_NOOP(parent != this);

Thomas Zander's avatar
Thomas Zander committed
642
    if (parent && parent != this) {
Thomas Zander's avatar
Thomas Zander committed
643
        d->parent = parent;
644
        parent->shapeInterface()->addShape(this);
645
    }
646

647
    notifyChanged();
648
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
649 650
}

651 652
int KoShape::zIndex() const
{
653
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
654
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
655 656
}

657 658
void KoShape::update() const
{
659
    Q_D(const KoShape);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
660

661 662
    if (!d->shapeManagers.empty()) {
        QRectF rect(boundingRect());
663
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
Jan Hambrecht's avatar
Jan Hambrecht committed
664
            manager->update(rect, this, true);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
665
        }
Thomas Zander's avatar
Thomas Zander committed
666 667 668
    }
}

Boudewijn Rempt's avatar
Boudewijn Rempt committed
669
void KoShape::update(const QRectF &rect) const
670
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
671 672 673 674 675

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

676
    Q_D(const KoShape);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
677

678
    if (!d->shapeManagers.empty() && isVisible()) {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
679
        QRectF rc(absoluteTransformation(0).mapRect(rect));
680
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
681
            manager->update(rc);
682 683
        }
    }
Thomas Zander's avatar
Thomas Zander committed
684 685
}

686
QPainterPath KoShape::outline() const
687
{
Thomas Zander's avatar
Thomas Zander committed
688
    QPainterPath path;
689
    path.addRect(outlineRect());
Thomas Zander's avatar
Thomas Zander committed
690 691 692
    return path;
}

693 694
QRectF KoShape::outlineRect() const
{
695
    const QSizeF s = size();
696 697 698 699 700 701 702
    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
703
    if (d->fill) {
704
        return outline();
Inge Wallin's avatar
Inge Wallin committed
705
    }
706 707

    return QPainterPath();
708 709
}

710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736
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);
}

737 738
void KoShape::copySettings(const KoShape *shape)
{
739
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
740 741
    d->size = shape->size();
    d->connectors.clear();
742
    Q_FOREACH (const KoConnectionPoint &point, shape->connectionPoints())
Jan Hambrecht's avatar
Jan Hambrecht committed
743
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
744 745
    d->zIndex = shape->zIndex();
    d->visible = shape->isVisible();
746

747 748 749 750 751
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
752

753
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
754 755
    d->protectContent = shape->isContentProtected();
    d->selectable = shape->isSelectable();
Thomas Zander's avatar
Thomas Zander committed
756
    d->keepAspect = shape->keepAspectRatio();
757
    d->localMatrix = shape->d_ptr->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
758 759
}

Thomas Zander's avatar
Thomas Zander committed
760
void KoShape::notifyChanged()
761
{
762
    Q_D(KoShape);
763
    Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
764
        manager->notifyShapeChanged(this);
765 766 767
    }
}

768 769
void KoShape::setUserData(KoShapeUserData *userData)
{
770
    Q_D(KoShape);
771
    d->userData.reset(userData);
772 773
}

774 775
KoShapeUserData *KoShape::userData() const
{
776
    Q_D(const KoShape);
777
    return d->userData.data();
778 779
}

Thomas Zander's avatar
Thomas Zander committed
780
bool KoShape::hasTransparency() const
781
{
Thomas Zander's avatar
Thomas Zander committed
782
    Q_D(const KoShape);
783
    if (! d->fill)
Thomas Zander's avatar
Thomas Zander committed
784
        return true;
785
    else
786 787 788 789 790 791
        return d->fill->hasTransparency() || d->transparency > 0.0;
}

void KoShape::setTransparency(qreal transparency)
{
    Q_D(KoShape);
792
    d->transparency = qBound<qreal>(0.0, transparency, 1.0);
793 794 795

    d->shapeChanged(TransparencyChanged);
    notifyChanged();
796 797 798 799 800 801 802 803 804 805 806 807
}

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
808 809
}

810
KoInsets KoShape::strokeInsets() const
811
{
812
    Q_D(const KoShape);
813
    KoInsets answer;
814 815
    if (d->stroke)
        d->stroke->strokeInsets(this, answer);
816 817 818
    return answer;
}

819 820
qreal KoShape::rotation() const
{
821
    Q_D(const KoShape);
822
    // try to extract the rotation angle out of the local matrix
823 824 825
    // if it is a pure rotation matrix

    // check if the matrix has shearing mixed in
826
    if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
827
        return std::numeric_limits<qreal>::quiet_NaN();
828
    // check if the matrix has scaling mixed in
829
    if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
830
        return std::numeric_limits<qreal>::quiet_NaN();
831 832

    // calculate the angle from the matrix elements
833 834
    qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
    if (angle < 0.0)
835 836 837
        angle += 360.0;

    return angle;
Thomas Zander's avatar
Thomas Zander committed
838 839
}

840
QSizeF KoShape::size() const
841
{
842
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
843 844 845
    return d->size;
}

846 847
QPointF KoShape::position() const
{
848
    Q_D(const KoShape);
849
    QPointF center = outlineRect().center();
850
    return d->localMatrix.map(center) - center;
Thomas Zander's avatar
Thomas Zander committed
851 852
}

853
int KoShape::addConnectionPoint(const KoConnectionPoint &point)
854
{