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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    KIS_SAFE_ASSERT_RECOVER_NOOP(parent != this);

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

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

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

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

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

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

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

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

725
    Q_D(const KoShape);
726

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

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

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

    return QPainterPath();
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 785
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);
}

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

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

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

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

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

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

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

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

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

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

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

868 869
qreal KoShape::rotation() const
{
870
    Q_D(const KoShape);
871
    // try to extract the rotation angle out of the local matrix
872 873 874
    // if it is a pure rotation matrix

    // check if the matrix has shearing mixed in
875
    if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
876
        return std::numeric_limits<qreal>::quiet_NaN();
877
    // check if the matrix has scaling mixed in
878
    if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
879
        return std::numeric_limits<qreal>::quiet_NaN();
880 881

    // calculate the angle from the matrix elements
882 883
    qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
    if (angle < 0.0)
884 885 886
        angle += 360.0;

    return angle;
Thomas Zander's avatar
Thomas Zander committed
887 888
}

889
QSizeF KoShape::size() const
890
{
891
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
892 893 894
    return d->size;
}

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

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

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

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

    return nextConnectionPointId;
}

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

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

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

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

    return true;
}

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

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

966
KoConnectionPoints KoShape::connectionPoints() const