KoShape.cpp 64.2 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
#include "KoInsets.h"
32
#include "KoShapeStrokeModel.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 "KoShapeStroke.h"
44
#include "ShapeDeleter_p.h"
45
#include "KoShapeShadow.h"
46
#include "KoClipPath.h"
Thorsten Zachmann's avatar
Thorsten Zachmann committed
47 48
#include "KoEventAction.h"
#include "KoEventActionRegistry.h"
49
#include "KoOdfWorkaround.h"
50
#include "KoFilterEffectStack.h"
51
#include <KoSnapData.h>
52
#include <KoElementReference.h>
Thomas Zander's avatar
Thomas Zander committed
53

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

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

72
#include <limits>
73
#include "KoOdfGradientBackground.h"
74

75 76
// KoShapePrivate

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

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

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

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

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

void KoShapePrivate::removeShapeManager(KoShapeManager *manager)
{
    shapeManagers.remove(manager);
171 172
}

173
void KoShapePrivate::convertFromShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
174
{
Jan Hambrecht's avatar
Jan Hambrecht committed
175
    switch(point.alignment) {
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
        case KoConnectionPoint::AlignNone:
            point.position = KoFlake::toRelative(point.position, shapeSize);
            point.position.rx() = qBound<qreal>(0.0, point.position.x(), 1.0);
            point.position.ry() = qBound<qreal>(0.0, point.position.y(), 1.0);
            break;
        case KoConnectionPoint::AlignRight:
            point.position.rx() -= shapeSize.width();
        case KoConnectionPoint::AlignLeft:
            point.position.ry() = 0.5*shapeSize.height();
            break;
        case KoConnectionPoint::AlignBottom:
            point.position.ry() -= shapeSize.height();
        case KoConnectionPoint::AlignTop:
            point.position.rx() = 0.5*shapeSize.width();
            break;
        case KoConnectionPoint::AlignTopLeft:
            // nothing to do here
            break;
        case KoConnectionPoint::AlignTopRight:
            point.position.rx() -= shapeSize.width();
            break;
        case KoConnectionPoint::AlignBottomLeft:
            point.position.ry() -= shapeSize.height();
            break;
        case KoConnectionPoint::AlignBottomRight:
            point.position.rx() -= shapeSize.width();
            point.position.ry() -= shapeSize.height();
            break;
        case KoConnectionPoint::AlignCenter:
            point.position.rx() -= 0.5 * shapeSize.width();
            point.position.ry() -= 0.5 * shapeSize.height();
            break;
    }
}

void KoShapePrivate::convertToShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
{
Jan Hambrecht's avatar
Jan Hambrecht committed
213
    switch(point.alignment) {
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
        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;
    }
245 246
}

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

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

    return value;
}

260 261


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

269 270 271 272 273
KoShape::KoShape(KoShapePrivate &dd)
    : d_ptr(&dd)
{
}

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

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

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

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

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

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

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

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

328
    // always set size, as d->size and size() may vary
Thomas Zander's avatar
Thomas Zander committed
329
    d->size = newSize;
330

331 332 333
    if (oldSize == newSize)
        return;

334
    notifyChanged();
335
    d->shapeChanged(SizeChanged);
Thomas Zander's avatar
Thomas Zander committed
336 337
}

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

    notifyChanged();
349
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
350 351
}

352
bool KoShape::hitTest(const QPointF &position) const
Thomas Zander's avatar
Thomas Zander committed
353
{
354
    Q_D(const KoShape);
355
    if (d->parent && d->parent->isClipped(this) && !d->parent->hitTest(position))
Thomas Zander's avatar
Thomas Zander committed
356 357
        return false;

358
    QPointF point = absoluteTransformation(0).inverted().map(position);
359
    QRectF bb(QPointF(), size());
360
    if (d->stroke) {
361
        KoInsets insets;
362
        d->stroke->strokeInsets(this, insets);
363
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
364
    }
365 366 367
    if (bb.contains(point))
        return true;

368
    // if there is no shadow we can as well just leave
369
    if (! d->shadow)
370
        return false;
Thomas Zander's avatar
Thomas Zander committed
371

372 373
    // the shadow has an offset to the shape, so we simply
    // check if the position minus the shadow offset hits the shape
374
    point = absoluteTransformation(0).inverted().map(position - d->shadow->offset());
375

376
    return bb.contains(point);
Thomas Zander's avatar
Thomas Zander committed
377 378
}

