KoShape.cpp 63.5 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
// KoShapeCache

75
/// Empty all cached images from the image cache
76 77 78 79 80 81 82 83
void KoShapeCache::purge()
{
    qDeleteAll(deviceData);
    deviceData.clear();
}

// KoShapePrivate

84 85
KoShapePrivate::KoShapePrivate(KoShape *shape)
    : size(50, 50),
86 87 88 89 90 91 92
      parent(0),
      userData(0),
      appData(0),
      fill(0),
      border(0),
      q_ptr(shape),
      shadow(0),
93
      clipPath(0),
94 95 96
      filterEffectStack(0),
      transparency(0.0),
      zIndex(0),
97
      runThrough(0),
98 99 100 101 102 103 104
      visible(true),
      printable(true),
      geometryProtected(false),
      keepAspect(false),
      selectable(true),
      detectCollision(false),
      protectContent(false),
105
      cacheMode(KoShape::NoCache),
106 107 108
      cache(0),
      textRunAroundSide(KoShape::BiggestRunAroundSide),
      textRunAroundDistance(1.0)
109
{
110 111 112 113
    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);
114
}
Thomas Zander's avatar
Thomas Zander committed
115

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

139 140
void KoShapePrivate::shapeChanged(KoShape::ChangeType type)
{
141
    Q_Q(KoShape);
142
    if (parent)
143 144
        parent->model()->childChanged(q, type);
    q->shapeChanged(type);
145
    foreach(KoShape * shape, dependees)
146
        shape->shapeChanged(type, q);
147
}
148

149 150
void KoShapePrivate::updateBorder()
{
151
    Q_Q(KoShape);
152 153 154 155 156 157 158
    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,
159
                     inner.height() + insets.top + insets.bottom));
160 161
    // update top
    q->update(QRectF(-insets.left, -insets.top,
162
                     inner.width() + insets.left + insets.right, insets.top));
163 164
    // update right
    q->update(QRectF(inner.width(), -insets.top, insets.right,
165
                     inner.height() + insets.top + insets.bottom));
166 167
    // update bottom
    q->update(QRectF(-insets.left, inner.height(),
168
                     inner.width() + insets.left + insets.right, insets.bottom));
169
}
Thomas Zander's avatar
Thomas Zander committed
170

171 172 173 174 175 176 177 178
void KoShapePrivate::addShapeManager(KoShapeManager *manager)
{
    shapeManagers.insert(manager);
}

void KoShapePrivate::removeShapeManager(KoShapeManager *manager)
{
    shapeManagers.remove(manager);
179 180 181 182 183 184 185 186 187 188 189
    if (cacheMode == KoShape::ScaledCache) {
        if (KoShapeCache *cache = maybeShapeCache()) {
            KoShapeCache::DeviceData *deviceData = cache->deviceData.take(manager);
            delete deviceData;
        }
    }
}

KoShapeCache *KoShapePrivate::maybeShapeCache() const
{
    return cache;
190
}
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208

KoShapeCache *KoShapePrivate::shapeCache() const
{
    if (!cache) {
        const_cast<KoShapePrivate *>(this)->cache = new KoShapeCache;
    }
    return cache;
}

void KoShapePrivate::removeShapeCache()
{
    if (cache) {
        cache->purge();
        delete cache;
        cache = 0;
    }
}

209
void KoShapePrivate::convertFromShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
210
{
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 243 244 245 246 247 248
        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
249
    switch(point.alignment) {
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
        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;
    }
281 282
}

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

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

    return value;
}

296 297


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

305 306 307 308 309
KoShape::KoShape(KoShapePrivate &dd)
    : d_ptr(&dd)
{
}

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

318 319
void KoShape::paintDecorations(QPainter &painter, const KoViewConverter &converter, const KoCanvasBase *canvas)
{
Thomas Zander's avatar
Thomas Zander committed
320 321 322
    Q_UNUSED(painter);
    Q_UNUSED(converter);
    Q_UNUSED(canvas);
323
    /* Since this code is not actually used (kivio is going to be the main user) lets disable instead of fix.
324
        if (selected)
Thomas Zander's avatar
Thomas Zander committed
325
        {
326
            // draw connectors
327 328 329 330 331
            QPen pen(Qt::blue);
            pen.setWidth(0);
            painter.setPen(pen);
            painter.setBrush(Qt::NoBrush);
            for (int i = 0; i < d->connectors.size(); ++i)
332
          {
333
                QPointF p = converter.documentToView(d->connectors[ i ]);
334 335
                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));
336 337
            }
        }*/
Thomas Zander's avatar
Thomas Zander committed
338 339
}

