KoShape.cpp 56.1 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[KoFlake::TopConnectionPoint] = defaultConnectionPoint(KoFlake::TopConnectionPoint);
    connectors[KoFlake::RightConnectionPoint] = defaultConnectionPoint(KoFlake::RightConnectionPoint);
    connectors[KoFlake::BottomConnectionPoint] = defaultConnectionPoint(KoFlake::BottomConnectionPoint);
    connectors[KoFlake::LeftConnectionPoint] = defaultConnectionPoint(KoFlake::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 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
QPointF KoShapePrivate::defaultConnectionPoint(KoFlake::ConnectionPointId connectionPointId)
{
    switch(connectionPointId)
    {
        case KoFlake::TopConnectionPoint:
            return QPointF(0.5, 0.0);
        case KoFlake::RightConnectionPoint:
            return QPointF(1.0, 0.5);
        case KoFlake::BottomConnectionPoint:
            return QPointF(0.5, 1.0);
        case KoFlake::LeftConnectionPoint:
            return QPointF(0.0, 0.5);
        default:
            return QPointF();
    }
}
222
// static
223
QString KoShapePrivate::getStyleProperty(const char *property, KoShapeLoadingContext &context)
224 225 226 227 228 229 230 231 232 233 234
{
    KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
    QString value;

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

    return value;
}

235 236


237
// ======== KoShape
Thomas Zander's avatar
Thomas Zander committed
238
KoShape::KoShape()
239
    : d_ptr(new KoShapePrivate(this))
Thomas Zander's avatar
Thomas Zander committed
240
{
241
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
242 243
}

244 245 246 247 248
KoShape::KoShape(KoShapePrivate &dd)
    : d_ptr(&dd)
{
}

Thomas Zander's avatar
Thomas Zander committed
249 250
KoShape::~KoShape()
{
251
    Q_D(KoShape);
252
    d->shapeChanged(Deleted);
253
    d->removeShapeCache();
254
    delete d_ptr;
Thomas Zander's avatar
Thomas Zander committed
255 256
}

257 258
void KoShape::paintDecorations(QPainter &painter, const KoViewConverter &converter, const KoCanvasBase *canvas)
{
Thomas Zander's avatar
Thomas Zander committed
259 260 261
    Q_UNUSED(painter);
    Q_UNUSED(converter);
    Q_UNUSED(canvas);
262
    /* Since this code is not actually used (kivio is going to be the main user) lets disable instead of fix.
263
        if (selected)
Thomas Zander's avatar
Thomas Zander committed
264
        {
265
            // draw connectors
266 267 268 269 270
            QPen pen(Qt::blue);
            pen.setWidth(0);
            painter.setPen(pen);
            painter.setBrush(Qt::NoBrush);
            for (int i = 0; i < d->connectors.size(); ++i)
271
          {
272
                QPointF p = converter.documentToView(d->connectors[ i ]);
273 274
                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));
275 276
            }
        }*/
Thomas Zander's avatar
Thomas Zander committed
277 278
}

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

289
    notifyChanged();
290
    d->shapeChanged(ScaleChanged);
Thomas Zander's avatar
Thomas Zander committed
291 292
}

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

303
    notifyChanged();
304
    d->shapeChanged(RotationChanged);
Thomas Zander's avatar
Thomas Zander committed
305 306
}

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

317
    notifyChanged();
318
    d->shapeChanged(ShearChanged);
Thomas Zander's avatar
Thomas Zander committed
319 320
}

321
void KoShape::setSize(const QSizeF &newSize)
Thomas Zander's avatar
Thomas Zander committed
322
{
323
    Q_D(KoShape);
324 325
    QSizeF oldSize(size());
    if (oldSize == newSize)
Thomas Zander's avatar
Thomas Zander committed
326
        return;
327

Thomas Zander's avatar
Thomas Zander committed
328
    d->size = newSize;
329

330
    notifyChanged();
331
    d->shapeChanged(SizeChanged);
Thomas Zander's avatar
Thomas Zander committed
332 333
}

