KoShape.cpp 62.7 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>
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 32
#include "KoInsets.h"
#include "KoShapeBorderModel.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 "KoLineBorder.h"
44
#include "ShapeDeleter_p.h"
45
#include "KoShapeShadow.h"
46
#include "KoClipPath.h"
Thorsten Zachmann's avatar
Thorsten Zachmann committed
47 48
#include "KoEventAction.h"
#include "KoEventActionRegistry.h"
49
#include "KoOdfWorkaround.h"
50
#include "KoFilterEffectStack.h"
51
#include <KoSnapData.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

Thomas Zander's avatar
Thomas Zander committed
63
#include <QPainter>
64
#include <QVariant>
Thomas Zander's avatar
Thomas Zander committed
65
#include <QPainterPath>
66
#include <QList>
67
#include <QMap>
68
#include <QByteArray>
69 70
#include <kdebug.h>

71 72
#include <limits>

73 74
// KoShapePrivate

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

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

129 130
void KoShapePrivate::shapeChanged(KoShape::ChangeType type)
{
131
    Q_Q(KoShape);
132
    if (parent)
133 134
        parent->model()->childChanged(q, type);
    q->shapeChanged(type);
135
    foreach(KoShape * shape, dependees)
136
        shape->shapeChanged(type, q);
137
}
138

139 140
void KoShapePrivate::updateBorder()
{
141
    Q_Q(KoShape);
142 143 144 145 146 147 148
    if (border == 0)
        return;
    KoInsets insets;
    border->borderInsets(q, insets);
    QSizeF inner = q->size();
    // update left
    q->update(QRectF(-insets.left, -insets.top, insets.left,
149
                     inner.height() + insets.top + insets.bottom));
150 151
    // update top
    q->update(QRectF(-insets.left, -insets.top,
152
                     inner.width() + insets.left + insets.right, insets.top));
153 154
    // update right
    q->update(QRectF(inner.width(), -insets.top, insets.right,
155
                     inner.height() + insets.top + insets.bottom));
156 157
    // update bottom
    q->update(QRectF(-insets.left, inner.height(),
158
                     inner.width() + insets.left + insets.right, insets.bottom));
159
}
Thomas Zander's avatar
Thomas Zander committed
160

161 162 163 164 165 166 167 168
void KoShapePrivate::addShapeManager(KoShapeManager *manager)
{
    shapeManagers.insert(manager);
}

void KoShapePrivate::removeShapeManager(KoShapeManager *manager)
{
    shapeManagers.remove(manager);
169 170
}

171
void KoShapePrivate::convertFromShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
172
{
Jan Hambrecht's avatar
Jan Hambrecht committed
173
    switch(point.alignment) {
174 175 176 177 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
        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
211
    switch(point.alignment) {
212 213 214 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
        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;
    }
243 244
}

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

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

    return value;
}

258 259


260
// ======== KoShape
Thomas Zander's avatar
Thomas Zander committed
261
KoShape::KoShape()
262
    : d_ptr(new KoShapePrivate(this))
Thomas Zander's avatar
Thomas Zander committed
263
{
264
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
265 266
}

267 268 269 270 271
KoShape::KoShape(KoShapePrivate &dd)
    : d_ptr(&dd)
{
}

Thomas Zander's avatar
Thomas Zander committed
272 273
KoShape::~KoShape()
{
274
    Q_D(KoShape);
275
    d->shapeChanged(Deleted);
276
    delete d_ptr;
Thomas Zander's avatar
Thomas Zander committed
277 278
}

