KoShape.cpp 41.5 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-2009 Thomas Zander <zander@kde.org>
4
   Copyright (C) 2006-2008 Thorsten Zachmann <zachmann@kde.org>
5
   Copyright (C) 2007-2009 Jan Hambrecht <jaham@gmx.net>
Thomas Zander's avatar
Thomas Zander committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

   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,
 * Boston, MA 02110-1301, USA.
*/

#include "KoShape.h"
24
#include "KoShape_p.h"
Thomas Zander's avatar
Thomas Zander committed
25
#include "KoShapeContainer.h"
26
#include "KoShapeLayer.h"
27
#include "KoShapeContainerModel.h"
Thomas Zander's avatar
Thomas Zander committed
28
#include "KoSelection.h"
29
#include "KoPointerEvent.h"
Thomas Zander's avatar
Thomas Zander committed
30 31
#include "KoInsets.h"
#include "KoShapeBorderModel.h"
32 33 34 35
#include "KoShapeBackground.h"
#include "KoColorBackground.h"
#include "KoGradientBackground.h"
#include "KoPatternBackground.h"
36
#include "KoShapeManager.h"
37
#include "KoShapeUserData.h"
38
#include "KoShapeApplicationData.h"
39
#include "KoShapeSavingContext.h"
40
#include "KoShapeLoadingContext.h"
41
#include "KoViewConverter.h"
42
#include "KoLineBorder.h"
43
#include "ShapeDeleter_p.h"
44
#include "KoShapeShadow.h"
Thorsten Zachmann's avatar
Thorsten Zachmann committed
45 46
#include "KoEventAction.h"
#include "KoEventActionRegistry.h"
47
#include "KoOdfWorkaround.h"
48
#include "KoFilterEffectStack.h"
Thomas Zander's avatar
Thomas Zander committed
49

50
#include <KoXmlReader.h>
51
#include <KoXmlWriter.h>
52
#include <KoXmlNS.h>
53
#include <KoGenStyle.h>
54
#include <KoGenStyles.h>
55
#include <KoUnit.h>
56
#include <KoOdfStylesReader.h>
57
#include <KoOdfGraphicStyles.h>
58
#include <KoOdfLoadingContext.h>
59

Thomas Zander's avatar
Thomas Zander committed
60
#include <QPainter>
61
#include <QVariant>
Thomas Zander's avatar
Thomas Zander committed
62
#include <QPainterPath>
63
#include <QList>
64
#include <QMap>
65
#include <QByteArray>
Thomas Zander's avatar
Thomas Zander committed
66

67 68
#include <kdebug.h>

69 70
#include <limits>

71 72 73 74 75 76 77
KoShapePrivate::KoShapePrivate(KoShape *shape)
    : size(50, 50),
    parent(0),
    userData(0),
    appData(0),
    fill(0),
    border(0),
78
    q(shape),
79
    shadow(0),
80
    filterEffectStack(0),
81
    transparency(0.0),
82 83 84 85 86 87 88 89
    zIndex(0),
    visible(true),
    printable(true),
    geometryProtected(false),
    keepAspect(false),
    selectable(true),
    detectCollision(false),
    protectContent(false)
90
{
91 92 93 94
    connectors.append(QPointF(0.5, 0.0));
    connectors.append(QPointF(1.0, 0.5));
    connectors.append(QPointF(0.5, 1.0));
    connectors.append(QPointF(0.0, 0.5));
95
}
Thomas Zander's avatar
Thomas Zander committed
96

97 98 99
KoShapePrivate::~KoShapePrivate()
{
    if (parent)
100
        parent->removeChild(q);
101
    foreach(KoShapeManager *manager, shapeManagers) {
102 103
        manager->remove(q);
        manager->removeAdditional(q);
Thomas Zander's avatar
Thomas Zander committed
104
    }
105 106 107 108 109 110 111 112
    delete userData;
    delete appData;
    if (border && ! border->removeUser())
        delete border;
    if (shadow && ! shadow->removeUser())
        delete shadow;
    if (fill && ! fill->removeUser())
        delete fill;
113 114
    if (filterEffectStack && ! filterEffectStack->removeUser())
        delete filterEffectStack;
115 116
    qDeleteAll(eventActions);
}
Thomas Zander's avatar
Thomas Zander committed
117

