KoShape.cpp 67 KB
Newer Older
Thomas Zander's avatar
Thomas Zander committed
1
/* This file is part of the KDE project
Thomas Zander's avatar
Thomas Zander committed
2
   Copyright (C) 2006 Casper Boemann Rasmussen <cbr@boemann.dk>
Thomas Zander's avatar
Thomas Zander committed
3
   Copyright (C) 2006-2010 Thomas Zander <zander@kde.org>
4
   Copyright (C) 2006-2010 Thorsten Zachmann <zachmann@kde.org>
5
   Copyright (C) 2007-2009,2011 Jan Hambrecht <jaham@gmx.net>
Boudewijn Rempt's avatar
Boudewijn Rempt committed
6
   CopyRight (C) 2010 Boudewijn Rempt <boud@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,
Boudewijn Rempt's avatar
Boudewijn Rempt committed
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 35 36
#include "KoShapeBackground.h"
#include "KoColorBackground.h"
#include "KoGradientBackground.h"
#include "KoPatternBackground.h"
37
#include "KoShapeManager.h"
38
#include "KoShapeUserData.h"
39
#include "KoShapeApplicationData.h"
40
#include "KoShapeSavingContext.h"
41
#include "KoShapeLoadingContext.h"
42
#include "KoViewConverter.h"
43
#include "KoShapeStroke.h"
44
#include "ShapeDeleter_p.h"
45
#include "KoShapeShadow.h"
46
#include "KoClipPath.h"
47
#include "KoPathShape.h"
Thorsten Zachmann's avatar
Thorsten Zachmann committed
48 49
#include "KoEventAction.h"
#include "KoEventActionRegistry.h"
50
#include "KoOdfWorkaround.h"
51
#include "KoFilterEffectStack.h"
52
#include <KoSnapData.h>
53
#include <KoElementReference.h>
Thomas Zander's avatar
Thomas Zander committed
54

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

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>
71 72
#include <kdebug.h>

73
#include <limits>
74
#include "KoOdfGradientBackground.h"
75

Boudewijn Rempt's avatar
Boudewijn Rempt committed
76 77
// KoShapePrivate

78
KoShapePrivate::KoShapePrivate(KoShape *shape)
79 80
    : q_ptr(shape),
      size(50, 50),
Boudewijn Rempt's avatar
Boudewijn Rempt committed
81 82 83
      parent(0),
      userData(0),
      appData(0),
84
      stroke(0),
Boudewijn Rempt's avatar
Boudewijn Rempt committed
85 86
      fill(0),
      shadow(0),
87
      clipPath(0),
Boudewijn Rempt's avatar
Boudewijn Rempt committed
88 89 90
      filterEffectStack(0),
      transparency(0.0),
      zIndex(0),
91
      runThrough(0),
Boudewijn Rempt's avatar
Boudewijn Rempt committed
92 93 94 95 96 97 98
      visible(true),
      printable(true),
      geometryProtected(false),
      keepAspect(false),
      selectable(true),
      detectCollision(false),
      protectContent(false),
99
      textRunAroundSide(KoShape::BiggestRunAroundSide),
100
      textRunAroundDistance(1.0),
101 102
      textRunAroundThreshold(0.0),
      textRunAroundContour(KoShape::ContourFull)
103
{
104 105 106 107
    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);
108
}
Thomas Zander's avatar
Thomas Zander committed
109

110 111
KoShapePrivate::~KoShapePrivate()
{
112
    Q_Q(KoShape);
113
    if (parent)
Thomas Zander's avatar
Thomas Zander committed
114
        parent->removeShape(q);
115
    foreach(KoShapeManager *manager, shapeManagers) {
116 117
        manager->remove(q);
        manager->removeAdditional(q);
Thomas Zander's avatar
Thomas Zander committed
118
    }
119 120
    delete userData;
    delete appData;
121 122
    if (stroke && !stroke->deref())
        delete stroke;
123
    if (shadow && !shadow->deref())
124
        delete shadow;
125
    if (fill && !fill->deref())
126
        delete fill;
127
    if (filterEffectStack && !filterEffectStack->deref())
128
        delete filterEffectStack;
Jan Hambrecht's avatar
Jan Hambrecht committed
129
    delete clipPath;
130 131
    qDeleteAll(eventActions);
}
Thomas Zander's avatar
Thomas Zander committed
132

133 134
void KoShapePrivate::shapeChanged(KoShape::ChangeType type)
{
135
    Q_Q(KoShape);
136
    if (parent)
137 138
        parent->model()->childChanged(q, type);
    q->shapeChanged(type);
139
    foreach(KoShape * shape, dependees)
140
        shape->shapeChanged(type, q);
141
}
142

143
void KoShapePrivate::updateStroke()
144
{
145
    Q_Q(KoShape);
146
    if (stroke == 0)
147 148
        return;
    KoInsets insets;
149
    stroke->strokeInsets(q, insets);
150 151 152
    QSizeF inner = q->size();
    // update left
    q->update(QRectF(-insets.left, -insets.top, insets.left,
Boudewijn Rempt's avatar
Boudewijn Rempt committed
153
                     inner.height() + insets.top + insets.bottom));
154 155
    // update top
    q->update(QRectF(-insets.left, -insets.top,
Boudewijn Rempt's avatar
Boudewijn Rempt committed
156
                     inner.width() + insets.left + insets.right, insets.top));
157 158
    // update right
    q->update(QRectF(inner.width(), -insets.top, insets.right,
Boudewijn Rempt's avatar
Boudewijn Rempt committed
159
                     inner.height() + insets.top + insets.bottom));
160 161
    // update bottom
    q->update(QRectF(-insets.left, inner.height(),
Boudewijn Rempt's avatar
Boudewijn Rempt committed
162
                     inner.width() + insets.left + insets.right, insets.bottom));
163
}
Thomas Zander's avatar
Thomas Zander committed
164

165 166 167 168 169 170 171 172
void KoShapePrivate::addShapeManager(KoShapeManager *manager)
{
    shapeManagers.insert(manager);
}

void KoShapePrivate::removeShapeManager(KoShapeManager *manager)
{
    shapeManagers.remove(manager);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
173 174
}

175
void KoShapePrivate::convertFromShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
176
{
Jan Hambrecht's avatar
Jan Hambrecht committed
177
    switch(point.alignment) {
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
        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
215
    switch(point.alignment) {
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
        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;
    }
247 248
}

249
// static
250
QString KoShapePrivate::getStyleProperty(const char *property, KoShapeLoadingContext &context)
251 252 253 254 255 256 257 258 259 260 261
{
    KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
    QString value;

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

    return value;
}

262 263


264
// ======== KoShape
Thomas Zander's avatar
Thomas Zander committed
265
KoShape::KoShape()
Boudewijn Rempt's avatar
Boudewijn Rempt committed
266
    : d_ptr(new KoShapePrivate(this))
Thomas Zander's avatar
Thomas Zander committed
267
{
268
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
269 270
}

271 272 273 274 275
KoShape::KoShape(KoShapePrivate &dd)
    : d_ptr(&dd)
{
}

Thomas Zander's avatar
Thomas Zander committed
276 277
KoShape::~KoShape()
{
278
    Q_D(KoShape);
279
    d->shapeChanged(Deleted);
280
    delete d_ptr;
Thomas Zander's avatar
Thomas Zander committed
281 282
}

Thomas Zander's avatar
Thomas Zander committed
283
void KoShape::scale(qreal sx, qreal sy)
Thomas Zander's avatar
Thomas Zander committed
284
{
285
    Q_D(KoShape);
286
    QPointF pos = position();
287
    QTransform scaleMatrix;
288 289 290
    scaleMatrix.translate(pos.x(), pos.y());
    scaleMatrix.scale(sx, sy);
    scaleMatrix.translate(-pos.x(), -pos.y());
291
    d->localMatrix = d->localMatrix * scaleMatrix;
292

293
    notifyChanged();
294
    d->shapeChanged(ScaleChanged);
Thomas Zander's avatar
Thomas Zander committed
295 296
}

297
void KoShape::rotate(qreal angle)
Thomas Zander's avatar
Thomas Zander committed
298
{
299
    Q_D(KoShape);
300
    QPointF center = d->localMatrix.map(QPointF(0.5 * size().width(), 0.5 * size().height()));
301
    QTransform rotateMatrix;
302 303 304
    rotateMatrix.translate(center.x(), center.y());
    rotateMatrix.rotate(angle);
    rotateMatrix.translate(-center.x(), -center.y());
305
    d->localMatrix = d->localMatrix * rotateMatrix;
306

307
    notifyChanged();
308
    d->shapeChanged(RotationChanged);
Thomas Zander's avatar
Thomas Zander committed
309 310
}

Thomas Zander's avatar
Thomas Zander committed
311
void KoShape::shear(qreal sx, qreal sy)
Thomas Zander's avatar
Thomas Zander committed
312
{
313
    Q_D(KoShape);
314
    QPointF pos = position();
315
    QTransform shearMatrix;
316 317 318
    shearMatrix.translate(pos.x(), pos.y());
    shearMatrix.shear(sx, sy);
    shearMatrix.translate(-pos.x(), -pos.y());
319
    d->localMatrix = d->localMatrix * shearMatrix;
320

321
    notifyChanged();
322
    d->shapeChanged(ShearChanged);
Thomas Zander's avatar
Thomas Zander committed
323 324
}

325
void KoShape::setSize(const QSizeF &newSize)
Thomas Zander's avatar
Thomas Zander committed
326
{
327
    Q_D(KoShape);
328
    QSizeF oldSize(size());
329

330
    // always set size, as d->size and size() may vary
Thomas Zander's avatar
Thomas Zander committed
331
    d->size = newSize;
332

333 334 335
    if (oldSize == newSize)
        return;

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

340
void KoShape::setPosition(const QPointF &newPosition)
Thomas Zander's avatar
Thomas Zander committed
341
{
342
    Q_D(KoShape);
343
    QPointF currentPos = position();
344
    if (newPosition == currentPos)
Thomas Zander's avatar
Thomas Zander committed
345
        return;
346
    QTransform translateMatrix;
347
    translateMatrix.translate(newPosition.x() - currentPos.x(), newPosition.y() - currentPos.y());
348 349 350
    d->localMatrix = d->localMatrix * translateMatrix;

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

354
bool KoShape::hitTest(const QPointF &position) const
Thomas Zander's avatar
Thomas Zander committed
355
{
356
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
357
    if (d->parent && d->parent->isClipped(this) && !d->parent->hitTest(position))
Thomas Zander's avatar
Thomas Zander committed
358 359
        return false;

360
    QPointF point = absoluteTransformation(0).inverted().map(position);
361
    QRectF bb(QPointF(), size());
362
    if (d->stroke) {
363
        KoInsets insets;
364
        d->stroke->strokeInsets(this, insets);
365
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
366
    }
367 368 369
    if (bb.contains(point))
        return true;

370
    // if there is no shadow we can as well just leave
371
    if (! d->shadow)
372
        return false;
Thomas Zander's avatar
Thomas Zander committed
373

374 375
    // the shadow has an offset to the shape, so we simply
    // check if the position minus the shadow offset hits the shape
376
    point = absoluteTransformation(0).inverted().map(position - d->shadow->offset());
377

378
    return bb.contains(point);
Thomas Zander's avatar
Thomas Zander committed
379 380
}

381
QRectF KoShape::boundingRect() const
Thomas Zander's avatar
Thomas Zander committed
382
{
383
    Q_D(const KoShape);
Jan Hambrecht's avatar
Jan Hambrecht committed
384

385
    QTransform transform = absoluteTransformation(0);
386
    QRectF bb = outlineRect();
387
    if (d->stroke) {
388
        KoInsets insets;
389
        d->stroke->strokeInsets(this, insets);
390 391
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
392
    bb = transform.mapRect(bb);
393
    if (d->shadow) {
394
        KoInsets insets;
Thomas Zander's avatar
Thomas Zander committed
395
        d->shadow->insets(insets);
396 397
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
398
    if (d->filterEffectStack) {
399
        QRectF clipRect = d->filterEffectStack->clipRectForBoundingRect(outlineRect());
400 401
        bb |= transform.mapRect(clipRect);
    }
Thomas Zander's avatar
Thomas Zander committed
402

403
    return bb;
Thomas Zander's avatar
Thomas Zander committed
404 405
}

406
QTransform KoShape::absoluteTransformation(const KoViewConverter *converter) const
407
{
408
    Q_D(const KoShape);
409
    QTransform matrix;
Thomas Zander's avatar
Thomas Zander committed
410
    // apply parents matrix to inherit any transformations done there.
411
    KoShapeContainer * container = d->parent;
412
    if (container) {
413
        if (container->inheritsTransform(this)) {
414 415 416 417
            // We do need to pass the converter here, otherwise the parent's
            // translation is not inherited.
            matrix = container->absoluteTransformation(converter);
        } else {
418 419 420 421
            QSizeF containerSize = container->size();
            QPointF containerPos = container->absolutePosition() - QPointF(0.5 * containerSize.width(), 0.5 * containerSize.height());
            if (converter)
                containerPos = converter->documentToView(containerPos);
422
            matrix.translate(containerPos.x(), containerPos.y());
Thomas Zander's avatar
Thomas Zander committed
423 424 425
        }
    }

426
    if (converter) {
427 428 429
        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
430
    }
431 432

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
433 434
}

435
void KoShape::applyAbsoluteTransformation(const QTransform &matrix)
436
{
437
    QTransform globalMatrix = absoluteTransformation(0);
438 439
    // 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
440
    // to be relative to the local coordinate system
441
    QTransform transformMatrix = globalMatrix * matrix * globalMatrix.inverted();
442
    applyTransformation(transformMatrix);
443 444
}

445
void KoShape::applyTransformation(const QTransform &matrix)
446
{
447
    Q_D(KoShape);
448
    d->localMatrix = matrix * d->localMatrix;
449
    notifyChanged();
450
    d->shapeChanged(GenericMatrixChange);
451 452
}

453
void KoShape::setTransformation(const QTransform &matrix)
454
{
455
    Q_D(KoShape);
456 457
    d->localMatrix = matrix;
    notifyChanged();
458
    d->shapeChanged(GenericMatrixChange);
459
}
Thomas Zander's avatar
Thomas Zander committed
460

461
QTransform KoShape::transformation() const
462
{
463
    Q_D(const KoShape);
464 465 466
    return d->localMatrix;
}

467
KoShape::ChildZOrderPolicy KoShape::childZOrderPolicy()
468
{
469
    return ChildZDefault;
470 471
}

472 473
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
{
474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
    // 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
513
    bool foundCommonParent = false;
514 515 516 517
    int index1 = s1->zIndex();
    int index2 = s2->zIndex();
    parentShapeS1 = s1;
    parentShapeS2 = s2;
518 519 520 521 522 523 524 525
    while (parentShapeS1 && !foundCommonParent) {
        parentShapeS2 = s2;
        index2 = parentShapeS2->zIndex();
        while (parentShapeS2) {
            if (parentShapeS2 == parentShapeS1) {
                foundCommonParent = true;
                break;
            }
526
            if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) {
527 528
                index2 = parentShapeS2->zIndex();
            }
529
            parentShapeS2 = parentShapeS2->parent();
530
        }
531 532

        if (!foundCommonParent) {
533
            if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) {
534 535
                index1 = parentShapeS1->zIndex();
            }
536
            parentShapeS1 = parentShapeS1->parent();
537 538
        }
    }
539

540 541 542 543 544 545 546 547
    // If the one shape is a parent/child of the other then sort so.
    if (s1 == parentShapeS2) {
        return true;
    }
    if (s2 == parentShapeS1) {
        return false;
    }

548
    // If we went that far then the z-Index is used for sorting.
549
    return index1 < index2;
Thomas Zander's avatar
Thomas Zander committed
550 551
}

552 553
void KoShape::setParent(KoShapeContainer *parent)
{
554
    Q_D(KoShape);
555
    if (d->parent == parent)
556
        return;
557 558 559
    KoShapeContainer *oldParent = d->parent;
    d->parent = 0; // avoids recursive removing
    if (oldParent)
Thomas Zander's avatar
Thomas Zander committed
560
        oldParent->removeShape(this);
Thomas Zander's avatar
Thomas Zander committed
561
    if (parent && parent != this) {
Thomas Zander's avatar
Thomas Zander committed
562
        d->parent = parent;
Thomas Zander's avatar
Thomas Zander committed
563
        parent->addShape(this);
564
    }
565
    notifyChanged();
566
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
567 568
}

569 570
int KoShape::zIndex() const
{
571
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
572
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
573 574
}

575 576
void KoShape::update() const
{
577
    Q_D(const KoShape);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
578

579 580
    if (!d->shapeManagers.empty()) {
        QRectF rect(boundingRect());
Boudewijn Rempt's avatar
Boudewijn Rempt committed
581
        foreach(KoShapeManager * manager, d->shapeManagers) {
Jan Hambrecht's avatar
Jan Hambrecht committed
582
            manager->update(rect, this, true);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
583
        }
Thomas Zander's avatar
Thomas Zander committed
584 585 586
    }
}

Boudewijn Rempt's avatar
Boudewijn Rempt committed
587
void KoShape::update(const QRectF &rect) const
588
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
589 590 591 592 593

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

594
    Q_D(const KoShape);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
595

596
    if (!d->shapeManagers.empty() && isVisible()) {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
597
        QRectF rc(absoluteTransformation(0).mapRect(rect));
598
        foreach(KoShapeManager * manager, d->shapeManagers) {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
599
            manager->update(rc);
600 601
        }
    }
Thomas Zander's avatar
Thomas Zander committed
602 603
}

604
QPainterPath KoShape::outline() const
605
{
Thomas Zander's avatar
Thomas Zander committed
606
    QPainterPath path;
607
    path.addRect(outlineRect());
Thomas Zander's avatar
Thomas Zander committed
608 609 610
    return path;
}

611 612
QRectF KoShape::outlineRect() const
{
613
    const QSizeF s = size();
614 615 616 617 618 619 620
    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);
Inge Wallin's avatar
Inge Wallin committed
621
    if (d->fill) {
622
        return outline();
Inge Wallin's avatar
Inge Wallin committed
623
    }
624 625

    return QPainterPath();
626 627
}

628 629
QPointF KoShape::absolutePosition(KoFlake::Position anchor) const
{
630
    QPointF point;
631 632 633 634 635 636
    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;
637
    }
638
    return absoluteTransformation(0).map(point);
Thomas Zander's avatar
Thomas Zander committed
639 640
}

641 642
void KoShape::setAbsolutePosition(QPointF newPosition, KoFlake::Position anchor)
{
643
    Q_D(KoShape);
644
    QPointF currentAbsPosition = absolutePosition(anchor);
645
    QPointF translate = newPosition - currentAbsPosition;
646
    QTransform translateMatrix;
647 648
    translateMatrix.translate(translate.x(), translate.y());
    applyAbsoluteTransformation(translateMatrix);
649
    notifyChanged();
650
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
651 652
}

653 654
void KoShape::copySettings(const KoShape *shape)
{
655
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
656 657
    d->size = shape->size();
    d->connectors.clear();
658
    foreach(const KoConnectionPoint &point, shape->connectionPoints())
Jan Hambrecht's avatar
Jan Hambrecht committed
659
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
660 661
    d->zIndex = shape->zIndex();
    d->visible = shape->isVisible();
662

663 664 665 666 667
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
668

669
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
670 671
    d->protectContent = shape->isContentProtected();
    d->selectable = shape->isSelectable();
Thomas Zander's avatar
Thomas Zander committed
672
    d->keepAspect = shape->keepAspectRatio();
673
    d->localMatrix = shape->d_ptr->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
674 675
}

Thomas Zander's avatar
Thomas Zander committed
676
void KoShape::notifyChanged()
677
{
678
    Q_D(KoShape);
679 680
    foreach(KoShapeManager * manager, d->shapeManagers) {
        manager->notifyShapeChanged(this);
681 682 683
    }
}

684 685
void KoShape::setUserData(KoShapeUserData *userData)
{
686
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
687
    delete d->userData;
Thomas Zander's avatar
Thomas Zander committed
688
    d->userData = userData;
689 690
}

691 692
KoShapeUserData *KoShape::userData() const
{
693
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
694
    return d->userData;
695 696
}

697 698
void KoShape::setApplicationData(KoShapeApplicationData *appData)
{
699
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
700
    // appdata is deleted by the application.
Thomas Zander's avatar
Thomas Zander committed
701
    d->appData = appData;
702 703
}

704 705
KoShapeApplicationData *KoShape::applicationData() const
{
706
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
707
    return d->appData;
708 709
}

Thomas Zander's avatar
Thomas Zander committed
710
bool KoShape::hasTransparency() const
711
{
Thomas Zander's avatar
Thomas Zander committed
712
    Q_D(const KoShape);
713
    if (! d->fill)
Thomas Zander's avatar
Thomas Zander committed
714
        return true;
715
    else
716 717 718 719 720 721
        return d->fill->hasTransparency() || d->transparency > 0.0;
}

void KoShape::setTransparency(qreal transparency)
{
    Q_D(KoShape);
722
    d->transparency = qBound<qreal>(0.0, transparency, 1.0);
723 724 725 726 727 728 729 730 731 732 733 734
}

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
735 736
}

737
KoInsets KoShape::strokeInsets() const
738
{
739
    Q_D(const KoShape);
740
    KoInsets answer;
741 742
    if (d->stroke)
        d->stroke->strokeInsets(this, answer);
743 744 745
    return answer;
}

746 747
qreal KoShape::rotation() const
{
748
    Q_D(const KoShape);
749
    // try to extract the rotation angle out of the local matrix
750 751 752
    // if it is a pure rotation matrix

    // check if the matrix has shearing mixed in
753
    if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
754
        return std::numeric_limits<qreal>::quiet_NaN();
755
    // check if the matrix has scaling mixed in
756
    if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
757
        return std::numeric_limits<qreal>::quiet_NaN();
758 759

    // calculate the angle from the matrix elements
760 761
    qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
    if (angle < 0.0)
762 763 764
        angle += 360.0;

    return angle;
Thomas Zander's avatar
Thomas Zander committed
765 766
}

767
QSizeF KoShape::size() const
768
{
769
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
770 771 772
    return d->size;
}

773 774
QPointF KoShape::position() const
{
775
    Q_D(const KoShape);
776 777
    QPointF center(0.5*size().width(), 0.5*size().height());
    return d->localMatrix.map(center) - center;
Thomas Zander's avatar
Thomas Zander committed
778 779
}

780
int KoShape::addConnectionPoint(const KoConnectionPoint &point)
781
{
782
    Q_D(KoShape);
783

784
    // get next glue point id
785
    int nextConnectionPointId = KoConnectionPoint::FirstCustomConnectionPoint;
Jan Hambrecht's avatar
Jan Hambrecht committed
786
    if (d->connectors.size())
787 788
        nextConnectionPointId = qMax(nextConnectionPointId, (--d->connectors.end()).key()+1);

789
    KoConnectionPoint p = point;
790 791
    d->convertFromShapeCoordinates(p, size());
    d->connectors[nextConnectionPointId] = p;
792 793 794 795

    return nextConnectionPointId;
}

796
bool KoShape::setConnectionPoint(int connectionPointId, const KoConnectionPoint &point)
797 798
{
    Q_D(KoShape);
799
    if (connectionPointId < 0)
800 801
        return false;

802 803
    const bool insertPoint = !hasConnectionPoint(connectionPointId);

804
    switch(connectionPointId) {
805 806 807 808 809 810
        case KoConnectionPoint::TopConnectionPoint:
        case KoConnectionPoint::RightConnectionPoint:
        case KoConnectionPoint::BottomConnectionPoint:
        case KoConnectionPoint::LeftConnectionPoint:
        {
            KoConnectionPoint::PointId id = static_cast<KoConnectionPoint::PointId>(connectionPointId);
811
            d->connectors[id] = KoConnectionPoint::defaultConnectionPoint(id);
812
            break;
813
        }
814
        default:
815
        {
816
            KoConnectionPoint p = point;
817 818
            d->convertFromShapeCoordinates(p, size());
            d->connectors[connectionPointId] = p;
819
            break;
820
        }
821 822
    }

823 824
    if(!insertPoint)
        d->shapeChanged(ConnectionPointChanged);
825 826 827 828

    return true;
}

829 830 831 832 833 834
bool KoShape::hasConnectionPoint(int connectionPointId) const
{
    Q_D(const KoShape);
    return d->connectors.contains(connectionPointId);
}

835
KoConnectionPoint KoShape::connectionPoint(int connectionPointId) const
836 837
{
    Q_D(const KoShape);
838
    KoConnectionPoint p = d->connectors.value(connectionPointId, KoConnectionPoint());
839
    // convert glue point to shape coordinates
840
    d->convertToShapeCoordinates(p, size());
841
    return p;
Thomas Zander's avatar
Thomas Zander committed
842 843
}

844
KoConnectionPoints KoShape::connectionPoints() const
845
{
846
    Q_D(const KoShape);
847
    QSizeF s = size();
848 849 850
    KoConnectionPoints points = d->connectors;
    KoConnectionPoints::iterator point = points.begin();
    KoConnectionPoints::iterator lastPoint = points.end();
851
    // convert glue points to shape coordinates
852
    for(; point != lastPoint; ++point) {
853
        d->convertToShapeCoordinates(point.value(), s);
854
    }
855 856

    return points;
857 858
}

859 860 861 862
void KoShape::removeConnectionPoint(int connectionPointId)
{
    Q_D(KoShape);
    d->connectors.remove(connectionPointId);
863
    d->shapeChanged(ConnectionPointChanged);
864 865 866 867 868 869 870 871
}

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

872
void KoShape::addEventAction(KoEventAction *action)
873
{
874
    Q_D(KoShape);