KoShape.cpp 43.2 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>
Thomas Zander's avatar
Thomas Zander committed
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 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"
49
#include <KoSnapData.h>
Thomas Zander's avatar
Thomas Zander committed
50

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

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

68 69
#include <kdebug.h>

70 71
#include <limits>

72 73 74 75 76 77 78
KoShapePrivate::KoShapePrivate(KoShape *shape)
    : size(50, 50),
    parent(0),
    userData(0),
    appData(0),
    fill(0),
    border(0),
79
    q_ptr(shape),
80
    shadow(0),
81
    filterEffectStack(0),
82
    transparency(0.0),
83 84 85 86 87 88 89 90
    zIndex(0),
    visible(true),
    printable(true),
    geometryProtected(false),
    keepAspect(false),
    selectable(true),
    detectCollision(false),
    protectContent(false)
91
{
92 93 94 95
    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));
96
}
Thomas Zander's avatar
Thomas Zander committed
97

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

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

130 131
void KoShapePrivate::updateBorder()
{
132
    Q_Q(KoShape);
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
    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
151

152 153 154 155 156 157 158 159 160
void KoShapePrivate::addShapeManager(KoShapeManager *manager)
{
    shapeManagers.insert(manager);
}

void KoShapePrivate::removeShapeManager(KoShapeManager *manager)
{
    shapeManagers.remove(manager);
}
161
// static
162
QString KoShapePrivate::getStyleProperty(const char *property, KoShapeLoadingContext &context)
163 164 165 166 167 168 169 170 171 172 173
{
    KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
    QString value;

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

    return value;
}

174 175


176
// ======== KoShape
Thomas Zander's avatar
Thomas Zander committed
177
KoShape::KoShape()
178
        : d_ptr(new KoShapePrivate(this))
Thomas Zander's avatar
Thomas Zander committed
179
{
180
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
181 182
}

183 184 185 186 187
KoShape::KoShape(KoShapePrivate &dd)
    : d_ptr(&dd)
{
}

Thomas Zander's avatar
Thomas Zander committed
188 189
KoShape::~KoShape()
{
190
    Q_D(KoShape);
191
    d->shapeChanged(Deleted);
192
    delete d_ptr;
Thomas Zander's avatar
Thomas Zander committed
193 194
}

195 196
void KoShape::paintDecorations(QPainter &painter, const KoViewConverter &converter, const KoCanvasBase *canvas)
{
Thomas Zander's avatar
Thomas Zander committed
197 198 199
    Q_UNUSED(painter);
    Q_UNUSED(converter);
    Q_UNUSED(canvas);
200
    /* Since this code is not actually used (kivio is going to be the main user) lets disable instead of fix.
201
        if (selected)
Thomas Zander's avatar
Thomas Zander committed
202
        {
203
            // draw connectors
204 205 206 207 208
            QPen pen(Qt::blue);
            pen.setWidth(0);
            painter.setPen(pen);
            painter.setBrush(Qt::NoBrush);
            for (int i = 0; i < d->connectors.size(); ++i)
209 210
            {
                QPointF p = converter.documentToView(d->connectors[ i ]);
211 212
                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));
213 214
            }
        }*/
Thomas Zander's avatar
Thomas Zander committed
215 216
}

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

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

231
void KoShape::rotate(qreal angle)
Thomas Zander's avatar
Thomas Zander committed
232
{
233
    Q_D(KoShape);
234
    QPointF center = d->localMatrix.map(QPointF(0.5 * size().width(), 0.5 * size().height()));
235
    QMatrix rotateMatrix;
236 237 238
    rotateMatrix.translate(center.x(), center.y());
    rotateMatrix.rotate(angle);
    rotateMatrix.translate(-center.x(), -center.y());
239
    d->localMatrix = d->localMatrix * rotateMatrix;
240

241
    notifyChanged();
242
    d->shapeChanged(RotationChanged);
Thomas Zander's avatar
Thomas Zander committed
243 244
}

245
void KoShape::setShear(qreal sx, qreal sy)
Thomas Zander's avatar
Thomas Zander committed
246
{
247
    Q_D(KoShape);
248 249
    QPointF pos = position();
    QMatrix shearMatrix;
250 251 252
    shearMatrix.translate(pos.x(), pos.y());
    shearMatrix.shear(sx, sy);
    shearMatrix.translate(-pos.x(), -pos.y());
253
    d->localMatrix = d->localMatrix * shearMatrix;
254

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

259
void KoShape::setSize(const QSizeF &newSize)
Thomas Zander's avatar
Thomas Zander committed
260
{
261
    Q_D(KoShape);
262 263
    QSizeF oldSize(size());
    if (oldSize == newSize)
Thomas Zander's avatar
Thomas Zander committed
264
        return;
265

Thomas Zander's avatar
Thomas Zander committed
266
    d->size = newSize;
267

268
    notifyChanged();
269
    d->shapeChanged(SizeChanged);
Thomas Zander's avatar
Thomas Zander committed
270 271
}

272
void KoShape::setPosition(const QPointF &newPosition)
Thomas Zander's avatar
Thomas Zander committed
273
{
274
    Q_D(KoShape);
275
    QPointF currentPos = position();
276
    if (newPosition == currentPos)
Thomas Zander's avatar
Thomas Zander committed
277
        return;
278
    QMatrix translateMatrix;
279
    translateMatrix.translate(newPosition.x() - currentPos.x(), newPosition.y() - currentPos.y());
280 281 282
    d->localMatrix = d->localMatrix * translateMatrix;

    notifyChanged();
283
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
284 285
}

286
bool KoShape::hitTest(const QPointF &position) const
Thomas Zander's avatar
Thomas Zander committed
287
{
288
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
289
    if (d->parent && d->parent->isClipped(this) && !d->parent->hitTest(position))
Thomas Zander's avatar
Thomas Zander committed
290 291
        return false;

292
    QPointF point = absoluteTransformation(0).inverted().map(position);
293
    QRectF bb(QPointF(), size());
294
    if (d->border) {
295
        KoInsets insets;
296
        d->border->borderInsets(this, insets);
297
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
298
    }
299 300 301
    if (bb.contains(point))
        return true;

302
    // if there is no shadow we can as well just leave
303
    if (! d->shadow)
304
        return false;
Thomas Zander's avatar
Thomas Zander committed
305

306 307
    // the shadow has an offset to the shape, so we simply
    // check if the position minus the shadow offset hits the shape
308
    point = absoluteTransformation(0).inverted().map(position - d->shadow->offset());
309

310
    return bb.contains(point);
Thomas Zander's avatar
Thomas Zander committed
311 312
}

313
QRectF KoShape::boundingRect() const
Thomas Zander's avatar
Thomas Zander committed
314
{
315
    Q_D(const KoShape);
316 317 318
    QSizeF mySize = size();
    QMatrix transform = absoluteTransformation(0);
    QRectF bb(QPointF(0, 0), mySize);
319
    if (d->border) {
320 321 322 323
        KoInsets insets;
        d->border->borderInsets(this, insets);
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
324
    bb = transform.mapRect(bb);
325
    if (d->shadow) {
326
        KoInsets insets;
Thomas Zander's avatar
Thomas Zander committed
327
        d->shadow->insets(insets);
328 329
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
330 331 332 333
    if (d->filterEffectStack) {
        QRectF clipRect = d->filterEffectStack->clipRectForBoundingRect(QRectF(QPointF(), mySize));
        bb |= transform.mapRect(clipRect);
    }
Thomas Zander's avatar
Thomas Zander committed
334

335
    return bb;
Thomas Zander's avatar
Thomas Zander committed
336 337
}

338 339
QMatrix KoShape::absoluteTransformation(const KoViewConverter *converter) const
{
340
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
341 342
    QMatrix matrix;
    // apply parents matrix to inherit any transformations done there.
343
    KoShapeContainer * container = d->parent;
344
    if (container) {
Thomas Zander's avatar
Thomas Zander committed
345
        if (container->isClipped(this)) {
346 347 348 349
            // We do need to pass the converter here, otherwise the parent's
            // translation is not inherited.
            matrix = container->absoluteTransformation(converter);
        } else {
350 351 352 353
            QSizeF containerSize = container->size();
            QPointF containerPos = container->absolutePosition() - QPointF(0.5 * containerSize.width(), 0.5 * containerSize.height());
            if (converter)
                containerPos = converter->documentToView(containerPos);
354
            matrix.translate(containerPos.x(), containerPos.y());
Thomas Zander's avatar
Thomas Zander committed
355 356 357
        }
    }

358
    if (converter) {
359 360 361
        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
362
    }
363 364

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
365 366
}

367
void KoShape::applyAbsoluteTransformation(const QMatrix &matrix)
368
{
369
    QMatrix globalMatrix = absoluteTransformation(0);
370 371
    // 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
372
    // to be relative to the local coordinate system
373
    QMatrix transformMatrix = globalMatrix * matrix * globalMatrix.inverted();
374
    applyTransformation(transformMatrix);
375 376
}

377
void KoShape::applyTransformation(const QMatrix &matrix)
378
{
379
    Q_D(KoShape);
380
    d->localMatrix = matrix * d->localMatrix;
381
    notifyChanged();
382
    d->shapeChanged(GenericMatrixChange);
383 384
}

385
void KoShape::setTransformation(const QMatrix &matrix)
386
{
387
    Q_D(KoShape);
388 389
    d->localMatrix = matrix;
    notifyChanged();
390
    d->shapeChanged(GenericMatrixChange);
391
}
Thomas Zander's avatar
Thomas Zander committed
392

393
QMatrix KoShape::transformation() const
394
{
395
    Q_D(const KoShape);
396 397 398
    return d->localMatrix;
}

399 400
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
{
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
    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();
416
        }
417 418 419 420

        if (!foundCommonParent) {
            index1 = parentShapeS1->zIndex();
            parentShapeS1 = parentShapeS1->parent();
421 422
        }
    }
423 424 425 426 427 428 429
    if (s1 == parentShapeS2) {
        return true;
    }
    else if (s2 == parentShapeS1) {
        return false;
    }
    return index1 < index2;
Thomas Zander's avatar
Thomas Zander committed
430 431
}

432 433
void KoShape::setParent(KoShapeContainer *parent)
{
434
    Q_D(KoShape);
435
    if (d->parent == parent)
436
        return;
437 438 439
    KoShapeContainer *oldParent = d->parent;
    d->parent = 0; // avoids recursive removing
    if (oldParent)
Thomas Zander's avatar
Thomas Zander committed
440
        oldParent->removeShape(this);
Thomas Zander's avatar
Thomas Zander committed
441
    if (parent && parent != this) {
Thomas Zander's avatar
Thomas Zander committed
442
        d->parent = parent;
Thomas Zander's avatar
Thomas Zander committed
443
        parent->addShape(this);
444
    }
445
    notifyChanged();
446
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
447 448
}

449 450
int KoShape::zIndex() const
{
451
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
452
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
453 454
}

455 456
void KoShape::update() const
{
457
    Q_D(const KoShape);
458 459 460
    if (!d->shapeManagers.empty()) {
        QRectF rect(boundingRect());
        foreach(KoShapeManager * manager, d->shapeManagers)
Jan Hambrecht's avatar
Jan Hambrecht committed
461
            manager->update(rect, this, true);
Thomas Zander's avatar
Thomas Zander committed
462 463 464
    }
}

465 466
void KoShape::update(const QRectF &shape) const
{
467
    Q_D(const KoShape);
468
    if (!d->shapeManagers.empty() && isVisible()) {
469
        QRectF rect(absoluteTransformation(0).mapRect(shape));
470
        foreach(KoShapeManager * manager, d->shapeManagers) {
471
            manager->update(rect);
472 473
        }
    }
Thomas Zander's avatar
Thomas Zander committed
474 475
}

476
QPainterPath KoShape::outline() const
477
{
478
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
479
    QPainterPath path;
480
    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
481 482 483
    return path;
}

484 485
QPointF KoShape::absolutePosition(KoFlake::Position anchor) const
{
486
    QPointF point;
487 488 489 490 491 492
    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;
493
    }
494
    return absoluteTransformation(0).map(point);
Thomas Zander's avatar
Thomas Zander committed
495 496
}

497 498
void KoShape::setAbsolutePosition(QPointF newPosition, KoFlake::Position anchor)
{
499
    Q_D(KoShape);
500
    QPointF currentAbsPosition = absolutePosition(anchor);
501
    QPointF translate = newPosition - currentAbsPosition;
Jan Hambrecht's avatar
Jan Hambrecht committed
502
    QMatrix translateMatrix;
503 504
    translateMatrix.translate(translate.x(), translate.y());
    applyAbsoluteTransformation(translateMatrix);
505
    notifyChanged();
506
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
507 508
}

509 510
void KoShape::copySettings(const KoShape *shape)
{
511
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
512 513
    d->size = shape->size();
    d->connectors.clear();
514
    foreach(const QPointF &point, shape->connectionPoints())
Jan Hambrecht's avatar
Jan Hambrecht committed
515
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
516 517
    d->zIndex = shape->zIndex();
    d->visible = shape->isVisible();
518

519 520 521 522 523
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
524

525
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
526
    d->keepAspect = shape->keepAspectRatio();
527
    d->localMatrix = shape->d_ptr->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
528 529
}

Thomas Zander's avatar
Thomas Zander committed
530
void KoShape::notifyChanged()
531
{
532
    Q_D(KoShape);
533 534
    foreach(KoShapeManager * manager, d->shapeManagers) {
        manager->notifyShapeChanged(this);
535 536 537
    }
}

538 539
void KoShape::setUserData(KoShapeUserData *userData)
{
540
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
541
    delete d->userData;
Thomas Zander's avatar
Thomas Zander committed
542
    d->userData = userData;
543 544
}

545 546
KoShapeUserData *KoShape::userData() const
{
547
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
548
    return d->userData;
549 550
}

551 552
void KoShape::setApplicationData(KoShapeApplicationData *appData)
{
553
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
554
    // appdata is deleted by the application.
Thomas Zander's avatar
Thomas Zander committed
555
    d->appData = appData;
556 557
}

558 559
KoShapeApplicationData *KoShape::applicationData() const
{
560
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
561
    return d->appData;
562 563
}

564 565
bool KoShape::hasTransparency()
{
566
    Q_D(KoShape);
567
    if (! d->fill)
Thomas Zander's avatar
Thomas Zander committed
568
        return true;
569
    else
570 571 572 573 574 575
        return d->fill->hasTransparency() || d->transparency > 0.0;
}

void KoShape::setTransparency(qreal transparency)
{
    Q_D(KoShape);
576
    d->transparency = qBound<qreal>(0.0, transparency, 1.0);
577 578 579 580 581 582 583 584 585 586 587 588
}

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
589 590
}

