KoShape.cpp 73.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
    Q_FOREACH (KoShapeManager *manager, shapeManagers) {
163
        manager->remove(q);
Thomas Zander's avatar
Thomas Zander committed
164
    }
165

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

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

178
    q->shapeChanged(type);
179 180

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

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

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

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

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

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

221
void KoShapePrivate::convertFromShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
222
{
Jan Hambrecht's avatar
Jan Hambrecht committed
223
    switch(point.alignment) {
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
        case KoConnectionPoint::AlignNone:
            point.position = KoFlake::toRelative(point.position, shapeSize);
            point.position.rx() = qBound<qreal>(0.0, point.position.x(), 1.0);
            point.position.ry() = qBound<qreal>(0.0, point.position.y(), 1.0);
            break;
        case KoConnectionPoint::AlignRight:
            point.position.rx() -= shapeSize.width();
        case KoConnectionPoint::AlignLeft:
            point.position.ry() = 0.5*shapeSize.height();
            break;
        case KoConnectionPoint::AlignBottom:
            point.position.ry() -= shapeSize.height();
        case KoConnectionPoint::AlignTop:
            point.position.rx() = 0.5*shapeSize.width();
            break;
        case KoConnectionPoint::AlignTopLeft:
            // nothing to do here
            break;
        case KoConnectionPoint::AlignTopRight:
            point.position.rx() -= shapeSize.width();
            break;
        case KoConnectionPoint::AlignBottomLeft:
            point.position.ry() -= shapeSize.height();
            break;
        case KoConnectionPoint::AlignBottomRight:
            point.position.rx() -= shapeSize.width();
            point.position.ry() -= shapeSize.height();
            break;
        case KoConnectionPoint::AlignCenter:
            point.position.rx() -= 0.5 * shapeSize.width();
            point.position.ry() -= 0.5 * shapeSize.height();
            break;
    }
}

void KoShapePrivate::convertToShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
{
Jan Hambrecht's avatar
Jan Hambrecht committed
261
    switch(point.alignment) {
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
        case KoConnectionPoint::AlignNone:
            point.position = KoFlake::toAbsolute(point.position, shapeSize);
            break;
        case KoConnectionPoint::AlignRight:
            point.position.rx() += shapeSize.width();
        case KoConnectionPoint::AlignLeft:
            point.position.ry() = 0.5*shapeSize.height();
            break;
        case KoConnectionPoint::AlignBottom:
            point.position.ry() += shapeSize.height();
        case KoConnectionPoint::AlignTop:
            point.position.rx() = 0.5*shapeSize.width();
            break;
        case KoConnectionPoint::AlignTopLeft:
            // nothing to do here
            break;
        case KoConnectionPoint::AlignTopRight:
            point.position.rx() += shapeSize.width();
            break;
        case KoConnectionPoint::AlignBottomLeft:
            point.position.ry() += shapeSize.height();
            break;
        case KoConnectionPoint::AlignBottomRight:
            point.position.rx() += shapeSize.width();
            point.position.ry() += shapeSize.height();
            break;
        case KoConnectionPoint::AlignCenter:
            point.position.rx() += 0.5 * shapeSize.width();
            point.position.ry() += 0.5 * shapeSize.height();
            break;
    }
293 294
}

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

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

    return value;
}

308 309


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

477
    if (converter) {
478 479 480
        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
481
    }
482 483

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
484 485
}

486
void KoShape::applyAbsoluteTransformation(const QTransform &matrix)
487
{
488
    QTransform globalMatrix = absoluteTransformation(0);
489 490
    // 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
491
    // to be relative to the local coordinate system
492
    QTransform transformMatrix = globalMatrix * matrix * globalMatrix.inverted();
493
    applyTransformation(transformMatrix);
494 495
}

496
void KoShape::applyTransformation(const QTransform &matrix)
497
{
498
    Q_D(KoShape);
499
    d->localMatrix = matrix * d->localMatrix;
500
    notifyChanged();
501
    d->shapeChanged(GenericMatrixChange);
502 503
}

504
void KoShape::setTransformation(const QTransform &matrix)
505
{
506
    Q_D(KoShape);
507 508
    d->localMatrix = matrix;
    notifyChanged();
509
    d->shapeChanged(GenericMatrixChange);
510
}
Thomas Zander's avatar
Thomas Zander committed
511

512
QTransform KoShape::transformation() const
513
{
514
    Q_D(const KoShape);
515 516 517
    return d->localMatrix;
}

518
KoShape::ChildZOrderPolicy KoShape::childZOrderPolicy()
519
{
520
    return ChildZDefault;
521 522
}

