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 619 620 621 622
    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);
    if (d->fill)
        return outline();

    return QPainterPath();
623 624
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return nextConnectionPointId;
}

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

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

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

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

    return true;
}

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

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

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

    return points;
854 855
}

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

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

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

875
void KoShape::removeEventAction(KoEventAction *action)
876
{
877
    Q_D(KoShape);