279 280
void KoShape::paintDecorations(QPainter &painter, const KoViewConverter &converter, const KoCanvasBase *canvas)
{
Thomas Zander's avatar
Thomas Zander committed
281 282 283
    Q_UNUSED(painter);
    Q_UNUSED(converter);
    Q_UNUSED(canvas);
284
    /* Since this code is not actually used (flow is going to be the main user) lets disable instead of fix.
285
        if (selected)
Thomas Zander's avatar
Thomas Zander committed
286
        {
287
            // draw connectors
288 289 290 291 292
            QPen pen(Qt::blue);
            pen.setWidth(0);
            painter.setPen(pen);
            painter.setBrush(Qt::NoBrush);
            for (int i = 0; i < d->connectors.size(); ++i)
293
          {
294
                QPointF p = converter.documentToView(d->connectors[ i ]);
295 296
                painter.drawLine(QPointF(p.x() - 2, p.y() + 2), QPointF(p.x() + 2, p.y() - 2));
                painter.drawLine(QPointF(p.x() + 2, p.y() + 2), QPointF(p.x() - 2, p.y() - 2));
297 298
            }
        }*/
Thomas Zander's avatar
Thomas Zander committed
299 300
}

Thomas Zander's avatar
Thomas Zander committed
301
void KoShape::scale(qreal sx, qreal sy)
Thomas Zander's avatar
Thomas Zander committed
302
{
303
    Q_D(KoShape);
304
    QPointF pos = position();
305
    QTransform scaleMatrix;
306 307 308
    scaleMatrix.translate(pos.x(), pos.y());
    scaleMatrix.scale(sx, sy);
    scaleMatrix.translate(-pos.x(), -pos.y());
309
    d->localMatrix = d->localMatrix * scaleMatrix;
310

311
    notifyChanged();
312
    d->shapeChanged(ScaleChanged);
Thomas Zander's avatar
Thomas Zander committed
313 314
}

315
void KoShape::rotate(qreal angle)
Thomas Zander's avatar
Thomas Zander committed
316
{
317
    Q_D(KoShape);
318
    QPointF center = d->localMatrix.map(QPointF(0.5 * size().width(), 0.5 * size().height()));
319
    QTransform rotateMatrix;
320 321 322
    rotateMatrix.translate(center.x(), center.y());
    rotateMatrix.rotate(angle);
    rotateMatrix.translate(-center.x(), -center.y());
323
    d->localMatrix = d->localMatrix * rotateMatrix;
324

325
    notifyChanged();
326
    d->shapeChanged(RotationChanged);
Thomas Zander's avatar
Thomas Zander committed
327 328
}

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

339
    notifyChanged();
340
    d->shapeChanged(ShearChanged);
Thomas Zander's avatar
Thomas Zander committed
341 342
}

343
void KoShape::setSize(const QSizeF &newSize)
Thomas Zander's avatar
Thomas Zander committed
344
{
345
    Q_D(KoShape);
346
    QSizeF oldSize(size());
347

348
    // always set size, as d->size and size() may vary
Thomas Zander's avatar
Thomas Zander committed
349
    d->size = newSize;
350

351 352 353
    if (oldSize == newSize)
        return;

354
    notifyChanged();
355
    d->shapeChanged(SizeChanged);
Thomas Zander's avatar
Thomas Zander committed
356 357
}

358
void KoShape::setPosition(const QPointF &newPosition)
Thomas Zander's avatar
Thomas Zander committed
359
{
360
    Q_D(KoShape);
361
    QPointF currentPos = position();
362
    if (newPosition == currentPos)
Thomas Zander's avatar
Thomas Zander committed
363
        return;
364
    QTransform translateMatrix;
365
    translateMatrix.translate(newPosition.x() - currentPos.x(), newPosition.y() - currentPos.y());
366 367 368
    d->localMatrix = d->localMatrix * translateMatrix;

    notifyChanged();
369
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
370 371
}

372
bool KoShape::hitTest(const QPointF &position) const
Thomas Zander's avatar
Thomas Zander committed
373
{
374
    Q_D(const KoShape);
375
    if (d->parent && d->parent->isClipped(this) && !d->parent->hitTest(position))
Thomas Zander's avatar
Thomas Zander committed
376 377
        return false;

378
    QPointF point = absoluteTransformation(0).inverted().map(position);
379
    QRectF bb(QPointF(), size());
380
    if (d->border) {
381
        KoInsets insets;
382
        d->border->borderInsets(this, insets);
383
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
384
    }
385 386 387
    if (bb.contains(point))
        return true;

388
    // if there is no shadow we can as well just leave
389
    if (! d->shadow)
390
        return false;
Thomas Zander's avatar
Thomas Zander committed
391

392 393
    // the shadow has an offset to the shape, so we simply
    // check if the position minus the shadow offset hits the shape
394
    point = absoluteTransformation(0).inverted().map(position - d->shadow->offset());
395

396
    return bb.contains(point);
Thomas Zander's avatar
Thomas Zander committed
397 398
}

