KoShape.cpp 76.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>
3
   Copyright (C) 2006-2010 Thomas Zander <zander@kde.org>
4
   Copyright (C) 2006-2010 Thorsten Zachmann <zachmann@kde.org>
5
   Copyright (C) 2007-2009,2011 Jan Hambrecht <jaham@gmx.net>
Boudewijn Rempt's avatar
Boudewijn Rempt committed
6
   CopyRight (C) 2010 Boudewijn Rempt <boud@valdyas.org>
Thomas Zander's avatar
Thomas Zander committed
7 8 9 10 11 12 13 14 15 16 17 18 19 20

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

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

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

24 25
#include <limits>

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

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

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

75 76
#include "kis_assert.h"

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

80 81
// KoShapePrivate

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

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

160 161
KoShapePrivate::~KoShapePrivate()
{
162
    Q_Q(KoShape);
163 164 165 166 167 168 169 170 171 172

    /**
     * The shape must have already been detached from all the parents and
     * shape managers. Otherwise we migh accidentally request some RTTI
     * information, which is not available anymore (we are in d-tor).
     *
     * TL;DR: fix the code that caused this destruction without unparenting
     *        instead of trying to remove these assert!
     */
    KIS_SAFE_ASSERT_RECOVER (!parent) {
173
        parent->removeShape(q);
174 175
    }

176
    KIS_SAFE_ASSERT_RECOVER (shapeManagers.isEmpty()) {
177 178 179 180
        Q_FOREACH (KoShapeManager *manager, shapeManagers) {
            manager->shapeInterface()->notifyShapeDestructed(q);
        }
        shapeManagers.clear();
Thomas Zander's avatar
Thomas Zander committed
181
    }
182

183
    if (shadow && !shadow->deref())
184
        delete shadow;
185
    if (filterEffectStack && !filterEffectStack->deref())
186
        delete filterEffectStack;
187
}
Thomas Zander's avatar
Thomas Zander committed
188

189 190
void KoShapePrivate::shapeChanged(KoShape::ChangeType type)
{
191
    Q_Q(KoShape);
192
    if (parent)
193
        parent->model()->childChanged(q, type);
194

195
    q->shapeChanged(type);
196 197

    Q_FOREACH (KoShape * shape, dependees) {
198
        shape->shapeChanged(type, q);
199 200 201
    }

    Q_FOREACH (KoShape::ShapeChangeListener *listener, listeners) {
202
        listener->notifyShapeChangedImpl(type, q);
203
    }
204
}
205

206 207 208 209 210 211 212 213
void KoShapePrivate::addShapeManager(KoShapeManager *manager)
{
    shapeManagers.insert(manager);
}

void KoShapePrivate::removeShapeManager(KoShapeManager *manager)
{
    shapeManagers.remove(manager);
214 215
}

216
void KoShapePrivate::convertFromShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
217
{
Jan Hambrecht's avatar
Jan Hambrecht committed
218
    switch(point.alignment) {
219 220 221 222 223 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
    case KoConnectionPoint::AlignNone:
        point.position = KoFlake::toRelative(point.position, shapeSize);
        point.position.rx() = qBound<qreal>(0.0, point.position.x(), 1.0);
        point.position.ry() = qBound<qreal>(0.0, point.position.y(), 1.0);
        break;
    case KoConnectionPoint::AlignRight:
        point.position.rx() -= shapeSize.width();
        break;
    case KoConnectionPoint::AlignLeft:
        point.position.ry() = 0.5*shapeSize.height();
        break;
    case KoConnectionPoint::AlignBottom:
        point.position.ry() -= shapeSize.height();
        break;
    case KoConnectionPoint::AlignTop:
        point.position.rx() = 0.5*shapeSize.width();
        break;
    case KoConnectionPoint::AlignTopLeft:
        // nothing to do here
        break;
    case KoConnectionPoint::AlignTopRight:
        point.position.rx() -= shapeSize.width();
        break;
    case KoConnectionPoint::AlignBottomLeft:
        point.position.ry() -= shapeSize.height();
        break;
    case KoConnectionPoint::AlignBottomRight:
        point.position.rx() -= shapeSize.width();
        point.position.ry() -= shapeSize.height();
        break;
    case KoConnectionPoint::AlignCenter:
        point.position.rx() -= 0.5 * shapeSize.width();
        point.position.ry() -= 0.5 * shapeSize.height();
        break;
253 254 255 256 257
    }
}

