KoShape.cpp 71.2 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 <KoBorder.h>
65

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

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

77 78
// KoShapePrivate

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

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

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

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

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

void KoShapePrivate::removeShapeManager(KoShapeManager *manager)
{
    shapeManagers.remove(manager);
177 178
}

179
void KoShapePrivate::convertFromShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
180
{
Jan Hambrecht's avatar
Jan Hambrecht committed
181
    switch(point.alignment) {
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 213 214 215 216 217 218
        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
219
    switch(point.alignment) {
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 245 246 247 248 249 250
        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;
    }
251 252
}

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

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

    return value;
}

266 267


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

275 276 277 278 279
KoShape::KoShape(KoShapePrivate &dd)
    : d_ptr(&dd)
{
}

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

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

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

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

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

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

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

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

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

337 338 339
    if (oldSize == newSize)
        return;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

471
KoShape::ChildZOrderPolicy KoShape::childZOrderPolicy()
472
{
473
    return ChildZDefault;
474 475
}

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

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

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

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

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

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

579 580
void KoShape::update() const
{
581
    Q_D(const KoShape);
582

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

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

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

598
    Q_D(const KoShape);
599

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

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

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

    return QPainterPath();
630 631
}

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

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

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

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

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

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

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

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

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

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

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

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

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
739 740
}

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

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

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

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

    return angle;
Thomas Zander's avatar
Thomas Zander committed
769 770
}

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

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

784
int KoShape::addConnectionPoint(const KoConnectionPoint &point)
785
{
786
    Q_D(KoShape);
787

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

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

    return nextConnectionPointId;
}

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

806 807
    const bool insertPoint = !hasConnectionPoint(connectionPointId);

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

827 828
    if(!insertPoint)
        d->shapeChanged(ConnectionPointChanged);
829 830 831 832

    return true;
}

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

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

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

    return points;
861 862
}

863 864 865 866
void KoShape::removeConnectionPoint(int connectionPointId)
{
    Q_D(KoShape);
    d->connectors.remove(connectionPointId);
867
    d->shapeChanged(ConnectionPointChanged);
868 869 870