591 592
KoInsets KoShape::borderInsets() const
{
593
    Q_D(const KoShape);
594
    KoInsets answer;
595
    if (d->border)
596
        d->border->borderInsets(this, answer);
597 598 599
    return answer;
}

600 601
qreal KoShape::rotation() const
{
602
    Q_D(const KoShape);
603
    // try to extract the rotation angle out of the local matrix
604 605 606
    // if it is a pure rotation matrix

    // check if the matrix has shearing mixed in
607
    if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
608
        return std::numeric_limits<qreal>::quiet_NaN();
609
    // check if the matrix has scaling mixed in
610
    if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
611
        return std::numeric_limits<qreal>::quiet_NaN();
612 613

    // calculate the angle from the matrix elements
614 615
    qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
    if (angle < 0.0)
616 617 618
        angle += 360.0;

    return angle;
Thomas Zander's avatar
Thomas Zander committed
619 620
}

621
QSizeF KoShape::size() const
622
{
623
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
624 625 626
    return d->size;
}

627 628
QPointF KoShape::position() const
{
629
    Q_D(const KoShape);
630 631
    QPointF center(0.5*size().width(), 0.5*size().height());
    return d->localMatrix.map(center) - center;
632
    //return d->localMatrix.map(QPointF(0,0));
Thomas Zander's avatar
Thomas Zander committed
633 634
}