void KoShapePrivate::convertToShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
{
Jan Hambrecht's avatar
Jan Hambrecht committed
258
    switch(point.alignment) {
259 260 261 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
    case KoConnectionPoint::AlignNone:
        point.position = KoFlake::toAbsolute(point.position, shapeSize);
        break;
    case KoConnectionPoint::AlignRight:
        point.position.rx() += shapeSize.width();
        break;
    case KoConnectionPoint::AlignLeft:
        point.position.ry() = 0.5*shapeSize.height();
        break;
    case KoConnectionPoint::AlignBottom:
        point.position.ry() += shapeSize.height();
        break;
    case KoConnectionPoint::AlignTop:
        point.position.rx() = 0.5*shapeSize.width();
        break;
    case KoConnectionPoint::AlignTopLeft:
        // nothing to do here
        break;
    case KoConnectionPoint::AlignTopRight:
        point.position.rx() += shapeSize.width();
        break;
    case KoConnectionPoint::AlignBottomLeft:
        point.position.ry() += shapeSize.height();
        break;
    case KoConnectionPoint::AlignBottomRight:
        point.position.rx() += shapeSize.width();
        point.position.ry() += shapeSize.height();
        break;
    case KoConnectionPoint::AlignCenter:
        point.position.rx() += 0.5 * shapeSize.width();
        point.position.ry() += 0.5 * shapeSize.height();
        break;
291
    }
292 293
}

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

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

    return value;
}

307 308


309
// ======== KoShape
310 311 312 313

const qint16 KoShape::maxZIndex = std::numeric_limits<qint16>::max();
const qint16 KoShape::minZIndex = std::numeric_limits<qint16>::min();

Thomas Zander's avatar
Thomas Zander committed
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
    d->listeners.clear();
330
    delete d_ptr;
Thomas Zander's avatar
Thomas Zander committed
331 332
}

333 334
KoShape *KoShape::cloneShape() const
{
335
    KIS_SAFE_ASSERT_RECOVER_NOOP(0 && "not implemented!");
336
    qWarning() << shapeId() << "cannot be cloned";
337 338 339
    return 0;
}

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

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

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

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

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

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

391
void KoShape::setSize(const QSizeF &newSize)
Thomas Zander's avatar
Thomas Zander committed
392
{
393
    Q_D(KoShape);
394
    QSizeF oldSize(size());
395

396
    // always set size, as d->size and size() may vary
Thomas Zander's avatar
Thomas Zander committed
397
    d->size = newSize;
398

399 400 401
    if (oldSize == newSize)
        return;

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

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

    notifyChanged();
417
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
418 419
}

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

426
    QPointF point = absoluteTransformation(0).inverted().map(position);
427 428
    QRectF bb = outlineRect();

429
    if (d->stroke) {
430
        KoInsets insets;
431
        d->stroke->strokeInsets(this, insets);
432
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
433
    }
434 435 436
    if (bb.contains(point))
        return true;

437
    // if there is no shadow we can as well just leave
438
    if (! d->shadow)
439
        return false;
Thomas Zander's avatar
Thomas Zander committed
440

441 442
    // the shadow has an offset to the shape, so we simply
    // check if the position minus the shadow offset hits the shape
443
    point = absoluteTransformation(0).inverted().map(position - d->shadow->offset());
444

445
    return bb.contains(point);
Thomas Zander's avatar
Thomas Zander committed
446 447
}

