KoShape.cpp 71.6 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

78 79
// KoShapePrivate

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

156 157
KoShapePrivate::~KoShapePrivate()
{
158
    Q_Q(KoShape);
159
    if (parent)
160
        parent->removeShape(q);
161
    Q_FOREACH (KoShapeManager *manager, shapeManagers) {
162 163
        manager->remove(q);
        manager->removeAdditional(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 177
        parent->model()->childChanged(q, type);
    q->shapeChanged(type);
178
    Q_FOREACH (KoShape * shape, dependees)
179
        shape->shapeChanged(type, q);
180
}
181

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

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

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

void KoShapePrivate::removeShapeManager(KoShapeManager *manager)
{
    shapeManagers.remove(manager);
212 213
}

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

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

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

    return value;
}

301 302


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

310 311
KoShape::KoShape(KoShapePrivate *dd)
    : d_ptr(dd)
312 313 314
{
}

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

322 323 324 325 326
KoShape *KoShape::cloneShape() const
{
    return 0;
}

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

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

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

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

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

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

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

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

377 378 379
    if (oldSize == newSize)
        return;

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

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

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

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

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

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

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

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

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

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

447
    return bb;
Thomas Zander's avatar
Thomas Zander committed
448 449
}

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

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

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
477 478
}

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

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

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

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

511
KoShape::ChildZOrderPolicy KoShape::childZOrderPolicy()
512
{
513
    return ChildZDefault;
514 515
}

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

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

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

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

596 597
void KoShape::setParent(KoShapeContainer *parent)
{
598
    Q_D(KoShape);
599 600

    if (d->parent == parent) {
601
        return;
602 603
    }

604 605
    KoShapeContainer *oldParent = d->parent;
    d->parent = 0; // avoids recursive removing
606 607 608 609 610 611 612

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

    KIS_SAFE_ASSERT_RECOVER_NOOP(parent != this);

Thomas Zander's avatar
Thomas Zander committed
613
    if (parent && parent != this) {
Thomas Zander's avatar
Thomas Zander committed
614
        d->parent = parent;
615
        parent->shapeInterface()->addShape(this);
616
    }
617

618
    notifyChanged();
619
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
620 621
}

622 623
int KoShape::zIndex() const
{
624
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
625
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
626 627
}

628 629
void KoShape::update() const
{
630
    Q_D(const KoShape);
631

632 633
    if (!d->shapeManagers.empty()) {
        QRectF rect(boundingRect());
634
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
Jan Hambrecht's avatar
Jan Hambrecht committed
635
            manager->update(rect, this, true);
636
        }
Thomas Zander's avatar
Thomas Zander committed
637 638 639
    }
}

640
void KoShape::update(const QRectF &rect) const
641
{
642 643 644 645 646

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

647
    Q_D(const KoShape);
648

649
    if (!d->shapeManagers.empty() && isVisible()) {
650
        QRectF rc(absoluteTransformation(0).mapRect(rect));
651
        Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
652
            manager->update(rc);
653 654
        }
    }
Thomas Zander's avatar
Thomas Zander committed
655 656
}

657
QPainterPath KoShape::outline() const
658
{
Thomas Zander's avatar
Thomas Zander committed
659
    QPainterPath path;
660
    path.addRect(outlineRect());
Thomas Zander's avatar
Thomas Zander committed
661 662 663
    return path;
}

664 665
QRectF KoShape::outlineRect() const
{
666
    const QSizeF s = size();
667 668 669 670 671 672 673
    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);
674
    if (d->fill) {
675
        return outline();
676
    }
677 678

    return QPainterPath();
679 680
}

681 682
QPointF KoShape::absolutePosition(KoFlake::Position anchor) const
{
683
    QPointF point;
684 685 686 687 688 689
    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;
690
    }
691
    return absoluteTransformation(0).map(point);
Thomas Zander's avatar
Thomas Zander committed
692 693
}

Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
694
void KoShape::setAbsolutePosition(const QPointF &newPosition, KoFlake::Position anchor)
695
{
696
    Q_D(KoShape);
697
    QPointF currentAbsPosition = absolutePosition(anchor);
698
    QPointF translate = newPosition - currentAbsPosition;
699
    QTransform translateMatrix;
700 701
    translateMatrix.translate(translate.x(), translate.y());
    applyAbsoluteTransformation(translateMatrix);
702
    notifyChanged();
703
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
704 705
}

706 707
void KoShape::copySettings(const KoShape *shape)
{
708
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
709 710
    d->size = shape->size();
    d->connectors.clear();
711
    Q_FOREACH (const KoConnectionPoint &point, shape->connectionPoints())
Jan Hambrecht's avatar
Jan Hambrecht committed
712
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
713 714
    d->zIndex = shape->zIndex();
    d->visible = shape->isVisible();
715

716 717 718 719 720
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
721

722
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
723 724
    d->protectContent = shape->isContentProtected();
    d->selectable = shape->isSelectable();
Thomas Zander's avatar
Thomas Zander committed
725
    d->keepAspect = shape->keepAspectRatio();
726
    d->localMatrix = shape->d_ptr->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
727 728
}

Thomas Zander's avatar
Thomas Zander committed
729
void KoShape::notifyChanged()
730
{
731
    Q_D(KoShape);
732
    Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
733
        manager->notifyShapeChanged(this);
734 735 736
    }
}