635
void KoShape::addConnectionPoint(const QPointF &point)
636
{
637
    Q_D(KoShape);
638 639
    QSizeF s = size();
    // convert glue point from shape coordinates to factors of size
640
    d->connectors.append(QPointF(point.x() / s.width(), point.y() / s.height()));
Thomas Zander's avatar
Thomas Zander committed
641 642
}

643 644
QList<QPointF> KoShape::connectionPoints() const
{
645
    Q_D(const KoShape);
646 647 648
    QList<QPointF> points;
    QSizeF s = size();
    // convert glue points to shape coordinates
649
    foreach(const QPointF &cp, d->connectors)
Jan Hambrecht's avatar
Jan Hambrecht committed
650
        points.append(QPointF(s.width() * cp.x(), s.height() * cp.y()));
651 652

    return points;
653 654
}

655
void KoShape::addEventAction(KoEventAction *action)
656
{
657
    Q_D(KoShape);
658
    d->eventActions.insert(action);
659 660
}

661
void KoShape::removeEventAction(KoEventAction *action)
662
{
663
    Q_D(KoShape);
664
    d->eventActions.remove(action);
665 666
}

667
QSet<KoEventAction *> KoShape::eventActions() const
668
{
669
    Q_D(const KoShape);
670 671 672
    return d->eventActions;
}

