KoShape.cpp 64.4 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
    const QSizeF s = size();
612 613 614 615 616 617 618
    return QRectF(QPointF(0, 0), QSizeF(qMax(s.width(),  qreal(0.0001)),
                                        qMax(s.height(), qreal(0.0001))));
}

QPainterPath KoShape::shadowOutline() const
{
    Q_D(const KoShape);
619
    if (d->fill) {
620
        return outline();
621
    }
622 623

    return QPainterPath();
624 625
}

626 627
QPointF KoShape::absolutePosition(KoFlake::Position anchor) const
{
628
    QPointF point;
629 630 631 632 633 634
    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;
635
    }
636
    return absoluteTransformation(0).map(point);
Thomas Zander's avatar
Thomas Zander committed
637 638
}

639 640
void KoShape::setAbsolutePosition(QPointF newPosition, KoFlake::Position anchor)
{
641
    Q_D(KoShape);
642
    QPointF currentAbsPosition = absolutePosition(anchor);
643
    QPointF translate = newPosition - currentAbsPosition;
644
    QTransform translateMatrix;
645 646
    translateMatrix.translate(translate.x(), translate.y());
    applyAbsoluteTransformation(translateMatrix);
647
    notifyChanged();
648
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
649 650
}

651 652
void KoShape::copySettings(const KoShape *shape)
{
653
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
654 655
    d->size = shape->size();
    d->connectors.clear();
656
    foreach(const KoConnectionPoint &point, shape->connectionPoints())
Jan Hambrecht's avatar
Jan Hambrecht committed
657
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
658 659
    d->zIndex = shape->zIndex();
    d->visible = shape->isVisible();
660

661 662 663 664 665
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
666

667
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
668 669
    d->protectContent = shape->isContentProtected();
    d->selectable = shape->isSelectable();
Thomas Zander's avatar
Thomas Zander committed
670
    d->keepAspect = shape->keepAspectRatio();
671
    d->localMatrix = shape->d_ptr->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
672 673
}

Thomas Zander's avatar
Thomas Zander committed
674
void KoShape::notifyChanged()
675
{
676
    Q_D(KoShape);
677 678
    foreach(KoShapeManager * manager, d->shapeManagers) {
        manager->notifyShapeChanged(this);
679 680 681
    }
}

682 683
void KoShape::setUserData(KoShapeUserData *userData)
{
684
    Q_D(KoShape);
685
    delete d->userData;
Thomas Zander's avatar
Thomas Zander committed
686
    d->userData = userData;
687 688
}

689 690
KoShapeUserData *KoShape::userData() const
{
691
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
692
    return d->userData;
693 694
}

695 696
void KoShape::setApplicationData(KoShapeApplicationData *appData)
{
697
    Q_D(KoShape);
698
    // appdata is deleted by the application.
Thomas Zander's avatar
Thomas Zander committed
699
    d->appData = appData;
700 701
}

702 703
KoShapeApplicationData *KoShape::applicationData() const
{
704
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
705
    return d->appData;
706 707
}

Thomas Zander's avatar
Thomas Zander committed
708
bool KoShape::hasTransparency() const
709
{
Thomas Zander's avatar
Thomas Zander committed
710
    Q_D(const KoShape);
711
    if (! d->fill)
Thomas Zander's avatar
Thomas Zander committed
712
        return true;
713
    else
714 715 716 717 718 719
        return d->fill->hasTransparency() || d->transparency > 0.0;
}

void KoShape::setTransparency(qreal transparency)
{
    Q_D(KoShape);
720
    d->transparency = qBound<qreal>(0.0, transparency, 1.0);
721 722 723 724 725 726 727 728 729 730 731 732
}

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
733 734
}

735
KoInsets KoShape::strokeInsets() const
736
{
737
    Q_D(const KoShape);
738
    KoInsets answer;
739 740
    if (d->stroke)
        d->stroke->strokeInsets(this, answer);
741 742 743
    return answer;
}

