KoShape.cpp 71.4 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
#include <limits>
74
#include "KoOdfGradientBackground.h"
75

76 77
// KoShapePrivate

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

110 111 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
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)
{
}

154 155
KoShapePrivate::~KoShapePrivate()
{
156
    Q_Q(KoShape);
157
    if (parent)
158
        parent->removeShape(q);
159
    Q_FOREACH (KoShapeManager *manager, shapeManagers) {
160 161
        manager->remove(q);
        manager->removeAdditional(q);
Thomas Zander's avatar
Thomas Zander committed
162
    }
163

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

170 171
void KoShapePrivate::shapeChanged(KoShape::ChangeType type)
{
172
    Q_Q(KoShape);
173
    if (parent)
174 175
        parent->model()->childChanged(q, type);
    q->shapeChanged(type);
176
    Q_FOREACH (KoShape * shape, dependees)
177
        shape->shapeChanged(type, q);
178
}
179

180
void KoShapePrivate::updateStroke()
181
{
182
    Q_Q(KoShape);
183 184
    if (!stroke) return;

185
    KoInsets insets;
186
    stroke->strokeInsets(q, insets);
187 188 189
    QSizeF inner = q->size();
    // update left
    q->update(QRectF(-insets.left, -insets.top, insets.left,
190
                     inner.height() + insets.top + insets.bottom));
191 192
    // update top
    q->update(QRectF(-insets.left, -insets.top,
193
                     inner.width() + insets.left + insets.right, insets.top));
194 195
    // update right
    q->update(QRectF(inner.width(), -insets.top, insets.right,
196
                     inner.height() + insets.top + insets.bottom));
197 198
    // update bottom
    q->update(QRectF(-insets.left, inner.height(),
199
                     inner.width() + insets.left + insets.right, insets.bottom));
200
}
Thomas Zander's avatar
Thomas Zander committed
201

202 203 204 205 206 207 208 209
void KoShapePrivate::addShapeManager(KoShapeManager *manager)
{
    shapeManagers.insert(manager);
}

void KoShapePrivate::removeShapeManager(KoShapeManager *manager)
{
    shapeManagers.remove(manager);
210 211
}

212
void KoShapePrivate::convertFromShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
213
{
Jan Hambrecht's avatar
Jan Hambrecht committed
214
    switch(point.alignment) {
215 216 217 218 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
        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
252
    switch(point.alignment) {
253 254 255 256 257 258 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
        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;
    }
284 285
}

286
// static
287
QString KoShapePrivate::getStyleProperty(const char *property, KoShapeLoadingContext &context)
288 289 290 291 292 293 294 295 296 297 298
{
    KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
    QString value;

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

    return value;
}

299 300


301
// ======== KoShape
Thomas Zander's avatar
Thomas Zander committed
302
KoShape::KoShape()
303
    : d_ptr(new KoShapePrivate(this))
Thomas Zander's avatar
Thomas Zander committed
304
{
305
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
306 307
}

308 309
KoShape::KoShape(KoShapePrivate *dd)
    : d_ptr(dd)
310 311 312
{
}

Thomas Zander's avatar
Thomas Zander committed
313 314
KoShape::~KoShape()
{
315
    Q_D(KoShape);
316
    d->shapeChanged(Deleted);
317
    delete d_ptr;
Thomas Zander's avatar
Thomas Zander committed
318 319
}

320 321 322 323 324
KoShape *KoShape::cloneShape() const
{
    return 0;
}

Thomas Zander's avatar
Thomas Zander committed
325
void KoShape::scale(qreal sx, qreal sy)
Thomas Zander's avatar
Thomas Zander committed
326
{
327
    Q_D(KoShape);
328
    QPointF pos = position();
329
    QTransform scaleMatrix;
330 331 332
    scaleMatrix.translate(pos.x(), pos.y());
    scaleMatrix.scale(sx, sy);
    scaleMatrix.translate(-pos.x(), -pos.y());
333
    d->localMatrix = d->localMatrix * scaleMatrix;
334

335
    notifyChanged();
336
    d->shapeChanged(ScaleChanged);
Thomas Zander's avatar
Thomas Zander committed
337 338
}

339
void KoShape::rotate(qreal angle)
Thomas Zander's avatar
Thomas Zander committed
340
{
341
    Q_D(KoShape);
342
    QPointF center = d->localMatrix.map(QPointF(0.5 * size().width(), 0.5 * size().height()));
343
    QTransform rotateMatrix;
344 345 346
    rotateMatrix.translate(center.x(), center.y());
    rotateMatrix.rotate(angle);
    rotateMatrix.translate(-center.x(), -center.y());
347
    d->localMatrix = d->localMatrix * rotateMatrix;
348

349
    notifyChanged();
350
    d->shapeChanged(RotationChanged);
Thomas Zander's avatar
Thomas Zander committed
351 352
}

Thomas Zander's avatar
Thomas Zander committed
353
void KoShape::shear(qreal sx, qreal sy)
Thomas Zander's avatar
Thomas Zander committed
354
{
355
    Q_D(KoShape);
356
    QPointF pos = position();
357
    QTransform shearMatrix;
358 359 360
    shearMatrix.translate(pos.x(), pos.y());
    shearMatrix.shear(sx, sy);
    shearMatrix.translate(-pos.x(), -pos.y());
361
    d->localMatrix = d->localMatrix * shearMatrix;
362

363
    notifyChanged();
364
    d->shapeChanged(ShearChanged);
Thomas Zander's avatar
Thomas Zander committed
365 366
}

367
void KoShape::setSize(const QSizeF &newSize)
Thomas Zander's avatar
Thomas Zander committed
368
{
369
    Q_D(KoShape);
370
    QSizeF oldSize(size());
371

372
    // always set size, as d->size and size() may vary
Thomas Zander's avatar
Thomas Zander committed
373
    d->size = newSize;
374

375 376 377
    if (oldSize == newSize)
        return;

378
    notifyChanged();
379
    d->shapeChanged(SizeChanged);
Thomas Zander's avatar
Thomas Zander committed
380 381
}

382
void KoShape::setPosition(const QPointF &newPosition)
Thomas Zander's avatar
Thomas Zander committed
383
{
384
    Q_D(KoShape);
385
    QPointF currentPos = position();
386
    if (newPosition == currentPos)
Thomas Zander's avatar
Thomas Zander committed
387
        return;
388
    QTransform translateMatrix;
389
    translateMatrix.translate(newPosition.x() - currentPos.x(), newPosition.y() - currentPos.y());
390 391 392
    d->localMatrix = d->localMatrix * translateMatrix;

    notifyChanged();
393
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
394 395
}

396
bool KoShape::hitTest(const QPointF &position) const
Thomas Zander's avatar
Thomas Zander committed
397
{
398
    Q_D(const KoShape);
399
    if (d->parent && d->parent->isClipped(this) && !d->parent->hitTest(position))
Thomas Zander's avatar
Thomas Zander committed
400 401
        return false;

402
    QPointF point = absoluteTransformation(0).inverted().map(position);
403
    QRectF bb(QPointF(), size());
404
    if (d->stroke) {
405
        KoInsets insets;
406
        d->stroke->strokeInsets(this, insets);
407
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
408
    }
409 410 411
    if (bb.contains(point))
        return true;

412
    // if there is no shadow we can as well just leave
413
    if (! d->shadow)
414
        return false;
Thomas Zander's avatar
Thomas Zander committed
415

416 417
    // the shadow has an offset to the shape, so we simply
    // check if the position minus the shadow offset hits the shape
418
    point = absoluteTransformation(0).inverted().map(position - d->shadow->offset());
419

420
    return bb.contains(point);
Thomas Zander's avatar
Thomas Zander committed
421 422
}

423
QRectF KoShape::boundingRect() const
Thomas Zander's avatar
Thomas Zander committed
424
{
425
    Q_D(const KoShape);
Jan Hambrecht's avatar
Jan Hambrecht committed
426

427
    QTransform transform = absoluteTransformation(0);
428
    QRectF bb = outlineRect();
429
    if (d->stroke) {
430
        KoInsets insets;
431
        d->stroke->strokeInsets(this, insets);
432 433
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
434
    bb = transform.mapRect(bb);
435
    if (d->shadow) {
436
        KoInsets insets;
Thomas Zander's avatar
Thomas Zander committed
437
        d->shadow->insets(insets);
438 439
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
440
    if (d->filterEffectStack) {
441
        QRectF clipRect = d->filterEffectStack->clipRectForBoundingRect(outlineRect());
442 443
        bb |= transform.mapRect(clipRect);
    }
Thomas Zander's avatar
Thomas Zander committed
444

445
    return bb;
Thomas Zander's avatar
Thomas Zander committed
446 447
}

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

468
    if (converter) {
469 470 471
        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
472
    }
473 474

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
475 476
}

477
void KoShape::applyAbsoluteTransformation(const QTransform &matrix)
478
{
479
    QTransform globalMatrix = absoluteTransformation(0);
480 481
    // 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
482
    // to be relative to the local coordinate system
483
    QTransform transformMatrix = globalMatrix * matrix * globalMatrix.inverted();
484
    applyTransformation(transformMatrix);
485 486
}

487
void KoShape::applyTransformation(const QTransform &matrix)
488
{
489
    Q_D(KoShape);
490
    d->localMatrix = matrix * d->localMatrix;
491
    notifyChanged();
492
    d->shapeChanged(GenericMatrixChange);
493 494
}

495
void KoShape::setTransformation(const QTransform &matrix)
496
{
497
    Q_D(KoShape);
498 499
    d->localMatrix = matrix;
    notifyChanged();
500
    d->shapeChanged(GenericMatrixChange);
501
}
Thomas Zander's avatar
Thomas Zander committed
502

503
QTransform KoShape::transformation() const
504
{
505
    Q_D(const KoShape);
506 507 508
    return d->localMatrix;
}

509
KoShape::ChildZOrderPolicy KoShape::childZOrderPolicy()
510
{
511
    return ChildZDefault;
512 513
}

514 515
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
{
516 517 518 519 520 521 522 523 524 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
    // 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
555
    bool foundCommonParent = false;
556 557 558 559
    int index1 = s1->zIndex();
    int index2 = s2->zIndex();
    parentShapeS1 = s1;
    parentShapeS2 = s2;
560 561 562 563 564 565 566 567
    while (parentShapeS1 && !foundCommonParent) {
        parentShapeS2 = s2;
        index2 = parentShapeS2->zIndex();
        while (parentShapeS2) {
            if (parentShapeS2 == parentShapeS1) {
                foundCommonParent = true;
                break;
            }
568
            if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) {
569 570
                index2 = parentShapeS2->zIndex();
            }
571
            parentShapeS2 = parentShapeS2->parent();
572
        }
573 574

        if (!foundCommonParent) {
575
            if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) {
576 577
                index1 = parentShapeS1->zIndex();
            }
578
            parentShapeS1 = parentShapeS1->parent();
579 580
        }
    }
581

582 583 584 585 586 587 588 589
    // If the one shape is a parent/child of the other then sort so.
    if (s1 == parentShapeS2) {
        return true;
    }
    if (s2 == parentShapeS1) {
        return false;
    }

590
    // If we went that far then the z-Index is used for sorting.
591
    return index1 < index2;
Thomas Zander's avatar
Thomas Zander committed
592 593
}

594 595
void KoShape::setParent(KoShapeContainer *parent)
{
596
    Q_D(KoShape);
597
    if (d->parent == parent)
598
        return;
599 600 601
    KoShapeContainer *oldParent = d->parent;
    d->parent = 0; // avoids recursive removing
    if (oldParent)
602
        oldParent->removeShape(this);
Thomas Zander's avatar
Thomas Zander committed
603
    if (parent && parent != this) {
Thomas Zander's avatar
Thomas Zander committed
604
        d->parent = parent;
605
        parent->addShape(this);
606
    }
607
    notifyChanged();
608
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
609 610
}

611 612
int KoShape::zIndex() const
{
613
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
614
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
615 616
}

617 618
void KoShape::update() const
{
619
    Q_D(const KoShape);
620

621 622
    if (!d->shapeManagers.empty()) {
        QRectF rect(boundingRect());
623
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
Jan Hambrecht's avatar
Jan Hambrecht committed
624
            manager->update(rect, this, true);
625
        }
Thomas Zander's avatar
Thomas Zander committed
626 627 628
    }
}