379
QRectF KoShape::boundingRect() const
Thomas Zander's avatar
Thomas Zander committed
380
{
381
    Q_D(const KoShape);
Jan Hambrecht's avatar
Jan Hambrecht committed
382

383
    QTransform transform = absoluteTransformation(0);
384
    QRectF bb = outlineRect();
385
    if (d->stroke) {
386
        KoInsets insets;
387
        d->stroke->strokeInsets(this, insets);
388 389
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
390
    bb = transform.mapRect(bb);
391
    if (d->shadow) {
392
        KoInsets insets;
Thomas Zander's avatar
Thomas Zander committed
393
        d->shadow->insets(insets);
394 395
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
396
    if (d->filterEffectStack) {
397
        QRectF clipRect = d->filterEffectStack->clipRectForBoundingRect(outlineRect());
398 399
        bb |= transform.mapRect(clipRect);
    }
Thomas Zander's avatar
Thomas Zander committed
400

401
    return bb;
Thomas Zander's avatar
Thomas Zander committed
402 403
}

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

424
    if (converter) {
425 426 427
        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
428
    }
429 430

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
431 432
}

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

443
void KoShape::applyTransformation(const QTransform &matrix)
444
{
445
    Q_D(KoShape);
446
    d->localMatrix = matrix * d->localMatrix;
447
    notifyChanged();
448
    d->shapeChanged(GenericMatrixChange);
449 450
}

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

459
QTransform KoShape::transformation() const
460
{
461
    Q_D(const KoShape);
462 463 464
    return d->localMatrix;
}

465
KoShape::ChildZOrderPolicy KoShape::childZOrderPolicy()
466
{
467
    return ChildZDefault;
468 469
}

470 471
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
{
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
    // First sort according to runThrough which is sort of a master level
    KoShape *parentShapeS1 = s1->parent();
    KoShape *parentShapeS2 = s2->parent();
    int runThrough1 = s1->runThrough();
    int runThrough2 = s2->runThrough();
    while (parentShapeS1) {
        if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) {
            runThrough1 = parentShapeS1->runThrough();
        } else {
            runThrough1 = runThrough1 + parentShapeS1->runThrough();
        }
        parentShapeS1 = parentShapeS1->parent();
    }

    while (parentShapeS2) {
        if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) {
            runThrough2 = parentShapeS2->runThrough();
        } else {
            runThrough2 = runThrough2 + parentShapeS2->runThrough();
        }
        parentShapeS2 = parentShapeS2->parent();
    }

    if (runThrough1 > runThrough2) {
        return false;
    }
    if (runThrough1 < runThrough2) {
        return true;
    }

    // If on the same runThrough level then the zIndex is all that matters.
    //
    // We basically walk up through the parents until we find a common base parent
    // To do that we need two loops where the inner loop walks up through the parents
    // of s2 every time we step up one parent level on s1
    //
    // We don't update the index value until after we have seen that it's not a common base
    // That way we ensure that two children of a common base are sorted according to their respective
    // z value
511
    bool foundCommonParent = false;
512 513 514 515
    int index1 = s1->zIndex();
    int index2 = s2->zIndex();
    parentShapeS1 = s1;
    parentShapeS2 = s2;
516 517 518 519 520 521 522 523
    while (parentShapeS1 && !foundCommonParent) {
        parentShapeS2 = s2;
        index2 = parentShapeS2->zIndex();
        while (parentShapeS2) {
            if (parentShapeS2 == parentShapeS1) {
                foundCommonParent = true;
                break;
            }
524
            if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) {
525 526
                index2 = parentShapeS2->zIndex();
            }
527
            parentShapeS2 = parentShapeS2->parent();
528
        }
529 530

        if (!foundCommonParent) {
531
            if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) {
532 533
                index1 = parentShapeS1->zIndex();
            }
534
            parentShapeS1 = parentShapeS1->parent();
535 536
        }
    }
537

538 539 540 541 542 543 544 545
    // If the one shape is a parent/child of the other then sort so.
    if (s1 == parentShapeS2) {
        return true;
    }
    if (s2 == parentShapeS1) {
        return false;
    }

546
    // If we went that far then the z-Index is used for sorting.
547
    return index1 < index2;
Thomas Zander's avatar
Thomas Zander committed
548 549
}

550 551
void KoShape::setParent(KoShapeContainer *parent)
{
552
    Q_D(KoShape);
553
    if (d->parent == parent)
554
        return;
555 556 557
    KoShapeContainer *oldParent = d->parent;
    d->parent = 0; // avoids recursive removing
    if (oldParent)
558
        oldParent->removeShape(this);
Thomas Zander's avatar
Thomas Zander committed
559
    if (parent && parent != this) {
Thomas Zander's avatar
Thomas Zander committed
560
        d->parent = parent;
561
        parent->addShape(this);
562
    }
563
    notifyChanged();
564
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
565 566
}

567 568
int KoShape::zIndex() const
{
569
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
570
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
571 572
}