448
QRectF KoShape::boundingRect() const
Thomas Zander's avatar
Thomas Zander committed
449
{
450
    Q_D(const KoShape);
Jan Hambrecht's avatar
Jan Hambrecht committed
451

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

470
    return bb;
Thomas Zander's avatar
Thomas Zander committed
471 472
}

473 474 475 476 477 478 479 480 481
QRectF KoShape::boundingRect(const QList<KoShape *> &shapes)
{
    QRectF boundingRect;
    Q_FOREACH (KoShape *shape, shapes) {
        boundingRect |= shape->boundingRect();
    }
    return boundingRect;
}

482 483 484 485 486 487 488 489 490 491 492 493 494 495
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;
}

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

516
    if (converter) {
517 518 519
        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
520
    }
521 522

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
523 524
}

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

535
void KoShape::applyTransformation(const QTransform &matrix)
536
{
537
    Q_D(KoShape);
538
    d->localMatrix = matrix * d->localMatrix;
539
    notifyChanged();
540
    d->shapeChanged(GenericMatrixChange);
541 542
}

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

551
QTransform KoShape::transformation() const
552
{
553
    Q_D(const KoShape);
554 555 556
    return d->localMatrix;
}

557
KoShape::ChildZOrderPolicy KoShape::childZOrderPolicy()
558
{
559
    return ChildZDefault;
560 561
}

562 563
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
{
564 565 566 567 568 569
    /**
     * WARNING: Our definition of zIndex is not yet compatible with SVG2's
     *          definition. In SVG stacking context of groups with the same
     *          zIndex are **merged**, while in Krita the contents of groups
     *          is never merged. One group will always below than the other.
     *          Therefore, when zIndex of two groups inside the same parent
Boudewijn Rempt's avatar
Boudewijn Rempt committed
570
     *          coincide, the resulting painting order in Krita is
571 572 573 574 575 576
     *          **UNDEFINED**.
     *
     *          To avoid this trouble we use  KoShapeReorderCommand::mergeInShape()
     *          inside KoShapeCreateCommand.
     */

577
    /**
Boudewijn Rempt's avatar
Boudewijn Rempt committed
578 579
     * The algorithm below doesn't correctly handle the case when the two pointers actually
     * point to the same shape. So just check it in advance to guarantee strict weak ordering
580 581 582 583 584
     * relation requirement
     */
    if (s1 == s2) return false;


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

        if (!foundCommonParent) {
644
            if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) {
645 646
                index1 = parentShapeS1->zIndex();
            }
647
            parentShapeS1 = parentShapeS1->parent();
648 649
        }
    }
650

651 652 653 654 655 656 657 658
    // If the one shape is a parent/child of the other then sort so.
    if (s1 == parentShapeS2) {
        return true;
    }
    if (s2 == parentShapeS1) {
        return false;
    }

659
    // If we went that far then the z-Index is used for sorting.
660
    return index1 < index2;
Thomas Zander's avatar
Thomas Zander committed
661 662
}

663 664
void KoShape::setParent(KoShapeContainer *parent)
{
665
    Q_D(KoShape);
666 667

    if (d->parent == parent) {
668
        return;
669 670
    }

671 672
    KoShapeContainer *oldParent = d->parent;
    d->parent = 0; // avoids recursive removing
673 674 675 676 677 678 679

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

    KIS_SAFE_ASSERT_RECOVER_NOOP(parent != this);

Thomas Zander's avatar
Thomas Zander committed
680
    if (parent && parent != this) {
Thomas Zander's avatar
Thomas Zander committed
681
        d->parent = parent;
682
        parent->shapeInterface()->addShape(this);
683
    }
684

685
    notifyChanged();
686
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
687 688
}

689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710
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;
}

711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729
bool KoShape::hasCommonParent(const KoShape *shape) const
{
    const KoShape *thisShape = this;
    while (thisShape) {

        const KoShape *otherShape = shape;
        while (otherShape) {
            if (thisShape == otherShape) {
                return true;
            }
            otherShape = otherShape->parent();
        }

        thisShape = thisShape->parent();
    }

    return false;
}