673
void KoShape::setBackground(KoShapeBackground *fill)
674
{
675
    Q_D(KoShape);
676
    if (d->fill)
677
        d->fill->deref();
678
    d->fill = fill;
679
    if (d->fill)
680
        d->fill->ref();
681 682
    d->shapeChanged(BackgroundChanged);
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
683 684
}

685 686
KoShapeBackground * KoShape::background() const
{
687
    Q_D(const KoShape);
688
    return d->fill;
Thomas Zander's avatar
Thomas Zander committed
689 690
}

691 692
void KoShape::setZIndex(int zIndex)
{
693
    Q_D(KoShape);
694
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
695 696 697
    d->zIndex = zIndex;
}

698 699
void KoShape::setVisible(bool on)
{
700
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
701 702 703
    d->visible = on;
}

704
bool KoShape::isVisible(bool recursive) const
705
{
706
    Q_D(const KoShape);
707
    if (! recursive)
708
        return d->visible;
709
    if (recursive && ! d->visible)
Jan Hambrecht's avatar
Jan Hambrecht committed
710 711
        return false;

712
    KoShapeContainer * parentShape = parent();
713 714
    while (parentShape) {
        if (! parentShape->isVisible())
715 716 717 718
            return false;
        parentShape = parentShape->parent();
    }
    return true;
Thomas Zander's avatar
Thomas Zander committed
719 720
}