573 574
void KoShape::update() const
{
575
    Q_D(const KoShape);
576

577 578
    if (!d->shapeManagers.empty()) {
        QRectF rect(boundingRect());
579
        foreach(KoShapeManager * manager, d->shapeManagers) {
Jan Hambrecht's avatar
Jan Hambrecht committed
580
            manager->update(rect, this, true);
581
        }
Thomas Zander's avatar
Thomas Zander committed
582 583 584
    }
}

585
void KoShape::update(const QRectF &rect) const
586
{
587 588 589 590 591

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

592
    Q_D(const KoShape);
593

594
    if (!d->shapeManagers.empty() && isVisible()) {
595
        QRectF rc(absoluteTransformation(0).mapRect(rect));
596
        foreach(KoShapeManager * manager, d->shapeManagers) {
597
            manager->update(rc);
598 599
        }
    }
Thomas Zander's avatar
Thomas Zander committed
600 601
}

602
QPainterPath KoShape::outline() const
603
{
Thomas Zander's avatar
Thomas Zander committed
604
    QPainterPath path;
605
    path.addRect(outlineRect());
Thomas Zander's avatar
Thomas Zander committed
606 607 608
    return path;
}

609 610
QRectF KoShape::outlineRect() const
{
611 612
    const QSizeF s = size();
    return QRectF(QPointF(0, 0), QSizeF(qMax(s.width(), qreal(0.0001)), qMax(s.height(), qreal(0.0001))));
613 614
}

615 616
QPointF KoShape::absolutePosition(KoFlake::Position anchor) const
{
617
    QPointF point;
618 619 620 621 622 623
    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;
624
    }
625
    return absoluteTransformation(0).map(point);
Thomas Zander's avatar
Thomas Zander committed
626 627
}

628 629
void KoShape::setAbsolutePosition(QPointF newPosition, KoFlake::Position anchor)
{
630
    Q_D(KoShape);
631
    QPointF currentAbsPosition = absolutePosition(anchor);
632
    QPointF translate = newPosition - currentAbsPosition;
633
    QTransform translateMatrix;
634 635
    translateMatrix.translate(translate.x(), translate.y());
    applyAbsoluteTransformation(translateMatrix);
636
    notifyChanged();
637
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
638 639
}

640 641
void KoShape::copySettings(const KoShape *shape)
{
642
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
643 644
    d->size = shape->size();
    d->connectors.clear();
645
    foreach(const KoConnectionPoint &point, shape->connectionPoints())
Jan Hambrecht's avatar
Jan Hambrecht committed
646
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
647 648
    d->zIndex = shape->zIndex();
    d->visible = shape->isVisible();
649

650 651 652 653 654
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
655

656
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
657 658
    d->protectContent = shape->isContentProtected();
    d->selectable = shape->isSelectable();
Thomas Zander's avatar
Thomas Zander committed
659
    d->keepAspect = shape->keepAspectRatio();
660
    d->localMatrix = shape->d_ptr->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
661 662
}

Thomas Zander's avatar
Thomas Zander committed
663
void KoShape::notifyChanged()
664
{
665
    Q_D(KoShape);
666 667
    foreach(KoShapeManager * manager, d->shapeManagers) {
        manager->notifyShapeChanged(this);
668 669 670
    }
}

671 672
void KoShape::setUserData(KoShapeUserData *userData)
{
673
    Q_D(KoShape);
674
    delete d->userData;
Thomas Zander's avatar
Thomas Zander committed
675
    d->userData = userData;
676 677
}

678 679
KoShapeUserData *KoShape::userData() const
{
680
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
681
    return d->userData;
682 683
}

684 685
void KoShape::setApplicationData(KoShapeApplicationData *appData)
{
686
    Q_D(KoShape);
687
    // appdata is deleted by the application.
Thomas Zander's avatar
Thomas Zander committed
688
    d->appData = appData;
689 690
}

691 692
KoShapeApplicationData *KoShape::applicationData() const
{
693
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
694
    return d->appData;
695 696
}

Thomas Zander's avatar
Thomas Zander committed
697
bool KoShape::hasTransparency() const
698
{
Thomas Zander's avatar
Thomas Zander committed
699
    Q_D(const KoShape);
700
    if (! d->fill)
Thomas Zander's avatar
Thomas Zander committed
701
        return true;
702
    else
703 704 705 706 707 708
        return d->fill->hasTransparency() || d->transparency > 0.0;
}

void KoShape::setTransparency(qreal transparency)
{
    Q_D(KoShape);
709
    d->transparency = qBound<qreal>(0.0, transparency, 1.0);
710 711 712 713 714 715 716 717 718 719 720 721
}

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
722 723
}