399
QRectF KoShape::boundingRect() const
Thomas Zander's avatar
Thomas Zander committed
400
{
401
    Q_D(const KoShape);
Jan Hambrecht's avatar
Jan Hambrecht committed
402

403
    QTransform transform = absoluteTransformation(0);
404
    QRectF bb = outlineRect();
405
    if (d->border) {
406 407 408 409
        KoInsets insets;
        d->border->borderInsets(this, insets);
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
410
    bb = transform.mapRect(bb);
411
    if (d->shadow) {
412
        KoInsets insets;
Thomas Zander's avatar
Thomas Zander committed
413
        d->shadow->insets(insets);
414 415
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
416
    if (d->filterEffectStack) {
417
        QRectF clipRect = d->filterEffectStack->clipRectForBoundingRect(outlineRect());
418 419
        bb |= transform.mapRect(clipRect);
    }
Thomas Zander's avatar
Thomas Zander committed
420

421
    return bb;
Thomas Zander's avatar
Thomas Zander committed
422 423
}

424
QTransform KoShape::absoluteTransformation(const KoViewConverter *converter) const
425
{
426
    Q_D(const KoShape);
427
    QTransform matrix;
Thomas Zander's avatar
Thomas Zander committed
428
    // apply parents matrix to inherit any transformations done there.
429
    KoShapeContainer * container = d->parent;
430
    if (container) {
431
        if (container->inheritsTransform(this)) {
432 433 434 435
            // We do need to pass the converter here, otherwise the parent's
            // translation is not inherited.
            matrix = container->absoluteTransformation(converter);
        } else {
436 437 438 439
            QSizeF containerSize = container->size();
            QPointF containerPos = container->absolutePosition() - QPointF(0.5 * containerSize.width(), 0.5 * containerSize.height());
            if (converter)
                containerPos = converter->documentToView(containerPos);
440
            matrix.translate(containerPos.x(), containerPos.y());
Thomas Zander's avatar
Thomas Zander committed
441 442 443
        }
    }

444
    if (converter) {
445 446 447
        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
448
    }
449 450

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
451 452
}

453
void KoShape::applyAbsoluteTransformation(const QTransform &matrix)
454
{
455
    QTransform globalMatrix = absoluteTransformation(0);
456 457
    // 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
458
    // to be relative to the local coordinate system
459
    QTransform transformMatrix = globalMatrix * matrix * globalMatrix.inverted();
460
    applyTransformation(transformMatrix);
461 462
}

463
void KoShape::applyTransformation(const QTransform &matrix)
464
{
465
    Q_D(KoShape);
466
    d->localMatrix = matrix * d->localMatrix;
467
    notifyChanged();
468
    d->shapeChanged(GenericMatrixChange);
469 470
}

471
void KoShape::setTransformation(const QTransform &matrix)
472
{
473
    Q_D(KoShape);
474 475
    d->localMatrix = matrix;
    notifyChanged();
476
    d->shapeChanged(GenericMatrixChange);
477
}
Thomas Zander's avatar
Thomas Zander committed
478

479
QTransform KoShape::transformation() const
480
{
481
    Q_D(const KoShape);
482 483 484
    return d->localMatrix;
}

485 486
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
{
487 488 489 490 491
    bool foundCommonParent = false;
    KoShape *parentShapeS1 = s1;
    KoShape *parentShapeS2 = s2;
    int index1 = parentShapeS1->zIndex();
    int index2 = parentShapeS2->zIndex();
492 493
    int runThrough1 = parentShapeS1->runThrough();
    int runThrough2 = parentShapeS2->runThrough();
494 495 496
    while (parentShapeS1 && !foundCommonParent) {
        parentShapeS2 = s2;
        index2 = parentShapeS2->zIndex();
497
        runThrough2 = parentShapeS2->runThrough();
498 499 500 501 502 503
        while (parentShapeS2) {
            if (parentShapeS2 == parentShapeS1) {
                foundCommonParent = true;
                break;
            }
            index2 = parentShapeS2->zIndex();
504
            runThrough2 = parentShapeS2->runThrough();
505
            parentShapeS2 = parentShapeS2->parent();
506
        }
507 508 509

        if (!foundCommonParent) {
            index1 = parentShapeS1->zIndex();
510
            runThrough1 = parentShapeS1->runThrough();
511
            parentShapeS1 = parentShapeS1->parent();
512 513
        }
    }
514 515 516 517 518 519 520

    // If the shape runs through the foreground or background.
    if (runThrough1 > runThrough2) {
        return false;
    }
    if (runThrough1 < runThrough2) {
        return true;
521 522
    }

523 524 525 526 527 528 529 530
    // If the one shape is a parent/child of the other then sort so.
    if (s1 == parentShapeS2) {
        return true;
    }
    if (s2 == parentShapeS1) {
        return false;
    }

531
    // If we went that far then the z-Index is used for sorting.
532
    return index1 < index2;
Thomas Zander's avatar
Thomas Zander committed
533 534
}

535 536
void KoShape::setParent(KoShapeContainer *parent)
{
537
    Q_D(KoShape);
538
    if (d->parent == parent)
539
        return;
540 541 542
    KoShapeContainer *oldParent = d->parent;
    d->parent = 0; // avoids recursive removing
    if (oldParent)
543
        oldParent->removeShape(this);
Thomas Zander's avatar
Thomas Zander committed
544
    if (parent && parent != this) {
Thomas Zander's avatar
Thomas Zander committed
545
        d->parent = parent;
546
        parent->addShape(this);
547
    }
548
    notifyChanged();
549
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
550 551
}

552 553
int KoShape::zIndex() const
{
554
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
555
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
556 557
}

558 559
void KoShape::update() const
{
560
    Q_D(const KoShape);
561

562 563
    if (!d->shapeManagers.empty()) {
        QRectF rect(boundingRect());
564
        foreach(KoShapeManager * manager, d->shapeManagers) {
Jan Hambrecht's avatar
Jan Hambrecht committed
565
            manager->update(rect, this, true);
566
        }
Thomas Zander's avatar
Thomas Zander committed
567 568 569
    }
}

570
void KoShape::update(const QRectF &rect) const
571
{
572 573 574 575 576

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

577
    Q_D(const KoShape);
578

579
    if (!d->shapeManagers.empty() && isVisible()) {
580
        QRectF rc(absoluteTransformation(0).mapRect(rect));
581
        foreach(KoShapeManager * manager, d->shapeManagers) {
582
            manager->update(rc);
583 584
        }
    }
Thomas Zander's avatar
Thomas Zander committed
585 586
}

587
QPainterPath KoShape::outline() const
588
{
Thomas Zander's avatar
Thomas Zander committed
589
    QPainterPath path;
590
    path.addRect(outlineRect());
Thomas Zander's avatar
Thomas Zander committed
591 592 593
    return path;
}

594 595
QRectF KoShape::outlineRect() const
{
596 597
    const QSizeF s = size();
    return QRectF(QPointF(0, 0), QSizeF(qMax(s.width(), qreal(0.0001)), qMax(s.height(), qreal(0.0001))));
598 599
}

600 601
QPointF KoShape::absolutePosition(KoFlake::Position anchor) const
{
602
    QPointF point;
603 604 605 606 607 608
    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;
609
    }
610
    return absoluteTransformation(0).map(point);
Thomas Zander's avatar
Thomas Zander committed
611 612
}

613 614
void KoShape::setAbsolutePosition(QPointF newPosition, KoFlake::Position anchor)
{
615
    Q_D(KoShape);
616
    QPointF currentAbsPosition = absolutePosition(anchor);
617
    QPointF translate = newPosition - currentAbsPosition;
618
    QTransform translateMatrix;
619 620
    translateMatrix.translate(translate.x(), translate.y());
    applyAbsoluteTransformation(translateMatrix);
621
    notifyChanged();
622
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
623 624
}

625 626
void KoShape::copySettings(const KoShape *shape)
{
627
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
628 629
    d->size = shape->size();
    d->connectors.clear();
630
    foreach(const KoConnectionPoint &point, shape->connectionPoints())
Jan Hambrecht's avatar
Jan Hambrecht committed
631
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
632 633
    d->zIndex = shape->zIndex();
    d->visible = shape->isVisible();
634

635 636 637 638 639
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
640

641
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
642 643
    d->protectContent = shape->isContentProtected();
    d->selectable = shape->isSelectable();
Thomas Zander's avatar
Thomas Zander committed
644
    d->keepAspect = shape->keepAspectRatio();
645
    d->localMatrix = shape->d_ptr->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
646 647
}

Thomas Zander's avatar
Thomas Zander committed
648
void KoShape::notifyChanged()
649
{
650
    Q_D(KoShape);
651 652
    foreach(KoShapeManager * manager, d->shapeManagers) {
        manager->notifyShapeChanged(this);
653 654 655
    }
}

656 657
void KoShape::setUserData(KoShapeUserData *userData)
{
658
    Q_D(KoShape);
659
    delete d->userData;
Thomas Zander's avatar
Thomas Zander committed
660
    d->userData = userData;
661 662
}

663 664
KoShapeUserData *KoShape::userData() const
{
665
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
666
    return d->userData;
667 668
}

669 670
void KoShape::setApplicationData(KoShapeApplicationData *appData)
{
671
    Q_D(KoShape);
672
    // appdata is deleted by the application.
Thomas Zander's avatar
Thomas Zander committed
673
    d->appData = appData;
674 675
}

676 677
KoShapeApplicationData *KoShape::applicationData() const
{
678
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
679
    return d->appData;
680 681
}

Thomas Zander's avatar
Thomas Zander committed
682
bool KoShape::hasTransparency() const
683
{
Thomas Zander's avatar
Thomas Zander committed
684
    Q_D(const KoShape);
685
    if (! d->fill)
Thomas Zander's avatar
Thomas Zander committed
686
        return true;
687
    else
688 689 690 691 692 693
        return d->fill->hasTransparency() || d->transparency > 0.0;
}

void KoShape::setTransparency(qreal transparency)
{
    Q_D(KoShape);
694
    d->transparency = qBound<qreal>(0.0, transparency, 1.0);
695 696 697 698 699 700 701 702 703 704 705 706
}

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
707 708
}