744 745
qreal KoShape::rotation() const
{
746
    Q_D(const KoShape);
747
    // try to extract the rotation angle out of the local matrix
748 749 750
    // if it is a pure rotation matrix

    // check if the matrix has shearing mixed in
751
    if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
752
        return std::numeric_limits<qreal>::quiet_NaN();
753
    // check if the matrix has scaling mixed in
754
    if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
755
        return std::numeric_limits<qreal>::quiet_NaN();
756 757

    // calculate the angle from the matrix elements
758 759
    qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
    if (angle < 0.0)
760 761 762
        angle += 360.0;

    return angle;
Thomas Zander's avatar
Thomas Zander committed
763 764
}

765
QSizeF KoShape::size() const
766
{
767
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
768 769 770
    return d->size;
}

771 772
QPointF KoShape::position() const
{
773
    Q_D(const KoShape);
774 775
    QPointF center(0.5*size().width(), 0.5*size().height());
    return d->localMatrix.map(center) - center;
Thomas Zander's avatar
Thomas Zander committed
776 777
}

778
int KoShape::addConnectionPoint(const KoConnectionPoint &point)
779
{
780
    Q_D(KoShape);
781

782
    // get next glue point id
783
    int nextConnectionPointId = KoConnectionPoint::FirstCustomConnectionPoint;
Jan Hambrecht's avatar
Jan Hambrecht committed
784
    if (d->connectors.size())
785 786
        nextConnectionPointId = qMax(nextConnectionPointId, (--d->connectors.end()).key()+1);

787
    KoConnectionPoint p = point;
788 789
    d->convertFromShapeCoordinates(p, size());
    d->connectors[nextConnectionPointId] = p;
790 791 792 793

    return nextConnectionPointId;
}

794
bool KoShape::setConnectionPoint(int connectionPointId, const KoConnectionPoint &point)
795 796
{
    Q_D(KoShape);
797
    if (connectionPointId < 0)
798 799
        return false;

800 801
    const bool insertPoint = !hasConnectionPoint(connectionPointId);

802
    switch(connectionPointId) {
803 804 805 806 807 808
        case KoConnectionPoint::TopConnectionPoint:
        case KoConnectionPoint::RightConnectionPoint:
        case KoConnectionPoint::BottomConnectionPoint:
        case KoConnectionPoint::LeftConnectionPoint:
        {
            KoConnectionPoint::PointId id = static_cast<KoConnectionPoint::PointId>(connectionPointId);
809
            d->connectors[id] = KoConnectionPoint::defaultConnectionPoint(id);
810
            break;
811
        }
812
        default:
813
        {
814
            KoConnectionPoint p = point;
815 816
            d->convertFromShapeCoordinates(p, size());
            d->connectors[connectionPointId] = p;
817
            break;
818
        }
819 820
    }

821 822
    if(!insertPoint)
        d->shapeChanged(ConnectionPointChanged);
823 824 825 826

    return true;
}

827 828 829 830 831 832
bool KoShape::hasConnectionPoint(int connectionPointId) const
{
    Q_D(const KoShape);
    return d->connectors.contains(connectionPointId);
}

833
KoConnectionPoint KoShape::connectionPoint(int connectionPointId) const
834 835
{
    Q_D(const KoShape);
836
    KoConnectionPoint p = d->connectors.value(connectionPointId, KoConnectionPoint());
837
    // convert glue point to shape coordinates
838
    d->convertToShapeCoordinates(p, size());
839
    return p;
Thomas Zander's avatar
Thomas Zander committed
840 841
}

842
KoConnectionPoints KoShape::connectionPoints() const
843
{
844
    Q_D(const KoShape);
845
    QSizeF s = size();
846 847 848
    KoConnectionPoints points = d->connectors;
    KoConnectionPoints::iterator point = points.begin();
    KoConnectionPoints::iterator lastPoint = points.end();
849
    // convert glue points to shape coordinates
850
    for(; point != lastPoint; ++point) {
851
        d->convertToShapeCoordinates(point.value(), s);
852
    }
853 854

    return points;
855 856
}

857 858 859 860
void KoShape::removeConnectionPoint(int connectionPointId)
{
    Q_D(KoShape);
    d->connectors.remove(connectionPointId);
861
    d->shapeChanged(ConnectionPointChanged);
862 863 864 865 866 867 868 869
}

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

870
void KoShape::addEventAction(KoEventAction *action)
871
{
872
    Q_D(KoShape);
873
    d->eventActions.insert(action);
874 875
}