523 524
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
{
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
    // 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
564
    bool foundCommonParent = false;
565 566 567 568
    int index1 = s1->zIndex();
    int index2 = s2->zIndex();
    parentShapeS1 = s1;
    parentShapeS2 = s2;
569 570 571 572 573 574 575 576
    while (parentShapeS1 && !foundCommonParent) {
        parentShapeS2 = s2;
        index2 = parentShapeS2->zIndex();
        while (parentShapeS2) {
            if (parentShapeS2 == parentShapeS1) {
                foundCommonParent = true;
                break;
            }
577
            if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) {
578 579
                index2 = parentShapeS2->zIndex();
            }
580
            parentShapeS2 = parentShapeS2->parent();
581
        }
582 583

        if (!foundCommonParent) {
584
            if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) {
585 586
                index1 = parentShapeS1->zIndex();
            }
587
            parentShapeS1 = parentShapeS1->parent();
588 589
        }
    }
590

591 592 593 594 595 596 597 598
    // If the one shape is a parent/child of the other then sort so.
    if (s1 == parentShapeS2) {
        return true;
    }
    if (s2 == parentShapeS1) {
        return false;
    }

599
    // If we went that far then the z-Index is used for sorting.
600
    return index1 < index2;
Thomas Zander's avatar
Thomas Zander committed
601 602
}

603 604
void KoShape::setParent(KoShapeContainer *parent)
{
605
    Q_D(KoShape);
606 607

    if (d->parent == parent) {
608
        return;
609 610
    }

611 612
    KoShapeContainer *oldParent = d->parent;
    d->parent = 0; // avoids recursive removing
613 614 615 616 617 618 619

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

    KIS_SAFE_ASSERT_RECOVER_NOOP(parent != this);

Thomas Zander's avatar
Thomas Zander committed
620
    if (parent && parent != this) {
Thomas Zander's avatar
Thomas Zander committed
621
        d->parent = parent;
622
        parent->shapeInterface()->addShape(this);
623
    }
624

625
    notifyChanged();
626
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
627 628
}

629 630
int KoShape::zIndex() const
{
631
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
632
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
633 634
}

635 636
void KoShape::update() const
{
637
    Q_D(const KoShape);
638

639 640
    if (!d->shapeManagers.empty()) {
        QRectF rect(boundingRect());
641
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
Jan Hambrecht's avatar
Jan Hambrecht committed
642
            manager->update(rect, this, true);
643
        }
Thomas Zander's avatar
Thomas Zander committed
644 645 646
    }
}

647
void KoShape::update(const QRectF &rect) const
648
{
649 650 651 652 653

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

654
    Q_D(const KoShape);
655

656
    if (!d->shapeManagers.empty() && isVisible()) {
657
        QRectF rc(absoluteTransformation(0).mapRect(rect));
658
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
659
            manager->update(rc);
660 661
        }
    }
Thomas Zander's avatar
Thomas Zander committed
662 663
}

664
QPainterPath KoShape::outline() const
665
{
Thomas Zander's avatar
Thomas Zander committed
666
    QPainterPath path;
667
    path.addRect(outlineRect());
Thomas Zander's avatar
Thomas Zander committed
668 669 670
    return path;
}

671 672
QRectF KoShape::outlineRect() const
{
673
    const QSizeF s = size();
674 675 676 677 678 679 680
    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);
681
    if (d->fill) {
682
        return outline();
683
    }
684 685

    return QPainterPath();
686 687
}

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

715 716
void KoShape::copySettings(const KoShape *shape)
{
717
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
718 719
    d->size = shape->size();
    d->connectors.clear();
720
    Q_FOREACH (const KoConnectionPoint &point, shape->connectionPoints())
Jan Hambrecht's avatar
Jan Hambrecht committed
721
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
722 723
    d->zIndex = shape->zIndex();
    d->visible = shape->isVisible();
724

725 726 727 728 729
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
730

731
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
732 733
    d->protectContent = shape->isContentProtected();
    d->selectable = shape->isSelectable();
Thomas Zander's avatar
Thomas Zander committed
734
    d->keepAspect = shape->keepAspectRatio();
735
    d->localMatrix = shape->d_ptr->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
736 737
}

Thomas Zander's avatar
Thomas Zander committed
738
void KoShape::notifyChanged()
739
{
740
    Q_D(KoShape);
741
    Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
742
        manager->notifyShapeChanged(this);
743 744 745
    }
}

746 747
void KoShape::setUserData(KoShapeUserData *userData)
{
748
    Q_D(KoShape);
749
    d->userData.reset(userData);
750 751
}

752 753
KoShapeUserData *KoShape::userData() const
{
754
    Q_D(const KoShape);
755
    return d->userData.data();
756 757
}

Thomas Zander's avatar
Thomas Zander committed
758
bool KoShape::hasTransparency() const
759
{
Thomas Zander's avatar
Thomas Zander committed
760
    Q_D(const KoShape);
761
    if (! d->fill)
Thomas Zander's avatar
Thomas Zander committed
762
        return true;
763
    else
764 765 766 767 768 769
        return d->fill->hasTransparency() || d->transparency > 0.0;
}

void KoShape::setTransparency(qreal transparency)
{
    Q_D(KoShape);
770
    d->transparency = qBound<qreal>(0.0, transparency, 1.0);
771 772 773

    d->shapeChanged(TransparencyChanged);
    notifyChanged();
774 775 776 777 778 779 780 781 782 783 784 785
}

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
786 787
}