709 710
KoInsets KoShape::borderInsets() const
{
711
    Q_D(const KoShape);
712
    KoInsets answer;
713
    if (d->border)
714
        d->border->borderInsets(this, answer);
715 716 717
    return answer;
}

718 719
qreal KoShape::rotation() const
{
720
    Q_D(const KoShape);
721
    // try to extract the rotation angle out of the local matrix
722 723 724
    // if it is a pure rotation matrix

    // check if the matrix has shearing mixed in
725
    if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
726
        return std::numeric_limits<qreal>::quiet_NaN();
727
    // check if the matrix has scaling mixed in
728
    if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
729
        return std::numeric_limits<qreal>::quiet_NaN();
730 731

    // calculate the angle from the matrix elements
732 733
    qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
    if (angle < 0.0)
734 735 736
        angle += 360.0;

    return angle;
Thomas Zander's avatar
Thomas Zander committed
737 738
}

739
QSizeF KoShape::size() const
740
{
741
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
742 743 744
    return d->size;
}

745 746
QPointF KoShape::position() const
{
747
    Q_D(const KoShape);
748 749
    QPointF center(0.5*size().width(), 0.5*size().height());
    return d->localMatrix.map(center) - center;
Thomas Zander's avatar
Thomas Zander committed
750 751
}