118 119 120
void KoShapePrivate::shapeChanged(KoShape::ChangeType type)
{
    if (parent)
121 122
        parent->model()->childChanged(q, type);
    q->shapeChanged(type);
123
    foreach(KoShape * shape, dependees)
124
        shape->shapeChanged(type, q);
125
}
126

127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
void KoShapePrivate::updateBorder()
{
    if (border == 0)
        return;
    KoInsets insets;
    border->borderInsets(q, insets);
    QSizeF inner = q->size();
    // update left
    q->update(QRectF(-insets.left, -insets.top, insets.left,
                inner.height() + insets.top + insets.bottom));
    // update top
    q->update(QRectF(-insets.left, -insets.top,
                inner.width() + insets.left + insets.right, insets.top));
    // update right
    q->update(QRectF(inner.width(), -insets.top, insets.right,
                inner.height() + insets.top + insets.bottom));
    // update bottom
    q->update(QRectF(-insets.left, inner.height(),
                inner.width() + insets.left + insets.right, insets.bottom));
}
Thomas Zander's avatar
Thomas Zander committed
147

148
// ======== KoShape
Thomas Zander's avatar
Thomas Zander committed
149
KoShape::KoShape()
150
        : d_ptr(new KoShapePrivate(this))
Thomas Zander's avatar
Thomas Zander committed
151
{
152
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
153 154
}

155 156 157 158 159
KoShape::KoShape(KoShapePrivate &dd)
    : d_ptr(&dd)
{
}

Thomas Zander's avatar
Thomas Zander committed
160 161
KoShape::~KoShape()
{
162
    Q_D(KoShape);
163
    d->shapeChanged(Deleted);
164
    delete d_ptr;
Thomas Zander's avatar
Thomas Zander committed
165 166
}

167 168
void KoShape::paintDecorations(QPainter &painter, const KoViewConverter &converter, const KoCanvasBase *canvas)
{
Thomas Zander's avatar
Thomas Zander committed
169 170 171
    Q_UNUSED(painter);
    Q_UNUSED(converter);
    Q_UNUSED(canvas);
172 173
    /* Since this code is not actually used (kivio is going to be the main user) lets disable instead of fix.
        if ( selected )
Thomas Zander's avatar
Thomas Zander committed
174
        {
175 176 177 178 179 180 181 182 183 184 185 186
            // draw connectors
            QPen pen( Qt::blue );
            pen.setWidth( 0 );
            painter.setPen( pen );
            painter.setBrush( Qt::NoBrush );
            for ( int i = 0; i < d->connectors.size(); ++i )
            {
                QPointF p = converter.documentToView(d->connectors[ i ]);
                painter.drawLine( QPointF( p.x() - 2, p.y() + 2 ), QPointF( p.x() + 2, p.y() - 2 ) );
                painter.drawLine( QPointF( p.x() + 2, p.y() + 2 ), QPointF( p.x() - 2, p.y() - 2 ) );
            }
        }*/
Thomas Zander's avatar
Thomas Zander committed
187 188
}

189
void KoShape::setScale(qreal sx, qreal sy)
Thomas Zander's avatar
Thomas Zander committed
190
{
191
    Q_D(KoShape);
192 193
    QPointF pos = position();
    QMatrix scaleMatrix;
194 195 196
    scaleMatrix.translate(pos.x(), pos.y());
    scaleMatrix.scale(sx, sy);
    scaleMatrix.translate(-pos.x(), -pos.y());
197
    d->localMatrix = d->localMatrix * scaleMatrix;
198

199
    notifyChanged();
200
    d->shapeChanged(ScaleChanged);
Thomas Zander's avatar
Thomas Zander committed
201 202
}