Thomas Zander's avatar
Thomas Zander committed
340
void KoShape::scale(qreal sx, qreal sy)
Thomas Zander's avatar
Thomas Zander committed
341
{
342
    Q_D(KoShape);
343
    QPointF pos = position();
344
    QTransform scaleMatrix;
345 346 347
    scaleMatrix.translate(pos.x(), pos.y());
    scaleMatrix.scale(sx, sy);
    scaleMatrix.translate(-pos.x(), -pos.y());
348
    d->localMatrix = d->localMatrix * scaleMatrix;
349

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

354
void KoShape::rotate(qreal angle)
Thomas Zander's avatar
Thomas Zander committed
355
{
356
    Q_D(KoShape);
357
    QPointF center = d->localMatrix.map(QPointF(0.5 * size().width(), 0.5 * size().height()));
358
    QTransform rotateMatrix;
359 360 361
    rotateMatrix.translate(center.x(), center.y());
    rotateMatrix.rotate(angle);
    rotateMatrix.translate(-center.x(), -center.y());
362
    d->localMatrix = d->localMatrix * rotateMatrix;
363

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

Thomas Zander's avatar
Thomas Zander committed
368
void KoShape::shear(qreal sx, qreal sy)
Thomas Zander's avatar
Thomas Zander committed
369
{
370
    Q_D(KoShape);
371
    QPointF pos = position();
372
    QTransform shearMatrix;
373 374 375
    shearMatrix.translate(pos.x(), pos.y());
    shearMatrix.shear(sx, sy);
    shearMatrix.translate(-pos.x(), -pos.y());
376
    d->localMatrix = d->localMatrix * shearMatrix;
377

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

382
void KoShape::setSize(const QSizeF &newSize)
Thomas Zander's avatar
Thomas Zander committed
383
{
384
    Q_D(KoShape);
385 386
    QSizeF oldSize(size());
    if (oldSize == newSize)
Thomas Zander's avatar
Thomas Zander committed
387
        return;
388

Thomas Zander's avatar
Thomas Zander committed
389
    d->size = newSize;
390

391
    notifyChanged();
392
    d->shapeChanged(SizeChanged);
Thomas Zander's avatar
Thomas Zander committed
393 394
}

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

    notifyChanged();
406
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
407 408
}

409
bool KoShape::hitTest(const QPointF &position) const
Thomas Zander's avatar
Thomas Zander committed
410
{
411
    Q_D(const KoShape);
412
    if (d->parent && d->parent->isClipped(this) && !d->parent->hitTest(position))
Thomas Zander's avatar
Thomas Zander committed
413 414
        return false;

415
    QPointF point = absoluteTransformation(0).inverted().map(position);
416
    QRectF bb(QPointF(), size());
417
    if (d->border) {
418
        KoInsets insets;
419
        d->border->borderInsets(this, insets);
420
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
421
    }
422 423 424
    if (bb.contains(point))
        return true;

425
    // if there is no shadow we can as well just leave
426
    if (! d->shadow)
427
        return false;
Thomas Zander's avatar
Thomas Zander committed
428

429 430
    // the shadow has an offset to the shape, so we simply
    // check if the position minus the shadow offset hits the shape
431
    point = absoluteTransformation(0).inverted().map(position - d->shadow->offset());
432

433
    return bb.contains(point);
Thomas Zander's avatar
Thomas Zander committed
434 435
}

436
QRectF KoShape::boundingRect() const
Thomas Zander's avatar
Thomas Zander committed
437
{
438
    Q_D(const KoShape);
439
    QSizeF mySize = size();
440
    QTransform transform = absoluteTransformation(0);
441
    QRectF bb = outlineRect();
442
    if (d->border) {
443 444 445 446
        KoInsets insets;
        d->border->borderInsets(this, insets);
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
447
    bb = transform.mapRect(bb);
448
    if (d->shadow) {
449
        KoInsets insets;
Thomas Zander's avatar
Thomas Zander committed
450
        d->shadow->insets(insets);
451 452
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
453
    if (d->filterEffectStack) {
454
        QRectF clipRect = d->filterEffectStack->clipRectForBoundingRect(outlineRect());
455 456
        bb |= transform.mapRect(clipRect);
    }
Thomas Zander's avatar
Thomas Zander committed
457

458
    return bb;
Thomas Zander's avatar
Thomas Zander committed
459 460
}

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

481
    if (converter) {
482 483 484
        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
485
    }
486 487

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
488 489
}

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

500
void KoShape::applyTransformation(const QTransform &matrix)
501
{
502
    Q_D(KoShape);
503
    d->localMatrix = matrix * d->localMatrix;
504
    notifyChanged();
505
    d->shapeChanged(GenericMatrixChange);
506 507
}

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

516
QTransform KoShape::transformation() const
517
{
518
    Q_D(const KoShape);
519 520 521
    return d->localMatrix;
}

522 523
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
{
524 525 526 527 528 529 530
    if(s1->runThrough() > s2->runThrough()) {
        return false;
    }
    if(s1->runThrough() < s2->runThrough()) {
        return true;
    }

531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
    bool foundCommonParent = false;
    KoShape *parentShapeS1 = s1;
    KoShape *parentShapeS2 = s2;
    int index1 = parentShapeS1->zIndex();
    int index2 = parentShapeS2->zIndex();
    while (parentShapeS1 && !foundCommonParent) {
        parentShapeS2 = s2;
        index2 = parentShapeS2->zIndex();
        while (parentShapeS2) {
            if (parentShapeS2 == parentShapeS1) {
                foundCommonParent = true;
                break;
            }
            index2 = parentShapeS2->zIndex();
            parentShapeS2 = parentShapeS2->parent();
546
        }
547 548 549 550

        if (!foundCommonParent) {
            index1 = parentShapeS1->zIndex();
            parentShapeS1 = parentShapeS1->parent();
551 552
        }
    }
553 554 555 556 557 558 559
    if (s1 == parentShapeS2) {
        return true;
    }
    else if (s2 == parentShapeS1) {
        return false;
    }
    return index1 < index2;
Thomas Zander's avatar
Thomas Zander committed
560 561
}

562 563
void KoShape::setParent(KoShapeContainer *parent)
{
564
    Q_D(KoShape);
565
    if (d->parent == parent)
566
        return;
567 568 569
    KoShapeContainer *oldParent = d->parent;
    d->parent = 0; // avoids recursive removing
    if (oldParent)
570
        oldParent->removeShape(this);
Thomas Zander's avatar
Thomas Zander committed
571
    if (parent && parent != this) {
Thomas Zander's avatar
Thomas Zander committed
572
        d->parent = parent;
573
        parent->addShape(this);
574
    }
575
    notifyChanged();
576
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
577 578
}

579 580
int KoShape::zIndex() const
{
581
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
582
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
583 584
}

585 586
void KoShape::update() const
{
587
    Q_D(const KoShape);
588 589 590 591 592 593 594 595 596 597

    if (d->cacheMode != NoCache) {
        KoShapeCache *cache = d->shapeCache();
        foreach(KoShapeCache::DeviceData *data, cache->deviceData.values()) {
            data->allExposed = true;
            data->exposed.clear();
        }

    }

598 599
    if (!d->shapeManagers.empty()) {
        QRectF rect(boundingRect());
600
        foreach(KoShapeManager * manager, d->shapeManagers) {
Jan Hambrecht's avatar
Jan Hambrecht committed
601
            manager->update(rect, this, true);
602
        }
Thomas Zander's avatar
Thomas Zander committed
603 604 605
    }
}

606
void KoShape::update(const QRectF &rect) const
607
{
608 609 610 611 612

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

613
    Q_D(const KoShape);
614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629

    if (d->cacheMode != NoCache) {
        KoShapeCache *cache = d->shapeCache();
        foreach(KoShapeCache::DeviceData *data, cache->deviceData.values()) {
            if (!data->allExposed) {
                if (rect.isNull()) {
                    data->allExposed = true;
                    data->exposed.clear();
                }
                else {
                    data->exposed.append(rect);
                }
            }
        }
    }

630
    if (!d->shapeManagers.empty() && isVisible()) {
631
        QRectF rc(absoluteTransformation(0).mapRect(rect));
632
        foreach(KoShapeManager * manager, d->shapeManagers) {
633
            manager->update(rc);
634 635
        }
    }
Thomas Zander's avatar
Thomas Zander committed
636 637
}

638
QPainterPath KoShape::outline() const
639
{
Thomas Zander's avatar
Thomas Zander committed
640
    QPainterPath path;
641
    path.addRect(outlineRect());
Thomas Zander's avatar
Thomas Zander committed
642 643 644
    return path;
}

645 646 647 648 649 650
QRectF KoShape::outlineRect() const
{
    Q_D(const KoShape);
    return QRectF(QPointF(0, 0), QSizeF(qMax(d->size.width(), qreal(0.0001)), qMax(d->size.height(), qreal(0.0001))));
}

651 652
QPointF KoShape::absolutePosition(KoFlake::Position anchor) const
{
653
    QPointF point;
654 655 656 657 658 659
    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;
660
    }
661
    return absoluteTransformation(0).map(point);
Thomas Zander's avatar
Thomas Zander committed
662 663
}

664 665
void KoShape::setAbsolutePosition(QPointF newPosition, KoFlake::Position anchor)
{
666
    Q_D(KoShape);
667
    QPointF currentAbsPosition = absolutePosition(anchor);
668
    QPointF translate = newPosition - currentAbsPosition;
669
    QTransform translateMatrix;
670 671
    translateMatrix.translate(translate.x(), translate.y());
    applyAbsoluteTransformation(translateMatrix);
672
    notifyChanged();
673
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
674 675
}

676 677
void KoShape::copySettings(const KoShape *shape)
{
678
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
679 680
    d->size = shape->size();
    d->connectors.clear();
681
    foreach(const KoConnectionPoint &point, shape->connectionPoints())
Jan Hambrecht's avatar
Jan Hambrecht committed
682
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
683 684
    d->zIndex = shape->zIndex();
    d->visible = shape->isVisible();
685

686 687 688 689 690
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
691

692
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
693 694
    d->protectContent = shape->isContentProtected();
    d->selectable = shape->isSelectable();
Thomas Zander's avatar
Thomas Zander committed
695
    d->keepAspect = shape->keepAspectRatio();
696
    d->localMatrix = shape->d_ptr->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
697 698
}

Thomas Zander's avatar
Thomas Zander committed
699
void KoShape::notifyChanged()
700
{
701
    Q_D(KoShape);
702 703
    foreach(KoShapeManager * manager, d->shapeManagers) {
        manager->notifyShapeChanged(this);
704
    }
705 706 707 708
    KoShapeCache *cache = d->maybeShapeCache();
    if (cache) {
        cache->purge();
    }
709 710
}

711 712
void KoShape::setUserData(KoShapeUserData *userData)
{
713
    Q_D(KoShape);
714
    delete d->userData;
Thomas Zander's avatar
Thomas Zander committed
715
    d->userData = userData;
716 717
}

718 719
KoShapeUserData *KoShape::userData() const
{
720
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
721
    return d->userData;
722 723
}

724 725
void KoShape::setApplicationData(KoShapeApplicationData *appData)
{
726
    Q_D(KoShape);
727
    // appdata is deleted by the application.
Thomas Zander's avatar
Thomas Zander committed
728
    d->appData = appData;
729 730
}

731 732
KoShapeApplicationData *KoShape::applicationData() const
{
733
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
734
    return d->appData;
735 736
}

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

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

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

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

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

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

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

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

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

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

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

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

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

    return nextConnectionPointId;
}

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

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

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

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

    return true;
}

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

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

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

    return points;
884 885
}

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

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

899
void KoShape::addEventAction(KoEventAction *action)
900
{
901
    Q_D(KoShape);
902
    d->eventActions.insert(action);
903 904
}

905
void KoShape::removeEventAction(KoEventAction *action)
906
{
907
    Q_D(KoShape);
908
    d->eventActions.remove(action);
909 910
}

911
QSet<KoEventAction *> KoShape::eventActions() const
912
{
913
    Q_D(const KoShape);
914 915