721 722
void KoShape::setPrintable(bool on)
{
723
    Q_D(KoShape);
724 725
    d->printable = on;
}
726

727 728
bool KoShape::isPrintable() const
{
729
    Q_D(const KoShape);
730 731 732 733
    if (d->visible)
        return d->printable;
    else
        return false;
734 735
}

736 737
void KoShape::setSelectable(bool selectable)
{
738
    Q_D(KoShape);
Thomas Zander's avatar
Thomas Zander committed
739 740 741
    d->selectable = selectable;
}

742 743
bool KoShape::isSelectable() const
{
744
    Q_D(const KoShape);
Thomas Zander's avatar
Thomas Zander committed
745 746 747
    return d->selectable;
}

748
void KoShape::setGeometryProtected(bool on)
749
{
750
    Q_D(KoShape);
751
    d->geometryProtected = on;
Thomas Zander's avatar
Thomas Zander committed
752 753
}

754
bool KoShape::isGeometryProtected() const
755
{
756
    Q_D(const KoShape);
757
    return d->geometryProtected;
Thomas Zander's avatar
Thomas Zander committed
758 759
}

760 761
void KoShape::setContentProtected(bool protect)
{
762
    Q_D(KoShape);
763 764 765 766 767
    d->protectContent = protect;
}

bool KoShape::isContentProtected() const
{