KoShape.cpp 57.6 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 223 224 225 226 227 228 229 230

void KoShapePrivate::setConnectionPoint(int id, const QPointF &position)
{
    // restrict position of connection point to bounding box
    const qreal x = qBound<qreal>(0.0, position.x(), 1.0);
    const qreal y = qBound<qreal>(0.0, position.y(), 1.0);
    connectors[id] = QPointF(x, y);
}

231
// static
232
QString KoShapePrivate::getStyleProperty(const char *property, KoShapeLoadingContext &context)
233 234 235 236 237 238 239 240 241 242 243
{
    KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
    QString value;

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

    return value;
}

244 245


246
// ======== KoShape
Thomas Zander's avatar
Thomas Zander committed
247
KoShape::KoShape()
248
    : d_ptr(new KoShapePrivate(this))
Thomas Zander's avatar
Thomas Zander committed
249
{
250
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
251 252
}

253 254 255 256 257
KoShape::KoShape(KoShapePrivate &dd)
    : d_ptr(&dd)
{
}

Thomas Zander's avatar
Thomas Zander committed
258 259
KoShape::~KoShape()
{
260
    Q_D(KoShape);
261
    d->shapeChanged(Deleted);
262
    d->removeShapeCache();
263
    delete d_ptr;
Thomas Zander's avatar
Thomas Zander committed
264 265
}

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

Thomas Zander's avatar
Thomas Zander committed
288
void KoShape::scale(qreal sx, qreal sy)
Thomas Zander's avatar
Thomas Zander committed
289
{
290
    Q_D(KoShape);
291
    QPointF pos = position();
292
    QTransform scaleMatrix;
293 294 295
    scaleMatrix.translate(pos.x(), pos.y());
    scaleMatrix.scale(sx, sy);
    scaleMatrix.translate(-pos.x(), -pos.y());
296
    d->localMatrix = d->localMatrix * scaleMatrix;
297

298
    notifyChanged();
299
    d->shapeChanged(ScaleChanged);
Thomas Zander's avatar
Thomas Zander committed
300 301
}

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

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

Thomas Zander's avatar
Thomas Zander committed
316
void KoShape::shear(qreal sx, qreal sy)
Thomas Zander's avatar
Thomas Zander committed
317
{
318
    Q_D(KoShape);
319
    QPointF pos = position();
320
    QTransform shearMatrix;
321 322 323
    shearMatrix.translate(pos.x(), pos.y());
    shearMatrix.shear(sx, sy);
    shearMatrix.translate(-pos.x(), -pos.y());
324
    d->localMatrix = d->localMatrix * shearMatrix;
325

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

330
void KoShape::setSize(const QSizeF &newSize)
Thomas Zander's avatar
Thomas Zander committed
331
{
332
    Q_D(KoShape);
333 334
    QSizeF oldSize(size());
    if (oldSize == newSize)
Thomas Zander's avatar
Thomas Zander committed
335
        return;
336

Thomas Zander's avatar
Thomas Zander committed
337
    d->size = newSize;
338

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

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

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

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

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

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

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

381
    return bb.contains(point);
Thomas Zander's avatar
Thomas Zander committed
382 383
}

384
QRectF KoShape::boundingRect() const
Thomas Zander's avatar
Thomas Zander committed
385
{
386
    Q_D(const KoShape);
387
    QSizeF mySize = size();
388
    QTransform transform = absoluteTransformation(0);
389
    QRectF bb = outlineRect();
390
    if (d->border) {
391 392 393 394
        KoInsets insets;
        d->border->borderInsets(this, insets);
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
395
    bb = transform.mapRect(bb);
396
    if (d->shadow) {
397
        KoInsets insets;
Thomas Zander's avatar
Thomas Zander committed
398
        d->shadow->insets(insets);
399 400
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
401
    if (d->filterEffectStack) {
402
        QRectF clipRect = d->filterEffectStack->clipRectForBoundingRect(outlineRect());
403 404
        bb |= transform.mapRect(clipRect);
    }
Thomas Zander's avatar
Thomas Zander committed
405

406
    return bb;
Thomas Zander's avatar
Thomas Zander committed
407 408
}

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

429
    if (converter) {
430 431 432
        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
433
    }
434 435

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
436 437
}

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

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

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

464
QTransform KoShape::transformation() const
465
{
466
    Q_D(const KoShape);
467 468 469
    return d->localMatrix;
}

470 471
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
{
472 473 474 475 476 477 478
    if(s1->runThrough() > s2->runThrough()) {
        return false;
    }
    if(s1->runThrough() < s2->runThrough()) {
        return true;
    }

479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
    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();
494
        }
495 496 497 498

        if (!foundCommonParent) {
            index1 = parentShapeS1->zIndex();
            parentShapeS1 = parentShapeS1->parent();
499 500
        }
    }
501 502 503 504 505 506 507
    if (s1 == parentShapeS2) {
        return true;
    }
    else if (s2 == parentShapeS1) {
        return false;
    }
    return index1 < index2;
Thomas Zander's avatar
Thomas Zander committed
508 509
}