334
void KoShape::setPosition(const QPointF &newPosition)
Thomas Zander's avatar
Thomas Zander committed
335
{
336
    Q_D(KoShape);
337
    QPointF currentPos = position();
338
    if (newPosition == currentPos)
Thomas Zander's avatar
Thomas Zander committed
339
        return;
340
    QTransform translateMatrix;
341
    translateMatrix.translate(newPosition.x() - currentPos.x(), newPosition.y() - currentPos.y());
342 343 344
    d->localMatrix = d->localMatrix * translateMatrix;

    notifyChanged();
345
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
346 347
}

348
bool KoShape::hitTest(const QPointF &position) const
Thomas Zander's avatar
Thomas Zander committed
349
{
350
    Q_D(const KoShape);
351
    if (d->parent && d->parent->isClipped(this) && !d->parent->hitTest(position))
Thomas Zander's avatar
Thomas Zander committed
352 353
        return false;

354
    QPointF point = absoluteTransformation(0).inverted().map(position);
355
    QRectF bb(QPointF(), size());
356
    if (d->border) {
357
        KoInsets insets;
358
        d->border->borderInsets(this, insets);
359
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
360
    }
361 362 363
    if (bb.contains(point))
        return true;

364
    // if there is no shadow we can as well just leave
365
    if (! d->shadow)
366
        return false;
Thomas Zander's avatar
Thomas Zander committed
367

368 369
    // the shadow has an offset to the shape, so we simply
    // check if the position minus the shadow offset hits the shape
370
    point = absoluteTransformation(0).inverted().map(position - d->shadow->offset());
371

372
    return bb.contains(point);
Thomas Zander's avatar
Thomas Zander committed
373 374
}

375
QRectF KoShape::boundingRect() const
Thomas Zander's avatar
Thomas Zander committed
376
{
377
    Q_D(const KoShape);
378
    QSizeF mySize = size();
379
    QTransform transform = absoluteTransformation(0);
380
    QRectF bb = outlineRect();
381
    if (d->border) {
382 383 384 385
        KoInsets insets;
        d->border->borderInsets(this, insets);
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
386
    bb = transform.mapRect(bb);
387
    if (d->shadow) {
388
        KoInsets insets;
Thomas Zander's avatar
Thomas Zander committed
389
        d->shadow->insets(insets);
390 391
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
392
    if (d->filterEffectStack) {
393
        QRectF clipRect = d->filterEffectStack->clipRectForBoundingRect(outlineRect());
394 395
        bb |= transform.mapRect(clipRect);
    }
Thomas Zander's avatar
Thomas Zander committed
396

397
    return bb;
Thomas Zander's avatar
Thomas Zander committed
398 399
}

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

420
    if (converter) {
421 422 423
        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
424
    }
425 426

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
427 428
}

429
void KoShape::applyAbsoluteTransformation(const QTransform &matrix)
430
{
431
    QTransform globalMatrix = absoluteTransformation(0);
432 433
    // 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
434
    // to be relative to the local coordinate system
435
    QTransform transformMatrix = globalMatrix * matrix * globalMatrix.inverted();
436
    applyTransformation(transformMatrix);
437 438
}

439
void KoShape::applyTransformation(const QTransform &matrix)
440
{
441
    Q_D(KoShape);
442
    d->localMatrix = matrix * d->localMatrix;
443
    notifyChanged();
444
    d->shapeChanged(GenericMatrixChange);
445 446
}

447
void KoShape::setTransformation(const QTransform &matrix)
448
{
449
    Q_D(KoShape);
450 451
    d->localMatrix = matrix;
    notifyChanged();
452
    d->shapeChanged(GenericMatrixChange);
453
}
Thomas Zander's avatar
Thomas Zander committed
454

455
QTransform KoShape::transformation() const
456
{
457
    Q_D(const KoShape);
458 459 460
    return d->localMatrix;
}

461 462
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
{
463 464 465 466 467 468 469
    if(s1->runThrough() > s2->runThrough()) {
        return false;
    }
    if(s1->runThrough() < s2->runThrough()) {
        return true;
    }

470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
    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();
485
        }
486 487 488 489

        if (!foundCommonParent) {
            index1 = parentShapeS1->zIndex();
            parentShapeS1 = parentShapeS1->parent();
490 491
        }
    }
492 493 494 495 496 497 498
    if (s1 == parentShapeS2) {
        return true;
    }
    else if (s2 == parentShapeS1) {
        return false;
    }
    return index1 < index2;
Thomas Zander's avatar
Thomas Zander committed
499 500
}