629
void KoShape::update(const QRectF &rect) const
630
{
631 632 633 634 635

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

636
    Q_D(const KoShape);
637

638
    if (!d->shapeManagers.empty() && isVisible()) {
639
        QRectF rc(absoluteTransformation(0).mapRect(rect));
640
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
641
            manager->update(rc);
642 643
        }
    }
Thomas Zander's avatar
Thomas Zander committed
644 645
}

646
QPainterPath KoShape::outline() const
647
{
Thomas Zander's avatar
Thomas Zander committed
648
    QPainterPath path;
649
    path.addRect(outlineRect());
Thomas Zander's avatar
Thomas Zander committed
650 651 652
    return path;
}

653 654
QRectF KoShape::outlineRect() const
{
655
    const QSizeF s = size();
656 657 658 659 660 661 662
    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);
663
    if (d->fill) {
664
        return outline();
665
    }
666 667

    return QPainterPath();
668 669
}

670 671
QPointF KoShape::absolutePosition(KoFlake::Position anchor) const
{
672
    QPointF point;
673 674 675 676 677 678
    switch (anchor) {
    case KoFlake::TopLeftCorner: break;
    case KoFlake::TopRightCorner: point = QPointF(size().width(), 0.0); break;
    case KoFlake::BottomLeftCorner: point = QPointF(0.0, size().height()); break;
    case KoFlake::BottomRightCorner: point = QPointF(size().width(), size().height()); break;
    case KoFlake::CenteredPosition: point = QPointF(size().width() / 2.0, size().height() / 2.0); break;
679
    }
680
    return absoluteTransformation(0).map(point);
Thomas Zander's avatar
Thomas Zander committed
681 682
}

Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
683
void KoShape::setAbsolutePosition(const QPointF &newPosition, KoFlake::Position anchor)
684
{
685
    Q_D(KoShape);
686
    QPointF currentAbsPosition = absolutePosition(anchor);
687
    QPointF translate = newPosition - currentAbsPosition;
688
    QTransform translateMatrix;
689 690
    translateMatrix.translate(translate.x(), translate.y());
    applyAbsoluteTransformation(translateMatrix);
691
    notifyChanged();
692
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
693 694
}