730
qint16 KoShape::zIndex() const
731
{
732
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
733
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
734 735
}

736 737
void KoShape::update() const
{
738
    Q_D(const KoShape);
739

740 741
    if (!d->shapeManagers.empty()) {
        QRectF rect(boundingRect());
742
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
Jan Hambrecht's avatar
Jan Hambrecht committed
743
            manager->update(rect, this, true);
744
        }
Thomas Zander's avatar
Thomas Zander committed
745 746 747
    }
}

748
void KoShape::updateAbsolute(const QRectF &rect) const
749
{
750 751 752 753
    if (rect.isEmpty() && !rect.isNull()) {
        return;
    }

754
    Q_D(const KoShape);
755

756
    if (!d->shapeManagers.empty() && isVisible()) {
757
        Q_FOREACH (KoShapeManager *manager, d->shapeManagers) {
758
            manager->update(rect);
759 760
        }
    }
Thomas Zander's avatar
Thomas Zander committed
761 762
}

763
QPainterPath KoShape::outline() const
764
{
Thomas Zander's avatar
Thomas Zander committed
765
    QPainterPath path;
766
    path.addRect(outlineRect());
Thomas Zander's avatar
Thomas Zander committed
767 768 769
    return path;
}

770 771
QRectF KoShape::outlineRect() const
{
772
    const QSizeF s = size();
773 774 775 776 777 778
    return QRectF(QPointF(0, 0), QSizeF(qMax(s.width(),  qreal(0.0001)),
                                        qMax(s.height(), qreal(0.0001))));
}

QPainterPath KoShape::shadowOutline() const
{
779
    if (background()) {
780
        return outline();
781
    }
782 783

    return QPainterPath();
784 785
}

786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812
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);
}

813 814
void KoShape::copySettings(const KoShape *shape)
{
815
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
816 817
    d->size = shape->size();
    d->connectors.clear();
818
    Q_FOREACH (const KoConnectionPoint &point, shape->connectionPoints())
Jan Hambrecht's avatar
Jan Hambrecht committed
819
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
820
    d->zIndex = shape->zIndex();
821
    d->visible = shape->isVisible(false);
822

823 824 825 826 827
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
828

829
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
830 831
    d->protectContent = shape->isContentProtected();
    d->selectable = shape->isSelectable();
Thomas Zander's avatar
Thomas Zander committed
832
    d->keepAspect = shape->keepAspectRatio();
833
    d->localMatrix = shape->d_ptr->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
834 835
}

Thomas Zander's avatar
Thomas Zander committed
836
void KoShape::notifyChanged()
837
{
838
    Q_D(KoShape);
839
    Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
840
        manager->notifyShapeChanged(this);
841 842 843
    }
}

844 845
void KoShape::setUserData(KoShapeUserData *userData)
{
846
    Q_D(KoShape);
847
    d->userData.reset(userData);
848 849
}

850 851
KoShapeUserData *KoShape::userData() const
{
852
    Q_D(const KoShape);
853
    return d->userData.data();
854 855
}

Thomas Zander's avatar
Thomas Zander committed
856
bool KoShape::hasTransparency() const
857
{
Thomas Zander's avatar
Thomas Zander committed
858
    Q_D(const KoShape);
859 860 861
    QSharedPointer<KoShapeBackground> bg = background();

    return !bg || bg->hasTransparency() || d->transparency > 0.0;
862 863 864 865 866
}

void KoShape::setTransparency(qreal transparency)
{
    Q_D(KoShape);
867
    d->transparency = qBound<qreal>(0.0, transparency, 1.0);
868 869 870

    d->shapeChanged(TransparencyChanged);
    notifyChanged();
871 872 873 874 875 876 877 878 879 880 881 882
}

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
883 884
}