203
void KoShape::rotate(qreal angle)
Thomas Zander's avatar
Thomas Zander committed
204
{
205
    Q_D(KoShape);
206
    QPointF center = d->localMatrix.map(QPointF(0.5 * size().width(), 0.5 * size().height()));
207
    QMatrix rotateMatrix;
208 209 210
    rotateMatrix.translate(center.x(), center.y());
    rotateMatrix.rotate(angle);
    rotateMatrix.translate(-center.x(), -center.y());
211
    d->localMatrix = d->localMatrix * rotateMatrix;
212

213
    notifyChanged();
214
    d->shapeChanged(RotationChanged);
Thomas Zander's avatar
Thomas Zander committed
215 216
}

217
void KoShape::setShear(qreal sx, qreal sy)
Thomas Zander's avatar
Thomas Zander committed
218
{
219
    Q_D(KoShape);
220 221
    QPointF pos = position();
    QMatrix shearMatrix;
222 223 224
    shearMatrix.translate(pos.x(), pos.y());
    shearMatrix.shear(sx, sy);
    shearMatrix.translate(-pos.x(), -pos.y());
225
    d->localMatrix = d->localMatrix * shearMatrix;
226

227
    notifyChanged();
228
    d->shapeChanged(ShearChanged);
Thomas Zander's avatar
Thomas Zander committed
229 230
}

231
void KoShape::setSize(const QSizeF &newSize)
Thomas Zander's avatar
Thomas Zander committed
232
{
233
    Q_D(KoShape);
234 235
    QSizeF oldSize(size());
    if (oldSize == newSize)
Thomas Zander's avatar
Thomas Zander committed
236
        return;
237

238
    QMatrix matrix;
239 240
    oldSize.setHeight(qMax((qreal) 1E-4, oldSize.height())); // avoids devision by zero below
    oldSize.setWidth(qMax((qreal) 1E-4, oldSize.width()));
241
    matrix.scale(newSize.width()/oldSize.width(), newSize.height()/oldSize.height());
242 243

    KoGradientBackground * g = dynamic_cast<KoGradientBackground*>(d->fill);
244
    if (g) {
245
        g->setMatrix(g->matrix() * matrix);
246
    }
247
    KoLineBorder *l = dynamic_cast<KoLineBorder*>(d->border);
248 249
    if (l && l->lineBrush().gradient()) {
        QBrush brush = l->lineBrush();
250 251
        brush.setMatrix(brush.matrix() * matrix);
        l->setLineBrush(brush);
252
    }
253

Thomas Zander's avatar
Thomas Zander committed
254
    d->size = newSize;
255

256
    notifyChanged();
257
    d->shapeChanged(SizeChanged);
Thomas Zander's avatar
Thomas Zander committed
258 259
}

260
void KoShape::setPosition(const QPointF &newPosition)
Thomas Zander's avatar
Thomas Zander committed
261
{
262
    Q_D(KoShape);
263
    QPointF currentPos = position();
264
    if (newPosition == currentPos)
Thomas Zander's avatar
Thomas Zander committed
265
        return;
266
    QMatrix translateMatrix;
267
    translateMatrix.translate(newPosition.x() - currentPos.x(), newPosition.y() - currentPos.y());
268 269 270
    d->localMatrix = d->localMatrix * translateMatrix;

    notifyChanged();
271
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
272 273
}