695 696
void KoShape::copySettings(const KoShape *shape)
{
697
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
698 699
    d->size = shape->size();
    d->connectors.clear();
700
    Q_FOREACH (const KoConnectionPoint &point, shape->connectionPoints())
Jan Hambrecht's avatar
Jan Hambrecht committed
701
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
702 703
    d->zIndex = shape->zIndex();
    d->visible = shape->isVisible();
704

705 706 707 708 709
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
710

711
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
712 713
    d->protectContent = shape->isContentProtected();
    d->selectable = shape->isSelectable();
Thomas Zander's avatar
Thomas Zander committed
714
    d->keepAspect = shape->keepAspectRatio();
715
    d->localMatrix = shape->d_ptr->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
716 717
}

Thomas Zander's avatar
Thomas Zander committed
718
void KoShape::notifyChanged()
719
{
720
    Q_D(KoShape);
721
    Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
722
        manager->notifyShapeChanged(this);
723 724 725
    }
}

726 727
void KoShape::setUserData(KoShapeUserData *userData)
{
728
    Q_D(KoShape);
729
    d->userData.reset(userData);
730 731
}

732 733
KoShapeUserData *KoShape::userData() const
{
734
    Q_D(const KoShape);
735
    return d->userData.data();
736 737
}

Thomas Zander's avatar
Thomas Zander committed
738
bool KoShape::hasTransparency() const
739
{
Thomas Zander's avatar
Thomas Zander committed
740
    Q_D(const KoShape);
741
    if (! d->fill)
Thomas Zander's avatar
Thomas Zander committed
742
        return true;
743
    else
744 745 746 747 748 749
        return d->fill->hasTransparency() || d->transparency > 0.0;
}