885
KoInsets KoShape::strokeInsets() const
886
{
887
    Q_D(const KoShape);
888
    KoInsets answer;
889 890
    if (d->stroke)
        d->stroke->strokeInsets(this, answer);
891 892 893
    return answer;
}

894 895
qreal KoShape::rotation() const
{
896
    Q_D(const KoShape);
897
    // try to extract the rotation angle out of the local matrix
898 899 900
    // if it is a pure rotation matrix

    // check if the matrix has shearing mixed in
901
    if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
902
        return std::numeric_limits<qreal>::quiet_NaN();
903
    // check if the matrix has scaling mixed in
904
    if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
905
        return std::numeric_limits<qreal>::quiet_NaN();
906 907

    // calculate the angle from the matrix elements
908 909
    qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
    if (angle < 0.0)
910 911 912
        angle += 360.0;

    return angle;
Thomas Zander's avatar
Thomas Zander committed
913 914
}

915
QSizeF KoShape::size() const
916
{
917
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
918 919 920
    return d->size;
}

921 922
QPointF KoShape::position() const
{
923
    Q_D(const KoShape);
924
    QPointF center = outlineRect().center();
925
    return d->localMatrix.map(center) - center;
Thomas Zander's avatar
Thomas Zander committed
926 927
}

928
int KoShape::addConnectionPoint(const KoConnectionPoint &point)
929
{
930
    Q_D(KoShape);
931

932
    // get next glue point id
933
    int nextConnectionPointId = KoConnectionPoint::FirstCustomConnectionPoint;
Jan Hambrecht's avatar
Jan Hambrecht committed
934
    if (d->connectors.size())
935 936
        nextConnectionPointId = qMax(nextConnectionPointId, (--d->connectors.end()).key()+1);

937
    KoConnectionPoint p = point;
938 939
    d->convertFromShapeCoordinates(p, size());
    d->connectors[nextConnectionPointId] = p;
940 941 942 943

    return nextConnectionPointId;
}

944
bool KoShape::setConnectionPoint(int connectionPointId, const KoConnectionPoint &point)
945 946
{
    Q_D(KoShape);
947
    if (connectionPointId < 0)
948 949
        return false;

950 951
    const bool insertPoint = !hasConnectionPoint(connectionPointId);

952
    switch(connectionPointId) {
953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968
    case KoConnectionPoint::TopConnectionPoint:
    case KoConnectionPoint::RightConnectionPoint:
    case KoConnectionPoint::BottomConnectionPoint:
    case KoConnectionPoint::LeftConnectionPoint:
    {
        KoConnectionPoint::PointId id = static_cast<KoConnectionPoint::PointId>(connectionPointId);
        d->connectors[id] = KoConnectionPoint::defaultConnectionPoint(id);
        break;
    }
    default:
    {
        KoConnectionPoint p = point;
        d->convertFromShapeCoordinates(p, size());
        d->connectors[connectionPointId] = p;
        break;
    }
969 970
    }

971 972
    if(!insertPoint)
        d->shapeChanged(ConnectionPointChanged);
973 974 975 976

    return true;
}

977 978 979 980 981 982
bool KoShape::hasConnectionPoint(int connectionPointId) const
{
    Q_D(const KoShape);
    return d->connectors.contains(connectionPointId);
}

983
KoConnectionPoint KoShape::connectionPoint(int connectionPointId) const
984 985
{
    Q_D(const KoShape);
986
    KoConnectionPoint p = d->connectors.value(connectionPointId, KoConnectionPoint());
987
    // convert glue point to shape coordinates
988
    d->convertToShapeCoordinates(p, size());
989
    return p;
Thomas Zander's avatar
Thomas Zander committed
990 991
}

992
KoConnectionPoints KoShape::connectionPoints() const
993
{
994
    Q_D(const KoShape);
995
    QSizeF s = size();
996 997 998
    KoConnectionPoints points = d->connectors;
    KoConnectionPoints::iterator point = points.begin();
    KoConnectionPoints::iterator lastPoint = points.end();
999
    // convert glue points to shape coordinates