274
bool KoShape::hitTest(const QPointF &position) const
Thomas Zander's avatar
Thomas Zander committed
275
{
276
    Q_D(const KoShape);
277
    if (d->parent && d->parent->childClipped(this) && !d->parent->hitTest(position))
Thomas Zander's avatar
Thomas Zander committed
278 279
        return false;

280
    QPointF point = absoluteTransformation(0).inverted().map(position);
281
    QRectF bb(QPointF(), size());
282
    if (d->border) {
283
        KoInsets insets;
284
        d->border->borderInsets(this, insets);
285
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
286
    }
287 288 289
    if (bb.contains(point))
        return true;

290
    // if there is no shadow we can as well just leave
291
    if (! d->shadow)
292
        return false;
Thomas Zander's avatar
Thomas Zander committed
293

294 295
    // the shadow has an offset to the shape, so we simply
    // check if the position minus the shadow offset hits the shape
296
    point = absoluteTransformation(0).inverted().map(position - d->shadow->offset());
297

298
    return bb.contains(point);
Thomas Zander's avatar
Thomas Zander committed
299 300
}

301
QRectF KoShape::boundingRect() const
Thomas Zander's avatar
Thomas Zander committed
302
{
303
    Q_D(const KoShape);
304 305 306
    QSizeF mySize = size();
    QMatrix transform = absoluteTransformation(0);
    QRectF bb(QPointF(0, 0), mySize);
307
    if (d->border) {
308 309 310 311
        KoInsets insets;
        d->border->borderInsets(this, insets);
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
312
    bb = transform.mapRect(bb);
313
    if (d->shadow) {
314
        KoInsets insets;
315
        d->shadow->insets(this, insets);
316 317
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
318 319 320 321 322
    if (d->filterEffectStack) {
        QRectF clipRect = d->filterEffectStack->clipRectForBoundingRect(QRectF(QPointF(), mySize));
        bb |= transform.mapRect(clipRect);
    }
    
323
    return bb;
Thomas Zander's avatar
Thomas Zander committed
324 325
}

326 327
QMatrix KoShape::absoluteTransformation(const KoViewConverter *converter) const
{
328
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
329 330
    QMatrix matrix;
    // apply parents matrix to inherit any transformations done there.
331
    KoShapeContainer * container = d->parent;
332 333
    if (container) {
        if (container->childClipped(this))
Johannes Simon's avatar
Johannes Simon committed
334
            matrix = container->absoluteTransformation(0);
Thomas Zander's avatar
Thomas Zander committed
335
        else {
336 337 338 339
            QSizeF containerSize = container->size();
            QPointF containerPos = container->absolutePosition() - QPointF(0.5 * containerSize.width(), 0.5 * containerSize.height());
            if (converter)
                containerPos = converter->documentToView(containerPos);
340
            matrix.translate(containerPos.x(), containerPos.y());
Thomas Zander's avatar
Thomas Zander committed
341 342 343
        }
    }

344
    if (converter) {
345 346 347
        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
348
    }
349 350

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
351 352
}

353
void KoShape::applyAbsoluteTransformation(const QMatrix &matrix)
354
{
355
    QMatrix globalMatrix = absoluteTransformation(0);
356 357
    // 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
358
    // to be relative to the local coordinate system
359
    QMatrix transformMatrix = globalMatrix * matrix * globalMatrix.inverted();
360
    applyTransformation(transformMatrix);
361 362
}

363
void KoShape::applyTransformation(const QMatrix &matrix)
364
{
365
    Q_D(KoShape);
366
    d->localMatrix = matrix * d->localMatrix;
367
    notifyChanged();
368
    d->shapeChanged(GenericMatrixChange);
369 370
}

371
void KoShape::setTransformation(const QMatrix &matrix)
372
{
373
    Q_D(KoShape);
374 375
    d->localMatrix = matrix;
    notifyChanged();
376
    d->shapeChanged(GenericMatrixChange);
377
}
Thomas Zander's avatar
Thomas Zander committed
378

379
QMatrix KoShape::transformation() const
380
{
381
    Q_D(const KoShape);
382 383 384
    return d->localMatrix;
}

385 386
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
{
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
    bool foundCommonParent = false;
    KoShape *parentShapeS1 = s1;
    KoShape *parentShapeS2 = s2;
    int index1 = parentShapeS1->zIndex();
    int index2 = parentShapeS2->zIndex();
    while (parentShapeS1 && !foundCommonParent) {
        parentShapeS2 = s2;
        index2 = parentShapeS2->zIndex();
        while (parentShapeS2) {
            if (parentShapeS2 == parentShapeS1) {
                foundCommonParent = true;
                break;
            }
            index2 = parentShapeS2->zIndex();
            parentShapeS2 = parentShapeS2->parent();
402
        }
403 404 405 406

        if (!foundCommonParent) {
            index1 = parentShapeS1->zIndex();
            parentShapeS1 = parentShapeS1->parent();
407 408
        }
    }
409 410 411 412 413 414 415
    if (s1 == parentShapeS2) {
        return true;
    }
    else if (s2 == parentShapeS1) {
        return false;
    }
    return index1 < index2;
Thomas Zander's avatar
Thomas Zander committed
416 417
}

418 419
void KoShape::setParent(KoShapeContainer *parent)
{
420
    Q_D(KoShape);
421
    if (d->parent == parent)
422
        return;
423 424 425 426
    KoShapeContainer *oldParent = d->parent;
    d->parent = 0; // avoids recursive removing
    if (oldParent)
        oldParent->removeChild(this);
Thomas Zander's avatar
Thomas Zander committed
427
    if (parent && parent != this) {
Thomas Zander's avatar
Thomas Zander committed
428
        d->parent = parent;
429
        parent->addChild(this);
430
    }
431
    notifyChanged();
432
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
433 434
}

435 436
int KoShape::zIndex() const
{
437
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
438
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
439 440
}

441 442
void KoShape::update() const
{
443
    Q_D(const KoShape);
444 445 446
    if (!d->shapeManagers.empty()) {
        QRectF rect(boundingRect());
        foreach(KoShapeManager * manager, d->shapeManagers)
Jan Hambrecht's avatar
Jan Hambrecht committed
447
            manager->update(rect, this, true);
Thomas Zander's avatar
Thomas Zander committed
448 449 450
    }
}

451 452
void KoShape::update(const QRectF &shape) const
{
453
    Q_D(const KoShape);
454
    if (!d->shapeManagers.empty() && isVisible()) {
455
        QRectF rect(absoluteTransformation(0).mapRect(shape));
456
        foreach(KoShapeManager * manager, d->shapeManagers) {
457
            manager->update(rect);
458 459
        }
    }
Thomas Zander's avatar
Thomas Zander committed
460 461
}

462
QPainterPath KoShape::outline() const
463
{
464
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
465
    QPainterPath path;
466
    path.addRect(QRectF(QPointF(0, 0), QSizeF(qMax(d->size.width(), qreal(0.0001)), qMax(d->size.height(), qreal(0.0001)))));
Thomas Zander's avatar
Thomas Zander committed
467 468 469
    return path;
}

470 471
QPointF KoShape::absolutePosition(KoFlake::Position anchor) const
{
472
    QPointF point;
473 474 475 476 477 478
    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;
479
    }
480
    return absoluteTransformation(0).map(point);
Thomas Zander's avatar
Thomas Zander committed
481 482
}

483 484
void KoShape::setAbsolutePosition(QPointF newPosition, KoFlake::Position anchor)
{
485
    Q_D(KoShape);
486
    QPointF currentAbsPosition = absolutePosition(anchor);
487
    QPointF translate = newPosition - currentAbsPosition;
Jan Hambrecht's avatar
Jan Hambrecht committed
488
    QMatrix translateMatrix;
489 490
    translateMatrix.translate(translate.x(), translate.y());
    applyAbsoluteTransformation(translateMatrix);
491
    notifyChanged();
492
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
493 494
}

495 496
void KoShape::copySettings(const KoShape *shape)
{
497
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
498 499
    d->size = shape->size();
    d->connectors.clear();
Jan Hambrecht's avatar
Jan Hambrecht committed
500 501
    foreach(const QPointF & point, shape->connectionPoints())
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
502 503
    d->zIndex = shape->zIndex();
    d->visible = shape->isVisible();
504

505 506 507 508 509
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
510

511
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
512
    d->keepAspect = shape->keepAspectRatio();
513
    d->localMatrix = shape->d_ptr->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
514 515
}

Thomas Zander's avatar
Thomas Zander committed
516
void KoShape::notifyChanged()
517
{
518
    Q_D(KoShape);
519 520
    foreach(KoShapeManager * manager, d->shapeManagers) {
        manager->notifyShapeChanged(this);
521 522 523
    }
}

524 525
void KoShape::setUserData(KoShapeUserData *userData)
{
526
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
527
    delete d->userData;
Thomas Zander's avatar
Thomas Zander committed
528
    d->userData = userData;
529 530
}

531 532
KoShapeUserData *KoShape::userData() const
{
533
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
534
    return d->userData;
535 536
}

537 538
void KoShape::setApplicationData(KoShapeApplicationData *appData)
{
539
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
540
    // appdata is deleted by the application.
Thomas Zander's avatar
Thomas Zander committed
541
    d->appData = appData;
542 543
}

544 545
KoShapeApplicationData *KoShape::applicationData() const
{
546
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
547
    return d->appData;
548 549
}

550 551
bool KoShape::hasTransparency()
{
552
    Q_D(KoShape);
553
    if (! d->fill)
Thomas Zander's avatar
Thomas Zander committed
554
        return true;
555
    else
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
        return d->fill->hasTransparency() || d->transparency > 0.0;
}

void KoShape::setTransparency(qreal transparency)
{
    Q_D(KoShape);
    d->transparency = qBound(0.0, transparency, 1.0);
}

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
575 576
}