737 738
void KoShape::setUserData(KoShapeUserData *userData)
{
739
    Q_D(KoShape);
740
    d->userData.reset(userData);
741 742
}

743 744
KoShapeUserData *KoShape::userData() const
{
745
    Q_D(const KoShape);
746
    return d->userData.data();
747 748
}

Thomas Zander's avatar
Thomas Zander committed
749
bool KoShape::hasTransparency() const
750
{
Thomas Zander's avatar
Thomas Zander committed
751
    Q_D(const KoShape);
752
    if (! d->fill)
Thomas Zander's avatar
Thomas Zander committed
753
        return true;
754
    else
755 756 757 758 759 760
        return d->fill->hasTransparency() || d->transparency > 0.0;
}

void KoShape::setTransparency(qreal transparency)
{
    Q_D(KoShape);
761
    d->transparency = qBound<qreal>(0.0, transparency, 1.0);
762 763 764 765 766 767 768 769 770 771 772 773
}

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
774 775
}

776
KoInsets KoShape::strokeInsets() const
777
{
778
    Q_D(const KoShape);
779
    KoInsets answer;
780 781
    if (d->stroke)
        d->stroke->strokeInsets(this, answer);
782 783 784
    return answer;
}

785 786
qreal KoShape::rotation() const
{
787
    Q_D(const KoShape);
788
    // try to extract the rotation angle out of the local matrix
789 790 791
    // if it is a pure rotation matrix

    // check if the matrix has shearing mixed in
792
    if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
793
        return std::numeric_limits<qreal>::quiet_NaN();
794
    // check if the matrix has scaling mixed in
795
    if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
796
        return std::numeric_limits<qreal>::quiet_NaN();
797 798

    // calculate the angle from the matrix elements
799 800
    qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
    if (angle < 0.0)
801 802 803
        angle += 360.0;

    return angle;
Thomas Zander's avatar
Thomas Zander committed
804 805
}

806
QSizeF KoShape::size() const
807
{
808
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
809 810 811
    return d->size;
}

812 813
QPointF KoShape::position() const
{
814
    Q_D(const KoShape);
815 816
    QPointF center(0.5*size().width(), 0.5*size().height());
    return d->localMatrix.map(center) - center;
Thomas Zander's avatar
Thomas Zander committed
817 818
}

819
int KoShape::addConnectionPoint(const KoConnectionPoint &point)
820
{
821
    Q_D(KoShape);
822

823
    // get next glue point id
824
    int nextConnectionPointId = KoConnectionPoint::FirstCustomConnectionPoint;
Jan Hambrecht's avatar
Jan Hambrecht committed
825
    if (d->connectors.size())
826 827
        nextConnectionPointId = qMax(nextConnectionPointId, (--d->connectors.end()).key()+1);

828
    KoConnectionPoint p = point;
829 830
    d->convertFromShapeCoordinates(p, size());
    d->connectors[nextConnectionPointId] = p;
831 832 833 834

    return nextConnectionPointId;
}

835
bool KoShape::setConnectionPoint(int connectionPointId, const KoConnectionPoint &point)
836 837
{
    Q_D(KoShape);
838
    if (connectionPointId < 0)
839 840
        return false;

841 842
    const bool insertPoint = !hasConnectionPoint(connectionPointId);

843
    switch(connectionPointId) {
844 845 846 847 848 849
        case KoConnectionPoint::TopConnectionPoint:
        case KoConnectionPoint::RightConnectionPoint:
        case KoConnectionPoint::BottomConnectionPoint:
        case KoConnectionPoint::LeftConnectionPoint:
        {
            KoConnectionPoint::PointId id = static_cast<KoConnectionPoint::PointId>(connectionPointId);
850
            d->connectors[id] = KoConnectionPoint::defaultConnectionPoint(id);
851
            break;
852
        }
853
        default:
854
        {
855
            KoConnectionPoint p = point;
856 857
            d->convertFromShapeCoordinates(p, size());
            d->connectors[connectionPointId] = p;
858
            break;
859
        }
860 861
    }

862 863
    if(!insertPoint)
        d->shapeChanged(ConnectionPointChanged);
864 865 866 867

    return true;
}

868 869 870 871 872 873
bool KoShape::hasConnectionPoint(int connectionPointId) const
{
    Q_D(const KoShape);
    return d->connectors.contains(connectionPointId);
}

874
KoConnectionPoint KoShape::connectionPoint(int connectionPointId) const
875 876
{
    Q_D(const KoShape);
877
    KoConnectionPoint p = d->connectors.value(connectionPointId, KoConnectionPoint());
878
    // convert glue point to shape coordinates
879
    d->convertToShapeCoordinates(p, size());
880
    return p;
Thomas Zander's avatar
Thomas Zander committed
881 882
}

883
KoConnectionPoints KoShape::connectionPoints() const
884
{
885
    Q_D(const KoShape);
886
    QSizeF s = size();
887 888 889
    KoConnectionPoints points = d->connectors;
    KoConnectionPoints::iterator point = points.begin();
    KoConnectionPoints::iterator lastPoint = points.end();
890
    // convert glue points to shape coordinates
891
    for(; point != lastPoint; ++point) {
892
        d->convertToShapeCoordinates(point.value(), s);
893
    }
894 895

    return points;
896 897
}

898 899 900 901
void KoShape::removeConnectionPoint(int connectionPointId)
{
    Q_D(KoShape);
    d->connectors.remove(connectionPointId);