KoShape.cpp 63 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"
Thorsten Zachmann's avatar
Thorsten Zachmann committed
46 47
#include "KoEventAction.h"
#include "KoEventActionRegistry.h"
48
#include "KoOdfWorkaround.h"
49
#include "KoFilterEffectStack.h"
50
#include <KoSnapData.h>
Thomas Zander's avatar
Thomas Zander committed
51

52
#include <KoXmlReader.h>
53
#include <KoXmlWriter.h>
54
#include <KoXmlNS.h>
55
#include <KoGenStyle.h>
56
#include <KoGenStyles.h>
57
#include <KoUnit.h>
58
#include <KoOdfStylesReader.h>
59
#include <KoOdfGraphicStyles.h>
60
#include <KoOdfLoadingContext.h>
61

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

70 71
#include <limits>

72 73
// KoShapeCache

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

// KoShapePrivate

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

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

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

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

168 169 170 171 172 173 174 175
void KoShapePrivate::addShapeManager(KoShapeManager *manager)
{
    shapeManagers.insert(manager);
}

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

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

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;
    }
}

206
void KoShapePrivate::convertFromShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
207
{
208 209 210 211 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 249 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
    switch(point.align) {
        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
{
    switch(point.align) {
        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;
    }
278 279
}

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

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

    return value;
}

293 294


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

302 303 304 305 306
KoShape::KoShape(KoShapePrivate &dd)
    : d_ptr(&dd)
{
}

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

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

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

347
    notifyChanged();
348
    d->shapeChanged(ScaleChanged);
Thomas Zander's avatar
Thomas Zander committed
349 350
}

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

361
    notifyChanged();
362
    d->shapeChanged(RotationChanged);
Thomas Zander's avatar
Thomas Zander committed
363 364
}

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

375
    notifyChanged();
376
    d->shapeChanged(ShearChanged);
Thomas Zander's avatar
Thomas Zander committed
377 378
}

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

Thomas Zander's avatar
Thomas Zander committed
386
    d->size = newSize;
387

388
    notifyChanged();
389
    d->shapeChanged(SizeChanged);
Thomas Zander's avatar
Thomas Zander committed
390 391
}

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

    notifyChanged();
403
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
404 405
}

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

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

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

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

430
    return bb.contains(point);
Thomas Zander's avatar
Thomas Zander committed
431 432
}

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

455
    return bb;
Thomas Zander's avatar
Thomas Zander committed
456 457
}

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

478
    if (converter) {
479 480 481
        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
482
    }
483 484

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
485 486
}

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

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

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

513
QTransform KoShape::transformation() const
514
{
515
    Q_D(const KoShape);
516 517 518
    return d->localMatrix;
}

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

528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
    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();
543
        }
544 545 546 547

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

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

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

582 583
void KoShape::update() const
{
584
    Q_D(const KoShape);
585 586 587 588 589 590 591 592 593 594

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

    }

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

603
void KoShape::update(const QRectF &rect) const
604
{
605 606 607 608 609

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

610
    Q_D(const KoShape);
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626

    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);
                }
            }
        }
    }

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

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

642 643 644 645 646 647
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))));
}

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

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

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

683 684 685 686 687
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
688

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

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

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

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

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

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

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

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

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
759 760
}

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

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

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

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

    return angle;
Thomas Zander's avatar
Thomas Zander committed
789 790
}

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

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

804
int KoShape::addConnectionPoint(const KoConnectionPoint &point)
805
{
806
    Q_D(KoShape);
807

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

813
    KoConnectionPoint p = point;
814 815
    d->convertFromShapeCoordinates(p, size());
    d->connectors[nextConnectionPointId] = p;
816 817 818 819

    return nextConnectionPointId;
}

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

826 827
    const bool insertPoint = !hasConnectionPoint(connectionPointId);

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

847 848
    if(!insertPoint)
        d->shapeChanged(ConnectionPointChanged);
849 850 851 852

    return true;
}

853 854 855 856 857 858
bool KoShape::hasConnectionPoint(int connectionPointId) const
{
    Q_D(const KoShape);
    return d->connectors.contains(connectionPointId);
}

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

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

    return points;
881 882
}

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

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

896
void KoShape::addEventAction(KoEventAction *action)
897
{
898
    Q_D(KoShape);
899
    d->eventActions.insert(action);
900 901
}

902
void KoShape::removeEventAction(KoEventAction *action)
903
{
904
    Q_D(KoShape);
905
    d->eventActions.remove(action);
906 907
}

908
QSet<KoEventAction *> KoShape::eventActions() const
909
{
910
    Q_D(const KoShape);
911 912 913
    return d->eventActions;
}

914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932
KoShape::TextRunAroundSide KoShape::textRunAroundSide() const
{
    Q_D(const KoShape);
    return d->textRunAroundSide;
}

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

    if (side == RunThrough) {
        if (runThrought == Background) {
            setRunThrough(-1);
        } else {
            setRunThrough(1);
        }
    } else {
        setRunThrough(0);
    }
933 934 935 936 937 938 939 940

    if ( d->textRunAroundSide == side) {
        return;</