577 578
KoInsets KoShape::borderInsets() const
{
579
    Q_D(const KoShape);
580
    KoInsets answer;
581
    if (d->border)
582
        d->border->borderInsets(this, answer);
583 584 585
    return answer;
}

586 587
qreal KoShape::rotation() const
{
588
    Q_D(const KoShape);
589
    // try to extract the rotation angle out of the local matrix
590 591 592
    // if it is a pure rotation matrix

    // check if the matrix has shearing mixed in
593
    if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
594
        return std::numeric_limits<qreal>::quiet_NaN();
595
    // check if the matrix has scaling mixed in
596
    if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
597
        return std::numeric_limits<qreal>::quiet_NaN();
598 599

    // calculate the angle from the matrix elements
600 601
    qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
    if (angle < 0.0)
602 603 604
        angle += 360.0;

    return angle;
Thomas Zander's avatar
Thomas Zander committed
605 606
}

607
QSizeF KoShape::size() const
608
{
609
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
610 611 612
    return d->size;
}

613 614
QPointF KoShape::position() const
{
615
    Q_D(const KoShape);
616 617
    QPointF center(0.5*size().width(), 0.5*size().height());
    return d->localMatrix.map(center) - center;
618
    //return d->localMatrix.map( QPointF(0,0) );
Thomas Zander's avatar
Thomas Zander committed
619 620
}