501 502
void KoShape::setParent(KoShapeContainer *parent)
{
503
    Q_D(KoShape);
504
    if (d->parent == parent)
505
        return;
506 507 508
    KoShapeContainer *oldParent = d->parent;
    d->parent = 0; // avoids recursive removing
    if (oldParent)
509
        oldParent->removeShape(this);
Thomas Zander's avatar
Thomas Zander committed
510
    if (parent && parent != this) {
Thomas Zander's avatar
Thomas Zander committed
511
        d->parent = parent;
512
        parent->addShape(this);
513
    }
514
    notifyChanged();
515
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
516 517
}

518 519
int KoShape::zIndex() const
{
520
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
521
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
522 523
}

524 525
void KoShape::update() const
{
526
    Q_D(const KoShape);
527 528 529 530 531 532 533 534 535 536

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

    }

537 538
    if (!d->shapeManagers.empty()) {
        QRectF rect(boundingRect());
539
        foreach(KoShapeManager * manager, d->shapeManagers) {
Jan Hambrecht's avatar
Jan Hambrecht committed
540
            manager->update(rect, this, true);
541
        }
Thomas Zander's avatar
Thomas Zander committed
542 543 544
    }
}

545
void KoShape::update(const QRectF &rect) const
546
{
547 548 549 550 551

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

552
    Q_D(const KoShape);
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568

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

569
    if (!d->shapeManagers.empty() && isVisible()) {
570
        QRectF rc(absoluteTransformation(0).mapRect(rect));
571
        foreach(KoShapeManager * manager, d->shapeManagers) {
572
            manager->update(rc);
573 574
        }
    }
Thomas Zander's avatar
Thomas Zander committed
575 576
}

577
QPainterPath KoShape::outline() const
578
{
Thomas Zander's avatar
Thomas Zander committed
579
    QPainterPath path;
580
    path.addRect(outlineRect());
Thomas Zander's avatar
Thomas Zander committed
581 582 583
    return path;
}

584 585 586 587 588 589
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))));
}

590 591
QPointF KoShape::absolutePosition(KoFlake::Position anchor) const
{
592
    QPointF point;
593 594 595 596 597 598
    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;
599
    }
600
    return absoluteTransformation(0).map(point);
Thomas Zander's avatar
Thomas Zander committed
601 602
}

603 604
void KoShape::setAbsolutePosition(QPointF newPosition, KoFlake::Position anchor)
{
605
    Q_D(KoShape);
606
    QPointF currentAbsPosition = absolutePosition(anchor);
607
    QPointF translate = newPosition - currentAbsPosition;
608
    QTransform translateMatrix;
609 610
    translateMatrix.translate(translate.x(), translate.y());
    applyAbsoluteTransformation(translateMatrix);
611
    notifyChanged();
612
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
613 614
}

615 616
void KoShape::copySettings(const KoShape *shape)
{
617
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
618 619
    d->size = shape->size();
    d->connectors.clear();
620
    foreach(const QPointF &point, shape->connectionPoints())
Jan Hambrecht's avatar
Jan Hambrecht committed
621
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
622 623
    d->zIndex = shape->zIndex();
    d->visible = shape->isVisible();
624

625 626 627 628 629
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
630

631
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
632 633
    d->protectContent = shape->isContentProtected();
    d->selectable = shape->isSelectable();
Thomas Zander's avatar
Thomas Zander committed
634
    d->keepAspect = shape->keepAspectRatio();
635
    d->localMatrix = shape->d_ptr->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
636 637
}

Thomas Zander's avatar
Thomas Zander committed
638
void KoShape::notifyChanged()
639
{
640
    Q_D(KoShape);
641 642
    foreach(KoShapeManager * manager, d->shapeManagers) {
        manager->notifyShapeChanged(this);
643
    }
644 645 646 647
    KoShapeCache *cache = d->maybeShapeCache();
    if (cache) {
        cache->purge();
    }
648 649
}

650 651
void KoShape::setUserData(KoShapeUserData *userData)
{
652
    Q_D(KoShape);
653
    delete d->userData;
Thomas Zander's avatar
Thomas Zander committed
654
    d->userData = userData;
655 656
}

657 658
KoShapeUserData *KoShape::userData() const
{
659
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
660
    return d->userData;
661 662
}