510 511
void KoShape::setParent(KoShapeContainer *parent)
{
512
    Q_D(KoShape);
513
    if (d->parent == parent)
514
        return;
515 516 517
    KoShapeContainer *oldParent = d->parent;
    d->parent = 0; // avoids recursive removing
    if (oldParent)
518
        oldParent->removeShape(this);
Thomas Zander's avatar
Thomas Zander committed
519
    if (parent && parent != this) {
Thomas Zander's avatar
Thomas Zander committed
520
        d->parent = parent;
521
        parent->addShape(this);
522
    }
523
    notifyChanged();
524
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
525 526
}

527 528
int KoShape::zIndex() const
{
529
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
530
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
531 532
}

533 534
void KoShape::update() const
{
535
    Q_D(const KoShape);
536 537 538 539 540 541 542 543 544 545

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

    }

546 547
    if (!d->shapeManagers.empty()) {
        QRectF rect(boundingRect());
548
        foreach(KoShapeManager * manager, d->shapeManagers) {
Jan Hambrecht's avatar
Jan Hambrecht committed
549
            manager->update(rect, this, true);
550
        }
Thomas Zander's avatar
Thomas Zander committed
551 552 553
    }
}

554
void KoShape::update(const QRectF &rect) const
555
{
556 557 558 559 560

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

561
    Q_D(const KoShape);
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577

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

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

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

593 594 595 596 597 598
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))));
}

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

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

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

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

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

Thomas Zander's avatar
Thomas Zander committed
647
void KoShape::notifyChanged()
648
{
649
    Q_D(KoShape);
650 651
    foreach(KoShapeManager * manager, d->shapeManagers) {
        manager->notifyShapeChanged(this);
652
    }
653 654 655 656
    KoShapeCache *cache = d->maybeShapeCache();
    if (cache) {
        cache->purge();
    }
657 658
}

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

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

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

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

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

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

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
710 711
}

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

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

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

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

    return angle;
Thomas Zander's avatar
Thomas Zander committed
740 741
}

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

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

755
int KoShape::addConnectionPoint(const QPointF &point)
756
{
757
    Q_D(KoShape);
758
    QSizeF s = size();
759

760 761
    // get next glue point id
    int nextConnectionPointId = KoFlake::FirstCustomConnectionPoint;
Jan Hambrecht's avatar
Jan Hambrecht committed
762
    if (d->connectors.size())
763 764
        nextConnectionPointId = qMax(nextConnectionPointId, (--d->connectors.end()).key()+1);

765 766
    // convert glue point from shape coordinates to factors of size
    d->setConnectionPoint(nextConnectionPointId, QPointF(point.x()/s.width(), point.y()/s.height()));
767 768 769 770

    return nextConnectionPointId;
}