752
int KoShape::addConnectionPoint(const KoConnectionPoint &point)
753
{
754
    Q_D(KoShape);
755

756
    // get next glue point id
757
    int nextConnectionPointId = KoConnectionPoint::FirstCustomConnectionPoint;
Jan Hambrecht's avatar
Jan Hambrecht committed
758
    if (d->connectors.size())
759 760
        nextConnectionPointId = qMax(nextConnectionPointId, (--d->connectors.end()).key()+1);

761
    KoConnectionPoint p = point;
762 763
    d->convertFromShapeCoordinates(p, size());
    d->connectors[nextConnectionPointId] = p;
764 765 766 767

    return nextConnectionPointId;
}

768
bool KoShape::setConnectionPoint(int connectionPointId, const KoConnectionPoint &point)
769 770
{
    Q_D(KoShape);
771
    if (connectionPointId < 0)
772 773
        return false;

774 775
    const bool insertPoint = !hasConnectionPoint(connectionPointId);

776
    switch(connectionPointId) {
777 778 779 780 781 782
        case KoConnectionPoint::TopConnectionPoint:
        case KoConnectionPoint::RightConnectionPoint:
        case KoConnectionPoint::BottomConnectionPoint:
        case KoConnectionPoint::LeftConnectionPoint:
        {
            KoConnectionPoint::PointId id = static_cast<KoConnectionPoint::PointId>(connectionPointId);
783
            d->connectors[id] = KoConnectionPoint::defaultConnectionPoint(id);
784
            break;
785
        }
786
        default:
787
        {
788
            KoConnectionPoint p = point;
789 790
            d->convertFromShapeCoordinates(p, size());
            d->connectors[connectionPointId] = p;
791
            break;
792
        }
793 794
    }

795 796
    if(!insertPoint)
        d->shapeChanged(ConnectionPointChanged);
797 798 799 800

    return true;
}