663 664
void KoShape::setApplicationData(KoShapeApplicationData *appData)
{
665
    Q_D(KoShape);
666
    // appdata is deleted by the application.
Thomas Zander's avatar
Thomas Zander committed
667
    d->appData = appData;
668 669
}

670 671
KoShapeApplicationData *KoShape::applicationData() const
{
672
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
673
    return d->appData;
674 675
}

Thomas Zander's avatar
Thomas Zander committed
676
bool KoShape::hasTransparency() const
677
{
Thomas Zander's avatar
Thomas Zander committed
678
    Q_D(const KoShape);
679
    if (! d->fill)
Thomas Zander's avatar
Thomas Zander committed
680
        return true;
681
    else
682 683 684 685 686 687
        return d->fill->hasTransparency() || d->transparency > 0.0;
}

void KoShape::setTransparency(qreal transparency)
{
    Q_D(KoShape);
688
    d->transparency = qBound<qreal>(0.0, transparency, 1.0);
689 690 691 692 693 694 695 696 697 698 699 700
}

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
701 702
}

703 704
KoInsets KoShape::borderInsets() const
{
705
    Q_D(const KoShape);
706
    KoInsets answer;
707
    if (d->border)
708
        d->border->borderInsets(this, answer);
709 710 711
    return answer;
}

712 713
qreal KoShape::rotation() const
{
714
    Q_D(const KoShape);
715
    // try to extract the rotation angle out of the local matrix
716 717 718
    // if it is a pure rotation matrix

    // check if the matrix has shearing mixed in
719
    if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
720
        return std::numeric_limits<qreal>::quiet_NaN();
721
    // check if the matrix has scaling mixed in
722
    if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
723
        return std::numeric_limits<qreal>::quiet_NaN();
724 725

    // calculate the angle from the matrix elements
726 727
    qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
    if (angle < 0.0)
728 729 730
        angle += 360.0;

    return angle;
Thomas Zander's avatar
Thomas Zander committed
731 732
}

733
QSizeF KoShape::size() const
734
{
735
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
736 737 738
    return d->size;
}

739 740
QPointF KoShape::position() const
{
741
    Q_D(const KoShape);
742 743
    QPointF center(0.5*size().width(), 0.5*size().height());
    return d->localMatrix.map(center) - center;
Thomas Zander's avatar
Thomas Zander committed
744 745
}

746
int KoShape::addConnectionPoint(const QPointF &point)
747
{
748
    Q_D(KoShape);
749 750
    QSizeF s = size();
    // convert glue point from shape coordinates to factors of size
751 752 753
    QPointF connectionPoint(point.x() / s.width(), point.y() / s.height());
    // get next glue point id
    int nextConnectionPointId = KoFlake::FirstCustomConnectionPoint;
Jan Hambrecht's avatar
Jan Hambrecht committed
754
    if (d->connectors.size())
755 756 757 758 759 760 761
        nextConnectionPointId = qMax(nextConnectionPointId, (--d->connectors.end()).key()+1);

    d->connectors[nextConnectionPointId] = connectionPoint;

    return nextConnectionPointId;
}

762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788
bool KoShape::insertConnectionPoint(const QPointF &point, int connectionPointId)
{
    Q_D(KoShape);
    if (connectionPointId < 0 || d->connectors.contains(connectionPointId))
        return false;

    QSizeF s = size();
    // convert glue point from shape coordinates to factors of size
    QPointF connectionPoint(point.x() / s.width(), point.y() / s.height());

    switch(connectionPointId) {
        case KoFlake::TopConnectionPoint:
        case KoFlake::RightConnectionPoint:
        case KoFlake::BottomConnectionPoint:
        case KoFlake::LeftConnectionPoint:
            connectionPoint = d->defaultConnectionPoint(static_cast<KoFlake::ConnectionPointId>(connectionPointId));
            break;
        default:
            // do nothing for all other cases
            break;
    }

    d->connectors[connectionPointId] = connectionPoint;

    return true;
}

789 790 791 792 793 794 795 796 797 798 799 800 801 802
bool KoShape::hasConnectionPoint(int connectionPointId) const
{
    Q_D(const KoShape);
    return d->connectors.contains(connectionPointId);
}