void KoShape::setTransparency(qreal transparency)
{
    Q_D(KoShape);
750
    d->transparency = qBound<qreal>(0.0, transparency, 1.0);
751 752 753 754 755 756 757 758 759 760 761 762
}

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
763 764
}

765
KoInsets KoShape::strokeInsets() const
766
{
767
    Q_D(const KoShape);
768
    KoInsets answer;
769 770
    if (d->stroke)
        d->stroke->strokeInsets(this, answer);
771 772 773
    return answer;
}

774 775
qreal KoShape::rotation() const
{
776
    Q_D(const KoShape);
777
    // try to extract the rotation angle out of the local matrix
778 779 780
    // if it is a pure rotation matrix

    // check if the matrix has shearing mixed in
781
    if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
782
        return std::numeric_limits<qreal>::quiet_NaN();
783
    // check if the matrix has scaling mixed in
784
    if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
785
        return std::numeric_limits<qreal>::quiet_NaN();
786 787

    // calculate the angle from the matrix elements
788 789
    qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
    if (angle < 0.0)
790 791 792
        angle += 360.0;

    return angle;
Thomas Zander's avatar
Thomas Zander committed
793 794
}

795
QSizeF KoShape::size() const
796
{
797
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
798 799 800
    return d->size;
}

801 802
QPointF KoShape::position() const
{
803
    Q_D(const KoShape);
804 805
    QPointF center(0.5*size().width(), 0.5*size().height());
    return d->localMatrix.map(center) - center;
Thomas Zander's avatar
Thomas Zander committed
806 807
}