621
void KoShape::addConnectionPoint(const QPointF &point)
622
{
623
    Q_D(KoShape);
624 625
    QSizeF s = size();
    // convert glue point from shape coordinates to factors of size
626
    d->connectors.append(QPointF(point.x() / s.width(), point.y() / s.height()));
Thomas Zander's avatar
Thomas Zander committed
627 628
}

629 630
QList<QPointF> KoShape::connectionPoints() const
{
631
    Q_D(const KoShape);
632 633 634
    QList<QPointF> points;
    QSizeF s = size();
    // convert glue points to shape coordinates
Jan Hambrecht's avatar
Jan Hambrecht committed
635 636
    foreach(const QPointF & cp, d->connectors)
        points.append(QPointF(s.width() * cp.x(), s.height() * cp.y()));
637 638

    return points;
639 640
}

641
void KoShape::addEventAction(KoEventAction * action)
642
{
643
    Q_D(KoShape);
644 645
    if (! d->eventActions.contains(action)) {
        d->eventActions.append(action);
646 647 648
    }
}

649
void KoShape::removeEventAction(KoEventAction * action)
650
{
651
    Q_D(KoShape);
652 653
    if (d->eventActions.contains(action)) {
        d->eventActions.removeAll(action);
654 655 656 657 658
    }
}