801 802 803 804 805 806
bool KoShape::hasConnectionPoint(int connectionPointId) const
{
    Q_D(const KoShape);
    return d->connectors.contains(connectionPointId);
}

807
KoConnectionPoint KoShape::connectionPoint(int connectionPointId) const
808 809
{
    Q_D(const KoShape);
810
    KoConnectionPoint p = d->connectors.value(connectionPointId, KoConnectionPoint());
811
    // convert glue point to shape coordinates
812
    d->convertToShapeCoordinates(p, size());
813
    return p;
Thomas Zander's avatar
Thomas Zander committed
814 815
}

816
KoConnectionPoints KoShape::connectionPoints() const
817
{
818
    Q_D(const KoShape);
819
    QSizeF s = size();
820 821 822
    KoConnectionPoints points = d->connectors;
    KoConnectionPoints::iterator point = points.begin();
    KoConnectionPoints::iterator lastPoint = points.end();
823
    // convert glue points to shape coordinates
824
    for(; point != lastPoint; ++point) {
825
        d->convertToShapeCoordinates(point.value(), s);
826
    }
827 828

    return points;
829 830
}

831 832 833 834
void KoShape::removeConnectionPoint(int connectionPointId)
{
    Q_D(KoShape);
    d->connectors.remove(connectionPointId);
835
    d->shapeChanged(ConnectionPointChanged);
836 837 838 839 840 841 842 843
}

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

844
void KoShape::addEventAction(KoEventAction *action)
845
{
846
    Q_D(KoShape);
847
    d->eventActions.insert(action);
848 849
}

850
void KoShape::removeEventAction(KoEventAction *action)
851
{
852
    Q_D(KoShape);
853
    d->eventActions.remove(action);
854 855
}

856
QSet<KoEventAction *> KoShape::eventActions() const
857
{
858
    Q_D(const KoShape);
859 860 861
    return d->eventActions;
}

862 863 864 865 866 867 868 869 870 871 872 873 874 875
KoShape::TextRunAroundSide KoShape::textRunAroundSide() const
{
    Q_D(const KoShape);
    return d->textRunAroundSide;
}

void KoShape::setTextRunAroundSide(TextRunAroundSide side, Through runThrought)
{
    Q_D