808
int KoShape::addConnectionPoint(const KoConnectionPoint &point)
809
{
810
    Q_D(KoShape);
811

812
    // get next glue point id
813
    int nextConnectionPointId = KoConnectionPoint::FirstCustomConnectionPoint;
Jan Hambrecht's avatar
Jan Hambrecht committed
814
    if (d->connectors.size())
815 816
        nextConnectionPointId = qMax(nextConnectionPointId, (--d->connectors.end()).key()+1);

817
    KoConnectionPoint p = point;
818 819
    d->convertFromShapeCoordinates(p, size());
    d->connectors[nextConnectionPointId] = p;
820 821 822 823

    return nextConnectionPointId;
}

824
bool KoShape::setConnectionPoint(int connectionPointId, const KoConnectionPoint &point)
825 826
{
    Q_D(KoShape);
827
    if (connectionPointId < 0)
828 829
        return false;

830 831
    const bool insertPoint = !hasConnectionPoint(connectionPointId);

832
    switch(connectionPointId) {
833 834 835 836 837 838
        case KoConnectionPoint::TopConnectionPoint:
        case KoConnectionPoint::RightConnectionPoint:
        case KoConnectionPoint::BottomConnectionPoint:
        case KoConnectionPoint::LeftConnectionPoint:
        {
            KoConnectionPoint::PointId id = static_cast<KoConnectionPoint::PointId>(connectionPointId);
839
            d->connectors[id] = KoConnectionPoint::defaultConnectionPoint(id);
840
            break;
841
        }
842
        default:
843
        {
844
            KoConnectionPoint p = point;
845 846
            d->convertFromShapeCoordinates(p, size());
            d->connectors[connectionPointId] = p;
847
            break;
848
        }
849 850
    }

851 852
    if(!insertPoint)
        d->shapeChanged(ConnectionPointChanged);
853 854 855 856

    return true;
}

857 858 859 860 861 862
bool KoShape::hasConnectionPoint(int connectionPointId) const
{
    Q_D(const KoShape);
    return d->connectors.contains(connectionPointId);
}

863
KoConnectionPoint KoShape::connectionPoint(int connectionPointId) const
864 865
{
    Q_D(const KoShape);
866
    KoConnectionPoint p = d->connectors.value(connectionPointId, KoConnectionPoint());
867
    // convert glue point to shape coordinates
868
    d->convertToShapeCoordinates(p, size());
869
    return p;
Thomas Zander's avatar
Thomas Zander committed
870 871
}

872
KoConnectionPoints KoShape::connectionPoints() const
873
{
874
    Q_D(const KoShape);
875
    QSizeF s = size();
876 877 878
    KoConnectionPoints points = d->connectors;
    KoConnectionPoints::iterator point = points.begin();
    KoConnectionPoints::iterator lastPoint = points.end();
879
    // convert glue points to shape coordinates
880
    for(; point != lastPoint; ++point) {
881
        d->convertToShapeCoordinates(point.value(), s);
882
    }
883 884

    return points;
885 886
}

887 888 889 890
void KoShape::removeConnectionPoint(int connectionPointId)
{
    Q_D(KoShape);
    d->connectors.remove(connectionPointId);
891
    d->shapeChanged(ConnectionPointChanged);
892 893 894 895 896 897 898 899
}

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

900 901 902 903