788
KoInsets KoShape::strokeInsets() const
789
{
790
    Q_D(const KoShape);
791
    KoInsets answer;
792 793
    if (d->stroke)
        d->stroke->strokeInsets(this, answer);
794 795 796
    return answer;
}

797 798
qreal KoShape::rotation() const
{
799
    Q_D(const KoShape);
800
    // try to extract the rotation angle out of the local matrix
801 802 803
    // if it is a pure rotation matrix

    // check if the matrix has shearing mixed in
804
    if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
805
        return std::numeric_limits<qreal>::quiet_NaN();
806
    // check if the matrix has scaling mixed in
807
    if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
808
        return std::numeric_limits<qreal>::quiet_NaN();
809 810

    // calculate the angle from the matrix elements
811 812
    qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
    if (angle < 0.0)
813 814 815
        angle += 360.0;

    return angle;
Thomas Zander's avatar
Thomas Zander committed
816 817
}

818
QSizeF KoShape::size() const
819
{
820
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
821 822 823
    return d->size;
}

824 825
QPointF KoShape::position() const
{
826
    Q_D(const KoShape);
827
    QPointF center = outlineRect().center();
828
    return d->localMatrix.map(center) - center;
Thomas Zander's avatar
Thomas Zander committed
829 830
}

831
int KoShape::addConnectionPoint(const KoConnectionPoint &point)
832
{
833
    Q_D(KoShape);
834

835
    // get next glue point id
836
    int nextConnectionPointId = KoConnectionPoint::FirstCustomConnectionPoint;
Jan Hambrecht's avatar
Jan Hambrecht committed
837
    if (d->connectors.size())
838 839
        nextConnectionPointId = qMax(nextConnectionPointId, (--d->connectors.end()).key()+1);

840
    KoConnectionPoint p = point;
841 842
    d->convertFromShapeCoordinates(p, size());
    d->connectors[nextConnectionPointId] = p;
843 844 845 846

    return nextConnectionPointId;
}

847
bool KoShape::setConnectionPoint(int connectionPointId, const KoConnectionPoint &point)
848 849
{
    Q_D(KoShape);
850
    if (connectionPointId < 0)
851 852
        return false;

853 854
    const bool insertPoint = !hasConnectionPoint(connectionPointId);

855
    switch(connectionPointId) {
856 857 858 859 860 861
        case KoConnectionPoint::TopConnectionPoint:
        case KoConnectionPoint::RightConnectionPoint:
        case KoConnectionPoint::BottomConnectionPoint:
        case KoConnectionPoint::LeftConnectionPoint:
        {
            KoConnectionPoint::PointId id = static_cast<KoConnectionPoint::PointId>(connectionPointId);
862
            d->connectors[id] = KoConnectionPoint::defaultConnectionPoint(id);
863
            break;
864
        }
865
        default:
866
        {
867
            KoConnectionPoint p = point;
868 869
            d->convertFromShapeCoordinates(p, size());
            d->connectors[connectionPointId] = p;
870
            break;
871
        }
872 873
    }

874 875
    if(!insertPoint)
        d->shapeChanged(ConnectionPointChanged);
876 877 878 879

    return true;
}

880 881 882 883 884 885
bool KoShape::hasConnectionPoint(int connectionPointId) const
{
    Q_D(const KoShape);
    return d->connectors.contains(connectionPointId);
}

886
KoConnectionPoint KoShape::connectionPoint(int connectionPointId) const
887 888
{
    Q_D(const KoShape);
889
    KoConnectionPoint p = d->connectors.value(connectionPointId, KoConnectionPoint());
890
    // convert glue point to shape coordinates
891
    d->convertToShapeCoordinates(p, size());
892
    return p;
Thomas Zander's avatar
Thomas Zander committed
893 894
}

895
KoConnectionPoints KoShape::connectionPoints() const
896
{
897
    Q_D(const KoShape);
898
    QSizeF s = size();
899 900 901
    KoConnectionPoints points = d->connectors;
    KoConnectionPoints::iterator point = points.begin();
    KoConnectionPoints::iterator lastPoint = points.end();
902
    // convert glue points to shape coordinates
903
    for(; point != lastPoint; ++point) {
904
        d->convertToShapeCoordinates(point.value(), s);
905
    }
906 907

    return points;
908 909
}

910 911 912 913
void KoShape::removeConnectionPoint(int connectionPointId)
{
    Q_D(KoShape);
    d->connectors.remove(connectionPointId);
914
    d->shapeChanged(ConnectionPointChanged);
915 916 917 918 919 920 921 922
}

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

923 924 925 926 927 928
KoShape::TextRunAroundSide KoShape::textRunAroundSide() const
{
    Q_D(const KoShape);
    return d->textRunAroundSide;
}

929
void KoShape::setTextRunAroundSide(TextRunAroundSide side, RunThroughLevel runThrought)
930