QPointF KoShape::connectionPoint(int connectionPointId) const
{
    Q_D(const KoShape);
    QSizeF s = size();
    QPointF p = d->connectors.value(connectionPointId, QPointF());
    p.rx() *= s.width();
    p.ry() *= s.height();
    return p;
Thomas Zander's avatar
Thomas Zander committed
803 804
}

805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820
bool KoShape::setConnectionPointPosition(int connectionPointId, const QPointF &newPosition)
{
    // do not allow to change position of default connection points
    if (connectionPointId < KoFlake::FirstCustomConnectionPoint)
        return false;

    Q_D(KoShape);
    KoConnectionPoints::iterator cp = d->connectors.find(connectionPointId);
    // check if connection point exists
    if(cp == d->connectors.end())
        return false;

    QSizeF s = size();
    cp->rx() = newPosition.x() / s.width();
    cp->ry() = newPosition.y() / s.height();

821 822
    d->shapeChanged(ConnectionPointChanged);

823 824 825
    return true;
}

826
KoConnectionPoints KoShape::connectionPoints() const
827
{
828
    Q_D(const KoShape);
829
    QSizeF s = size();
830 831 832
    KoConnectionPoints points = d->connectors;
    KoConnectionPoints::iterator point = points.begin();
    KoConnectionPoints::iterator lastPoint = points.end();
833
    // convert glue points to shape coordinates
834 835 836 837
    for(; point != lastPoint; ++point) {
        point->rx() *= s.width();
        point->ry() *= s.height();
    }
838 839

    return points;
840 841
}

842 843 844 845
void KoShape::removeConnectionPoint(int connectionPointId)
{
    Q_D(KoShape);
    d->connectors.remove(connectionPointId);
846
    d->shapeChanged(ConnectionPointChanged);
847 848 849 850 851 852 853 854
}

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

855
void KoShape::addEventAction(KoEventAction *action)
856
{
857
    Q_D(KoShape);
858
    d->eventActions.insert(action);
859 860
}

861
void KoShape::removeEventAction(KoEventAction *action)
862
{
863
    Q_D(KoShape);
864
    d->eventActions.remove(action);
865 866
}

867
QSet<KoEventAction *> KoShape::eventActions() const
868
{
869
    Q_D(const KoShape);
870 871 872
    return d->eventActions;
}

873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891
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);
    }
892 893 894 895 896 897 898 899

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

    d->textRunAroundSide = side;
    notifyChanged();
    d->shapeChanged(TextRunAroundChanged);
900 901 902 903 904 905 906 907 908 909 910 911 912 913
}

qreal KoShape::textRunAroundDistance() const
{
    Q_D(const KoShape);
    return d->textRunAroundDistance;
}

void KoShape::setTextRunAroundDistance(qreal distance)
{
    Q_D(KoShape);
    d->textRunAroundDistance = distance;
}

914
void KoShape::setBackground(KoShapeBackground *fill)
915
{
916
    Q_D(KoShape);
917
    if (d->fill)
918
        d->fill->deref();
919
    d->fill = fill;
920
    if (d->fill)
921
        d->fill->ref();
922
    d->shapeChanged(BackgroundChanged);
Thomas Zander's avatar
Thomas Zander committed
923 924
}

925 926
KoShapeBackground * KoShape::background() const
{
927
    Q_D(const KoShape);
928
    return d->fill;
Thomas Zander's avatar
Thomas Zander committed
929 930
}

931 932
void KoShape::setZIndex(int zIndex)
{
933
    Q_D(KoShape);
934
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
935 936 937
    d->zIndex = zIndex;
}

938 939 940 941 942 943 944 945 946 947 948 949
int KoShape::runThrough()
{
    Q_D(const KoShape);
    return d->runThrough;
}

void KoShape::setRunThrough(short int runThrough)
{
    Q_D(KoShape);
    d->runThrough = runThrough;
}

950 951
void KoShape::setVisible(bool on)
{
952
    Q_D(KoShape);
953
    if (d->visible == on) return;
Thomas Zander's avatar
Thomas Zander committed
954
    d->visible = on;
955 956 957 958 959 960
    if (d->visible) {
        KoShapeCache *cache = d->maybeShapeCache();
        if (cache) {
            cache->purge();
        }
    }
Thomas Zander's avatar
Thomas Zander committed
961 962
}

963
bool KoShape::isVisible(bool recursive) const
964
{
965
    Q_D(const KoShape);