771 772 773 774 775 776
bool KoShape::insertConnectionPoint(const QPointF &point, int connectionPointId)
{
    Q_D(KoShape);
    if (connectionPointId < 0 || d->connectors.contains(connectionPointId))
        return false;

777
    QPointF connectionPoint;
778 779 780 781 782 783 784 785
    switch(connectionPointId) {
        case KoFlake::TopConnectionPoint:
        case KoFlake::RightConnectionPoint:
        case KoFlake::BottomConnectionPoint:
        case KoFlake::LeftConnectionPoint:
            connectionPoint = d->defaultConnectionPoint(static_cast<KoFlake::ConnectionPointId>(connectionPointId));
            break;
        default:
786 787 788 789 790
        {
            QSizeF s = size();
            // convert glue point from shape coordinates to factors of size
            connectionPoint.rx() = point.x() / s.width();
            connectionPoint.ry() = point.y() / s.height();
791
            break;
792
        }
793 794
    }

795
    d->setConnectionPoint(connectionPointId, connectionPoint);
796 797 798 799

    return true;
}

800 801 802 803 804 805 806 807 808 809 810 811 812 813
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
814 815
}

816 817 818 819 820 821 822
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);
823
    if (!d->connectors.contains(connectionPointId))
824 825 826
        return false;

    QSizeF s = size();
827 828 829 830
    // convert glue point from shape coordinates to factors of size
    const qreal x = newPosition.x() / s.width();
    const qreal y = newPosition.y() / s.height();
    d->setConnectionPoint(connectionPointId, QPointF(x, y));
831 832
    d->shapeChanged(ConnectionPointChanged);

833 834 835
    return true;
}

836
KoConnectionPoints KoShape::connectionPoints() const
837
{
838
    Q_D(const KoShape);
839
    QSizeF s = size();
840 841 842
    KoConnectionPoints points = d->connectors;
    KoConnectionPoints::iterator point = points.begin();
    KoConnectionPoints::iterator lastPoint = points.end();
843
    // convert glue points to shape coordinates
844 845 846 847
    for(; point != lastPoint; ++point) {
        point->rx() *= s.width();
        point->ry() *= s.height();
    }
848 849

    return points;
850 851
}

852 853 854 855
void KoShape::removeConnectionPoint(int connectionPointId)
{
    Q_D(KoShape);
    d->connectors.remove(connectionPointId);
856
    d->shapeChanged(ConnectionPointChanged);
857 858 859 860 861 862 863 864
}

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

865
void KoShape::addEventAction(KoEventAction *action)
866
{
867
    Q_D(KoShape);
868
    d->eventActions.insert(action);
869 870
}

871
void KoShape::removeEventAction(KoEventAction *action)
872
{
873
    Q_D(KoShape);
874
    d->eventActions.remove(action);
875 876
}

877
QSet<KoEventAction *> KoShape::eventActions() const
878
{
879
    Q_D(const KoShape);
880 881 882
    return d->eventActions;
}

883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901
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);
    }
902 903 904 905 906 907 908 909

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

    d->textRunAroundSide = side;
    notifyChanged();
    d->shapeChanged(TextRunAroundChanged);
910 911 912 913 914 915 916 917 918 919 920 921 922 923
}

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

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

924
void KoShape::setBackground(KoShapeBackground *fill)
925
{
926
    Q_D(KoShape);
927
    if (d->fill)
928
        d->fill->deref();
929
    d->fill = fill;
930
    if (d->fill)
931
        d->fill->ref();
932
    d->shapeChanged(BackgroundChanged);
Thomas Zander's avatar
Thomas Zander committed
933 934
}

935 936
KoShapeBackground * KoShape::background() const
{
937
    Q_D(const KoShape);
938
    return d->fill;
Thomas Zander's avatar
Thomas Zander committed
939 940
}

941 942
void KoShape::setZIndex(int zIndex)
{
943
    Q_D(KoShape);