KoShape.cpp 71.5 KB
Newer Older
Thomas Zander's avatar
Thomas Zander committed
1
/* This file is part of the KDE project
C. Boemann's avatar
C. Boemann committed
2
   Copyright (C) 2006 C. Boemann Rasmussen <cbo@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
#include "KoShapeBackground.h"
#include "KoColorBackground.h"
35
#include "KoHatchBackground.h"
36 37
#include "KoGradientBackground.h"
#include "KoPatternBackground.h"
38
#include "KoShapeManager.h"
39
#include "KoShapeUserData.h"
40
#include "KoShapeApplicationData.h"
41
#include "KoShapeSavingContext.h"
42
#include "KoShapeLoadingContext.h"
43
#include "KoViewConverter.h"
44
#include "KoShapeStroke.h"
45
#include "KoShapeShadow.h"
46
#include "KoClipPath.h"
47
#include "KoPathShape.h"
Thorsten Zachmann's avatar
Thorsten Zachmann committed
48 49
#include "KoEventAction.h"
#include "KoEventActionRegistry.h"
50
#include "KoOdfWorkaround.h"
51
#include "KoFilterEffectStack.h"
52
#include <KoSnapData.h>
53
#include <KoElementReference.h>
Thomas Zander's avatar
Thomas Zander committed
54

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

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

75
#include <limits>
76
#include "KoOdfGradientBackground.h"
77

78 79
// KoShapePrivate

80
KoShapePrivate::KoShapePrivate(KoShape *shape)
81 82
    : q_ptr(shape),
      size(50, 50),
83 84 85
      parent(0),
      userData(0),
      appData(0),
86
      stroke(0),
87
      shadow(0),
88
      border(0),
89
      clipPath(0),
90 91 92
      filterEffectStack(0),
      transparency(0.0),
      zIndex(0),
93
      runThrough(0),
94 95 96 97 98 99 100
      visible(true),
      printable(true),
      geometryProtected(false),
      keepAspect(false),
      selectable(true),
      detectCollision(false),
      protectContent(false),
101
      textRunAroundSide(KoShape::BiggestRunAroundSide),
102
      textRunAroundDistanceLeft(0.0),
103
      textRunAroundDistanceTop(0.0),
104 105
      textRunAroundDistanceRight(0.0),
      textRunAroundDistanceBottom(0.0),
106
      textRunAroundThreshold(0.0),
107
      textRunAroundContour(KoShape::ContourFull),
108 109
      anchor(0),
      minimumHeight(0.0)
110
{
111 112 113 114
    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);
115
    connectors[KoConnectionPoint::FirstCustomConnectionPoint] = KoConnectionPoint(QPointF(0.5, 0.5), KoConnectionPoint::AllDirections, KoConnectionPoint::AlignCenter);
116
}
Thomas Zander's avatar
Thomas Zander committed
117

118 119
KoShapePrivate::~KoShapePrivate()
{
120
    Q_Q(KoShape);
121
    if (parent)
122
        parent->removeShape(q);
123
    foreach(KoShapeManager *manager, shapeManagers) {
124 125
        manager->remove(q);
        manager->removeAdditional(q);
Thomas Zander's avatar
Thomas Zander committed
126
    }
127 128
    delete userData;
    delete appData;
129 130
    if (stroke && !stroke->deref())
        delete stroke;
131
    if (shadow && !shadow->deref())
132
        delete shadow;
133
    if (filterEffectStack && !filterEffectStack->deref())
134
        delete filterEffectStack;
Jan Hambrecht's avatar
Jan Hambrecht committed
135
    delete clipPath;
136 137
    qDeleteAll(eventActions);
}
Thomas Zander's avatar
Thomas Zander committed
138

139 140
void KoShapePrivate::shapeChanged(KoShape::ChangeType type)
{
141
    Q_Q(KoShape);
142
    if (parent)
143 144
        parent->model()->childChanged(q, type);
    q->shapeChanged(type);
145
    foreach(KoShape * shape, dependees)
146
        shape->shapeChanged(type, q);
147
}
148

149
void KoShapePrivate::updateStroke()
150
{
151
    Q_Q(KoShape);
152
    if (stroke == 0)
153 154
        return;
    KoInsets insets;
155
    stroke->strokeInsets(q, insets);
156 157 158
    QSizeF inner = q->size();
    // update left
    q->update(QRectF(-insets.left, -insets.top, insets.left,
159
                     inner.height() + insets.top + insets.bottom));
160 161
    // update top
    q->update(QRectF(-insets.left, -insets.top,
162
                     inner.width() + insets.left + insets.right, insets.top));
163 164
    // update right
    q->update(QRectF(inner.width(), -insets.top, insets.right,
165
                     inner.height() + insets.top + insets.bottom));
166 167
    // update bottom
    q->update(QRectF(-insets.left, inner.height(),
168
                     inner.width() + insets.left + insets.right, insets.bottom));
169
}
Thomas Zander's avatar
Thomas Zander committed
170

171 172 173 174 175 176 177 178
void KoShapePrivate::addShapeManager(KoShapeManager *manager)
{
    shapeManagers.insert(manager);
}

void KoShapePrivate::removeShapeManager(KoShapeManager *manager)
{
    shapeManagers.remove(manager);
179 180
}

181
void KoShapePrivate::convertFromShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
182
{
Jan Hambrecht's avatar
Jan Hambrecht committed
183
    switch(point.alignment) {
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 213 214 215 216 217 218 219 220
        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
221
    switch(point.alignment) {
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
        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;
    }
253 254
}

255
// static
256
QString KoShapePrivate::getStyleProperty(const char *property, KoShapeLoadingContext &context)
257 258 259 260 261 262 263 264 265 266 267
{
    KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
    QString value;

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

    return value;
}

268 269


270
// ======== KoShape
Thomas Zander's avatar
Thomas Zander committed
271
KoShape::KoShape()
272
    : d_ptr(new KoShapePrivate(this))
Thomas Zander's avatar
Thomas Zander committed
273
{
274
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
275 276
}

277 278 279 280 281
KoShape::KoShape(KoShapePrivate &dd)
    : d_ptr(&dd)
{
}

Thomas Zander's avatar
Thomas Zander committed
282 283
KoShape::~KoShape()
{
284
    Q_D(KoShape);
285
    d->shapeChanged(Deleted);
286
    delete d_ptr;
Thomas Zander's avatar
Thomas Zander committed
287 288
}

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

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

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

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

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

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

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

336
    // always set size, as d->size and size() may vary
Thomas Zander's avatar
Thomas Zander committed
337
    d->size = newSize;
338

339 340 341
    if (oldSize == newSize)
        return;

342
    notifyChanged();
343
    d->shapeChanged(SizeChanged);
Thomas Zander's avatar
Thomas Zander committed
344 345
}

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

    notifyChanged();
357
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
358 359
}

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

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

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

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

384
    return bb.contains(point);
Thomas Zander's avatar
Thomas Zander committed
385 386
}

387
QRectF KoShape::boundingRect() const
Thomas Zander's avatar
Thomas Zander committed
388
{
389
    Q_D(const KoShape);
Jan Hambrecht's avatar
Jan Hambrecht committed
390

391
    QTransform transform = absoluteTransformation(0);
392
    QRectF bb = outlineRect();
393
    if (d->stroke) {
394
        KoInsets insets;
395
        d->stroke->strokeInsets(this, insets);
396 397
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
398
    bb = transform.mapRect(bb);
399
    if (d->shadow) {
400
        KoInsets insets;
Thomas Zander's avatar
Thomas Zander committed
401
        d->shadow->insets(insets);
402 403
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
404
    if (d->filterEffectStack) {
405
        QRectF clipRect = d->filterEffectStack->clipRectForBoundingRect(outlineRect());
406 407
        bb |= transform.mapRect(clipRect);
    }
Thomas Zander's avatar
Thomas Zander committed
408

409
    return bb;
Thomas Zander's avatar
Thomas Zander committed
410 411
}

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

432
    if (converter) {
433 434 435
        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
436
    }
437 438

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
439 440
}

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

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

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

467
QTransform KoShape::transformation() const
468
{
469
    Q_D(const KoShape);
470 471 472
    return d->localMatrix;
}

473
KoShape::ChildZOrderPolicy KoShape::childZOrderPolicy()
474
{
475
    return ChildZDefault;
476 477
}

478 479
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
{
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 511 512 513 514 515 516 517 518
    // 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
519
    bool foundCommonParent = false;
520 521 522 523
    int index1 = s1->zIndex();
    int index2 = s2->zIndex();
    parentShapeS1 = s1;
    parentShapeS2 = s2;
524 525 526 527 528 529 530 531
    while (parentShapeS1 && !foundCommonParent) {
        parentShapeS2 = s2;
        index2 = parentShapeS2->zIndex();
        while (parentShapeS2) {
            if (parentShapeS2 == parentShapeS1) {
                foundCommonParent = true;
                break;
            }
532
            if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) {
533 534
                index2 = parentShapeS2->zIndex();
            }
535
            parentShapeS2 = parentShapeS2->parent();
536
        }
537 538

        if (!foundCommonParent) {
539
            if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) {
540 541
                index1 = parentShapeS1->zIndex();
            }
542
            parentShapeS1 = parentShapeS1->parent();
543 544
        }
    }
545

546 547 548 549 550 551 552 553
    // If the one shape is a parent/child of the other then sort so.
    if (s1 == parentShapeS2) {
        return true;
    }
    if (s2 == parentShapeS1) {
        return false;
    }

554
    // If we went that far then the z-Index is used for sorting.
555
    return index1 < index2;
Thomas Zander's avatar
Thomas Zander committed
556 557
}

558 559
void KoShape::setParent(KoShapeContainer *parent)
{
560
    Q_D(KoShape);
561
    if (d->parent == parent)
562
        return;
563 564 565
    KoShapeContainer *oldParent = d->parent;
    d->parent = 0; // avoids recursive removing
    if (oldParent)
566
        oldParent->removeShape(this);
Thomas Zander's avatar
Thomas Zander committed
567
    if (parent && parent != this) {
Thomas Zander's avatar
Thomas Zander committed
568
        d->parent = parent;
569
        parent->addShape(this);
570
    }
571
    notifyChanged();
572
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
573 574
}

575 576
int KoShape::zIndex() const
{
577
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
578
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
579 580
}

581 582
void KoShape::update() const
{
583
    Q_D(const KoShape);
584

585 586
    if (!d->shapeManagers.empty()) {
        QRectF rect(boundingRect());
587
        foreach(KoShapeManager * manager, d->shapeManagers) {
Jan Hambrecht's avatar
Jan Hambrecht committed
588
            manager->update(rect, this, true);
589
        }
Thomas Zander's avatar
Thomas Zander committed
590 591 592
    }
}

593
void KoShape::update(const QRectF &rect) const
594
{
595 596 597 598 599

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

600
    Q_D(const KoShape);
601

602
    if (!d->shapeManagers.empty() && isVisible()) {
603
        QRectF rc(absoluteTransformation(0).mapRect(rect));
604
        foreach(KoShapeManager * manager, d->shapeManagers) {
605
            manager->update(rc);
606 607
        }
    }
Thomas Zander's avatar
Thomas Zander committed
608 609
}

610
QPainterPath KoShape::outline() const
611
{
Thomas Zander's avatar
Thomas Zander committed
612
    QPainterPath path;
613
    path.addRect(outlineRect());
Thomas Zander's avatar
Thomas Zander committed
614 615 616
    return path;
}

617 618
QRectF KoShape::outlineRect() const
{
619
    const QSizeF s = size();
620 621 622 623 624 625 626
    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);
627
    if (d->fill) {
628
        return outline();
629
    }
630 631

    return QPainterPath();
632 633
}

634 635
QPointF KoShape::absolutePosition(KoFlake::Position anchor) const
{
636
    QPointF point;
637 638 639 640 641 642
    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;
643
    }
644
    return absoluteTransformation(0).map(point);
Thomas Zander's avatar
Thomas Zander committed
645 646
}

Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
647
void KoShape::setAbsolutePosition(const QPointF &newPosition, KoFlake::Position anchor)
648
{
649
    Q_D(KoShape);
650
    QPointF currentAbsPosition = absolutePosition(anchor);
651
    QPointF translate = newPosition - currentAbsPosition;
652
    QTransform translateMatrix;
653 654
    translateMatrix.translate(translate.x(), translate.y());
    applyAbsoluteTransformation(translateMatrix);
655
    notifyChanged();
656
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
657 658
}

659 660
void KoShape::copySettings(const KoShape *shape)
{
661
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
662 663
    d->size = shape->size();
    d->connectors.clear();
664
    foreach(const KoConnectionPoint &point, shape->connectionPoints())
Jan Hambrecht's avatar
Jan Hambrecht committed
665
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
666 667
    d->zIndex = shape->zIndex();
    d->visible = shape->isVisible();
668

669 670 671 672 673
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
674

675
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
676 677
    d->protectContent = shape->isContentProtected();
    d->selectable = shape->isSelectable();
Thomas Zander's avatar
Thomas Zander committed
678
    d->keepAspect = shape->keepAspectRatio();
679
    d->localMatrix = shape->d_ptr->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
680 681
}

Thomas Zander's avatar
Thomas Zander committed
682
void KoShape::notifyChanged()
683
{
684
    Q_D(KoShape);
685 686
    foreach(KoShapeManager * manager, d->shapeManagers) {
        manager->notifyShapeChanged(this);
687 688 689
    }
}

690 691
void KoShape::setUserData(KoShapeUserData *userData)
{
692
    Q_D(KoShape);
693
    delete d->userData;
Thomas Zander's avatar
Thomas Zander committed
694
    d->userData = userData;
695 696
}

697 698
KoShapeUserData *KoShape::userData() const
{
699
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
700
    return d->userData;
701 702
}

703 704
void KoShape::setApplicationData(KoShapeApplicationData *appData)
{
705
    Q_D(KoShape);
706
    // appdata is deleted by the application.
Thomas Zander's avatar
Thomas Zander committed
707
    d->appData = appData;
708 709
}

710 711
KoShapeApplicationData *KoShape::applicationData() const
{
712
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
713
    return d->appData;
714 715
}

Thomas Zander's avatar
Thomas Zander committed
716
bool KoShape::hasTransparency() const
717
{
Thomas Zander's avatar
Thomas Zander committed
718
    Q_D(const KoShape);
719
    if (! d->fill)
Thomas Zander's avatar
Thomas Zander committed
720
        return true;
721
    else
722 723 724 725 726 727
        return d->fill->hasTransparency() || d->transparency > 0.0;
}

void KoShape::setTransparency(qreal transparency)
{
    Q_D(KoShape);
728
    d->transparency = qBound<qreal>(0.0, transparency, 1.0);
729 730 731 732 733 734 735 736 737 738 739 740
}

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
741 742
}

743
KoInsets KoShape::strokeInsets() const
744
{
745
    Q_D(const KoShape);
746
    KoInsets answer;
747 748
    if (d->stroke)
        d->stroke->strokeInsets(this, answer);
749 750 751
    return answer;
}

752 753
qreal KoShape::rotation() const
{
754
    Q_D(const KoShape);
755
    // try to extract the rotation angle out of the local matrix
756 757 758
    // if it is a pure rotation matrix

    // check if the matrix has shearing mixed in
759
    if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
760
        return std::numeric_limits<qreal>::quiet_NaN();
761
    // check if the matrix has scaling mixed in
762
    if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
763
        return std::numeric_limits<qreal>::quiet_NaN();
764 765

    // calculate the angle from the matrix elements
766 767
    qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
    if (angle < 0.0)
768 769 770
        angle += 360.0;

    return angle;
Thomas Zander's avatar
Thomas Zander committed
771 772
}

773
QSizeF KoShape::size() const
774
{
775
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
776 777 778
    return d->size;
}

779 780
QPointF KoShape::position() const
{
781
    Q_D(const KoShape);
782 783
    QPointF center(0.5*size().width(), 0.5*size().height());
    return d->localMatrix.map(center) - center;
Thomas Zander's avatar
Thomas Zander committed
784 785
}

786
int KoShape::addConnectionPoint(const KoConnectionPoint &point)
787
{
788
    Q_D(KoShape);
789

790
    // get next glue point id
791
    int nextConnectionPointId = KoConnectionPoint::FirstCustomConnectionPoint;
Jan Hambrecht's avatar
Jan Hambrecht committed
792
    if (d->connectors.size())
793 794
        nextConnectionPointId = qMax(nextConnectionPointId, (--d->connectors.end()).key()+1);

795
    KoConnectionPoint p = point;
796 797
    d->convertFromShapeCoordinates(p, size());
    d->connectors[nextConnectionPointId] = p;
798 799 800 801

    return nextConnectionPointId;
}

802
bool KoShape::setConnectionPoint(int connectionPointId, const KoConnectionPoint &point)
803 804
{
    Q_D(KoShape);
805
    if (connectionPointId < 0)
806 807
        return false;

808 809
    const bool insertPoint = !hasConnectionPoint(connectionPointId);

810
    switch(connectionPointId) {
811 812 813 814 815 816
        case KoConnectionPoint::TopConnectionPoint:
        case KoConnectionPoint::RightConnectionPoint:
        case KoConnectionPoint::BottomConnectionPoint:
        case KoConnectionPoint::LeftConnectionPoint:
        {
            KoConnectionPoint::PointId id = static_cast<KoConnectionPoint::PointId>(connectionPointId);
817
            d->connectors[id] = KoConnectionPoint::defaultConnectionPoint(id);
818
            break;
819
        }
820
        default:
821
        {
822
            KoConnectionPoint p = point;
823 824
            d->convertFromShapeCoordinates(p, size());
            d->connectors[connectionPointId] = p;
825
            break;
826
        }
827 828
    }

829 830
    if(!insertPoint)
        d->shapeChanged(ConnectionPointChanged);
831 832 833 834

    return true;
}

835 836 837 838 839 840
bool KoShape::hasConnectionPoint(int connectionPointId) const
{
    Q_D(const KoShape);
    return d->connectors.contains(connectionPointId);
}

841
KoConnectionPoint KoShape::connectionPoint(int connectionPointId) const
842 843
{
    Q_D(const KoShape);
844
    KoConnectionPoint p = d->connectors.value(connectionPointId, KoConnectionPoint());
845
    // convert glue point to shape coordinates
846
    d->convertToShapeCoordinates(p, size());
847
    return p;
Thomas Zander's avatar
Thomas Zander committed
848 849
}

850
KoConnectionPoints KoShape::connectionPoints() const
851
{
852
    Q_D(const KoShape);
853
    QSizeF s = size();
854 855 856
    KoConnectionPoints points = d->connectors;
    KoConnectionPoints::iterator point = points.begin();
    KoConnectionPoints::iterator lastPoint = points.end();
857
    // convert glue points to shape coordinates
858
    for(; point != lastPoint; ++point) {
859
        d->convertToShapeCoordinates(point.value(), s);
860
    }
861 862

    return points;
863 864
}

865 866