QList<KoEventAction *> KoShape::eventActions() const
{
659
    Q_D(const KoShape);
660 661 662
    return d->eventActions;
}

663
void KoShape::setBackground(KoShapeBackground * fill)
664
{
665
    Q_D(KoShape);
666
    if (d->fill)
667 668
        d->fill->removeUser();
    d->fill = fill;
669
    if (d->fill)
670
        d->fill->addUser();
671 672
    d->shapeChanged(BackgroundChanged);
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
673 674
}

675 676
KoShapeBackground * KoShape::background() const
{
677
    Q_D(const KoShape);
678
    return d->fill;
Thomas Zander's avatar
Thomas Zander committed
679 680
}

681 682
void KoShape::setZIndex(int zIndex)
{
683
    Q_D(KoShape);
684
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
685 686 687
    d->zIndex = zIndex;
}

688 689
void KoShape::setVisible(bool on)
{
690
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
691 692 693
    d->visible = on;
}

694
bool KoShape::isVisible(bool recursive) const
695
{
696
    Q_D(const KoShape);
697
    if (! recursive)
698
        return d->visible;
699
    if (recursive && ! d->visible)
Jan Hambrecht's avatar
Jan Hambrecht committed
700 701
        return false;

702
    KoShapeContainer * parentShape = parent();
703 704
    while (parentShape) {
        if (! parentShape->isVisible())
705 706 707 708
            return false;
        parentShape = parentShape->parent();
    }
    return true;
Thomas Zander's avatar
Thomas Zander committed
709 710
}

711 712
void KoShape::setPrintable(bool on)
{
713
    Q_D(KoShape);
714 715
    d->printable = on;
}
716

717 718
bool KoShape::isPrintable() const
{
719
    Q_D(const KoShape);
720 721 722 723
    if (d->visible)
        return d->printable;
    else
        return false;
724 725
}

726 727
void KoShape::setSelectable(bool selectable)
{
728
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
729 730 731
    d->selectable = selectable;
}

732 733
bool KoShape::isSelectable() const
{
734
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
735 736 737
    return d->selectable;
}

738
void KoShape::setGeometryProtected(bool on)
739
{
740
    Q_D(KoShape);
741
    d->geometryProtected = on;
Thomas Zander's avatar
Thomas Zander committed
742 743
}

744
bool KoShape::isGeometryProtected() const
745
{
746
    Q_D(const KoShape);
747
    return d->geometryProtected;
Thomas Zander's avatar
Thomas Zander committed
748 749
}

750 751
void KoShape::setContentProtected(bool protect)
{
752
    Q_D(KoShape);
753 754 755 756 757
    d->protectContent = protect;
}

bool KoShape::isContentProtected() const
{
758
    Q_D(const KoShape);
759 760 761
    return d->protectContent;
}

762 763
KoShapeContainer *KoShape::parent() const
{
764
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
765 766 767
    return d->parent;
}

768 769
void KoShape::setKeepAspectRatio(bool keepAspect)
{
770
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
771 772 773
    d->keepAspect = keepAspect;
}

774 775
bool KoShape::keepAspectRatio() const
{
776
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
777 778 779
    return d->keepAspect;
}

780
QString KoShape::shapeId() const
781
{
782
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
783 784 785
    return d->shapeId;
}

786