724
KoInsets KoShape::strokeInsets() const
725
{
726
    Q_D(const KoShape);
727
    KoInsets answer;
728 729
    if (d->stroke)
        d->stroke->strokeInsets(this, answer);
730 731 732
    return answer;
}

733 734
qreal KoShape::rotation() const
{
735
    Q_D(const KoShape);
736
    // try to extract the rotation angle out of the local matrix
737 738 739
    // if it is a pure rotation matrix

    // check if the matrix has shearing mixed in
740
    if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
741
        return std::numeric_limits<qreal>::quiet_NaN();
742
    // check if the matrix has scaling mixed in
743
    if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
744
        return std::numeric_limits<qreal>::quiet_NaN();
745 746

    // calculate the angle from the matrix elements
747 748
    qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
    if (angle < 0.0)
749 750 751
        angle += 360.0;

    return angle;
Thomas Zander's avatar
Thomas Zander committed
752 753
}

754
QSizeF KoShape::size() const
755
{
756
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
757 758 759
    return d->size;
}

760 761
QPointF KoShape::position() const
{
762
    Q_D(const KoShape);
763 764
    QPointF center(0.5*size().width(), 0.5*size().height());
    return d->localMatrix.map(center) - center;
Thomas Zander's avatar
Thomas Zander committed
765 766
}

767
int KoShape::addConnectionPoint(const KoConnectionPoint &point)
768
{
769
    Q_D(KoShape);
770

771
    // get next glue point id
772
    int nextConnectionPointId = KoConnectionPoint::FirstCustomConnectionPoint;
Jan Hambrecht's avatar
Jan Hambrecht committed
773
    if (d->connectors.size())
774 775
        nextConnectionPointId = qMax(nextConnectionPointId, (--d->connectors.end()).key()+1);

776
    KoConnectionPoint p = point;
777 778
    d->convertFromShapeCoordinates(p, size());
    d->connectors[nextConnectionPointId] = p;
779 780 781 782

    return nextConnectionPointId;
}

783
bool KoShape::setConnectionPoint(int connectionPointId, const KoConnectionPoint &point)
784 785
{
    Q_D(KoShape);
786
    if (connectionPointId < 0)
787 788
        return false;

789 790
    const bool insertPoint = !hasConnectionPoint(connectionPointId);

791
    switch(connectionPointId) {
792 793 794 795 796 797
        case KoConnectionPoint::TopConnectionPoint:
        case KoConnectionPoint::RightConnectionPoint:
        case KoConnectionPoint::BottomConnectionPoint:
        case KoConnectionPoint::LeftConnectionPoint:
        {
            KoConnectionPoint::PointId id = static_cast<KoConnectionPoint::PointId>(connectionPointId);
798
            d->connectors[id] = KoConnectionPoint::defaultConnectionPoint(id);
799
            break;
800
        }
801
        default:
802
        {
803
            KoConnectionPoint p = point;
804 805
            d->convertFromShapeCoordinates(p, size());
            d->connectors[connectionPointId] = p;
806
            break;
807
        }
808 809
    }

810 811
    if(!insertPoint)
        d->shapeChanged(ConnectionPointChanged);
812 813 814 815

    return true;
}

816 817 818 819 820 821
bool KoShape::hasConnectionPoint(int connectionPointId) const
{
    Q_D(const KoShape);
    return d->connectors.contains(connectionPointId);
}

822
KoConnectionPoint KoShape::connectionPoint(int connectionPointId) const
823 824
{
    Q_D(const KoShape);
825
    KoConnectionPoint p = d->connectors.value(connectionPointId, KoConnectionPoint());
826
    // convert glue point to shape coordinates
827
    d->convertToShapeCoordinates(p, size());
828
    return p;
Thomas Zander's avatar
Thomas Zander committed
829 830
}

831
KoConnectionPoints KoShape::connectionPoints() const
832
{
833
    Q_D(const KoShape);
834
    QSizeF s = size();
835 836 837
    KoConnectionPoints points = d->connectors;
    KoConnectionPoints::iterator point = points.begin();
    KoConnectionPoints::iterator lastPoint = points.end();
838
    // convert glue points to shape coordinates
839
    for(; point != lastPoint; ++point) {
840
        d->convertToShapeCoordinates(point.value(), s);
841
    }
842 843

    return points;
844 845
}

846 847 848 849
void KoShape::removeConnectionPoint(int connectionPointId)
{
    Q_D(KoShape);
    d->connectors.remove(connectionPointId);
850
    d->shapeChanged(ConnectionPointChanged);
851 852 853 854 855 856 857 858
}

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

859
void KoShape::addEventAction(KoEventAction *action)
860
{
861
    Q_D(KoShape);
862
    d->eventActions.insert(action);
863 864
}

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