KoShape.cpp 38.1 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 24

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

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

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

65 66
#include <kdebug.h>

67 68
#include <limits>

69 70
class KoShape::Private
{
Thomas Zander's avatar
Thomas Zander committed
71
public:
72
    Private(KoShape *shape)
73 74
            : size(50, 50),
            parent(0),
75 76 77 78 79 80 81
            userData(0),
            appData(0),
            fill(0),
            border(0),
            me(shape),
            shadow(0),
            zIndex(0),
82 83
            visible(true),
            printable(true),
84
            geometryProtected(false),
85 86 87
            keepAspect(false),
            selectable(true),
            detectCollision(false),
88 89
            protectContent(false)
    {
Thomas Zander's avatar
Thomas Zander committed
90 91
    }

92
    ~Private() {
93 94
        if (parent)
            parent->removeChild(me);
Jan Hambrecht's avatar
Jan Hambrecht committed
95
        foreach(KoShapeManager *manager, shapeManagers) {
Jan Hambrecht's avatar
Jan Hambrecht committed
96
            manager->remove(me);
Jan Hambrecht's avatar
Jan Hambrecht committed
97 98
            manager->removeAdditional(me);
        }
Thomas Zander's avatar
Thomas Zander committed
99 100
        delete userData;
        delete appData;
101
        if (border && ! border->removeUser())
102
            delete border;
103
        if (shadow && ! shadow->removeUser())
104
            delete shadow;
105
        if (fill && ! fill->removeUser())
106
            delete fill;
107
        qDeleteAll(eventActions);
Thomas Zander's avatar
Thomas Zander committed
108 109
    }

110
    void shapeChanged(ChangeType type) {
111
        if (parent)
112 113
            parent->model()->childChanged(me, type);
        me->shapeChanged(type);
114
        foreach(KoShape * shape, dependees)
115
            shape->shapeChanged(type, me);
116 117
    }

Thomas Zander's avatar
Thomas Zander committed
118 119
    QSizeF size; // size in pt
    QString shapeId;
120
    QString name; ///< the shapes names
Thomas Zander's avatar
Thomas Zander committed
121

122
    QMatrix localMatrix; ///< the shapes local transformation matrix
Thomas Zander's avatar
Thomas Zander committed
123 124 125 126 127 128 129

    QVector<QPointF> connectors; // in pt

    KoShapeContainer *parent;
    QSet<KoShapeManager *> shapeManagers;
    KoShapeUserData *userData;
    KoShapeApplicationData *appData;
130
    KoShapeBackground * fill; ///< Stands for the background color / fill etc.
131
    KoShapeBorderModel *border; ///< points to a border, or 0 if there is no border
132
    KoShape *me;
133
    QList<KoShape*> dependees; ///< list of shape dependent on this shape
134
    KoShapeShadow * shadow; ///< the current shape shadow
135
    QMap<QByteArray, QString> additionalAttributes;
136
    QMap<QByteArray, QString> additionalStyleAttributes;
Thorsten Zachmann's avatar
Thorsten Zachmann committed
137
    QList<KoEventAction *> eventActions; ///< list of event actions the shape has
138

139 140 141
    int zIndex : 14; // should be enough ;)
    int visible : 1;
    int printable : 1;
142
    int geometryProtected : 1;
143 144 145 146
    int keepAspect : 1;
    int selectable : 1;
    int detectCollision : 1;
    int protectContent : 1;
Thomas Zander's avatar
Thomas Zander committed
147 148
};

Thomas Zander's avatar
Thomas Zander committed
149
KoShape::KoShape()
150
        : d(new Private(this))
Thomas Zander's avatar
Thomas Zander committed
151
{
152 153 154 155
    d->connectors.append(QPointF(0.5, 0.0));
    d->connectors.append(QPointF(1.0, 0.5));
    d->connectors.append(QPointF(0.5, 1.0));
    d->connectors.append(QPointF(0.0, 0.5));
156

157
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
158 159 160 161
}

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

166 167
void KoShape::paintDecorations(QPainter &painter, const KoViewConverter &converter, const KoCanvasBase *canvas)
{
Thomas Zander's avatar
Thomas Zander committed
168 169 170
    Q_UNUSED(painter);
    Q_UNUSED(converter);
    Q_UNUSED(canvas);
171 172
    /* 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
173
        {
174 175 176 177 178 179 180 181 182 183 184 185
            // 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
186 187
}

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

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

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

210
    notifyChanged();
211
    d->shapeChanged(RotationChanged);
Thomas Zander's avatar
Thomas Zander committed
212 213
}

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

223
    notifyChanged();
224
    d->shapeChanged(ShearChanged);
Thomas Zander's avatar
Thomas Zander committed
225 226
}

227
void KoShape::setSize(const QSizeF &newSize)
Thomas Zander's avatar
Thomas Zander committed
228
{
229 230
    QSizeF oldSize(size());
    if (oldSize == newSize)
Thomas Zander's avatar
Thomas Zander committed
231
        return;
232

233
    QMatrix matrix;
234 235
    oldSize.setHeight(qMax((qreal) 1E-4, oldSize.height())); // avoids devision by zero below
    oldSize.setWidth(qMax((qreal) 1E-4, oldSize.width()));
236
    matrix.scale(newSize.width()/oldSize.width(), newSize.height()/oldSize.height());
237 238

    KoGradientBackground * g = dynamic_cast<KoGradientBackground*>(d->fill);
239
    if (g) {
240
        g->setMatrix(g->matrix() * matrix);
241
    }
242
    KoLineBorder *l = dynamic_cast<KoLineBorder*>(d->border);
243 244
    if (l && l->lineBrush().gradient()) {
        QBrush brush = l->lineBrush();
245 246
        brush.setMatrix(brush.matrix() * matrix);
        l->setLineBrush(brush);
247
    }
248

Thomas Zander's avatar
Thomas Zander committed
249
    d->size = newSize;
250

251
    notifyChanged();
252
    d->shapeChanged(SizeChanged);
Thomas Zander's avatar
Thomas Zander committed
253 254
}

255
void KoShape::setPosition(const QPointF &newPosition)
Thomas Zander's avatar
Thomas Zander committed
256
{
257
    QPointF currentPos = position();
258
    if (newPosition == currentPos)
Thomas Zander's avatar
Thomas Zander committed
259
        return;
260
    QMatrix translateMatrix;
261
    translateMatrix.translate(newPosition.x() - currentPos.x(), newPosition.y() - currentPos.y());
262 263 264
    d->localMatrix = d->localMatrix * translateMatrix;

    notifyChanged();
265
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
266 267
}

268
bool KoShape::hitTest(const QPointF &position) const
Thomas Zander's avatar
Thomas Zander committed
269
{
270
    if (d->parent && d->parent->childClipped(this) && !d->parent->hitTest(position))
Thomas Zander's avatar
Thomas Zander committed
271 272
        return false;

273
    QPointF point = absoluteTransformation(0).inverted().map(position);
274
    QRectF bb(QPointF(), size());
275
    if (d->border) {
276
        KoInsets insets;
277
        d->border->borderInsets(this, insets);
278
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
279
    }
280 281 282
    if (bb.contains(point))
        return true;

283
    // if there is no shadow we can as well just leave
284
    if (! d->shadow)
285
        return false;
Thomas Zander's avatar
Thomas Zander committed
286

287 288
    // the shadow has an offset to the shape, so we simply
    // check if the position minus the shadow offset hits the shape
289
    point = absoluteTransformation(0).inverted().map(position - d->shadow->offset());
290

291
    return bb.contains(point);
Thomas Zander's avatar
Thomas Zander committed
292 293
}

294
QRectF KoShape::boundingRect() const
Thomas Zander's avatar
Thomas Zander committed
295
{
296
    QRectF bb(QPointF(0, 0), size());
297
    if (d->border) {
298 299 300 301
        KoInsets insets;
        d->border->borderInsets(this, insets);
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
302 303
    bb = absoluteTransformation(0).mapRect(bb);
    if (d->shadow) {
304
        KoInsets insets;
305
        d->shadow->insets(this, insets);
306 307 308
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
    return bb;
Thomas Zander's avatar
Thomas Zander committed
309 310
}

311 312
QMatrix KoShape::absoluteTransformation(const KoViewConverter *converter) const
{
Thomas Zander's avatar
Thomas Zander committed
313 314
    QMatrix matrix;
    // apply parents matrix to inherit any transformations done there.
315
    KoShapeContainer * container = d->parent;
316 317
    if (container) {
        if (container->childClipped(this))
318
            matrix = container->absoluteTransformation(0);
Thomas Zander's avatar
Thomas Zander committed
319
        else {
320 321 322 323
            QSizeF containerSize = container->size();
            QPointF containerPos = container->absolutePosition() - QPointF(0.5 * containerSize.width(), 0.5 * containerSize.height());
            if (converter)
                containerPos = converter->documentToView(containerPos);
324
            matrix.translate(containerPos.x(), containerPos.y());
Thomas Zander's avatar
Thomas Zander committed
325 326 327
        }
    }

328
    if (converter) {
329 330 331
        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
332
    }
333 334

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
335 336
}

337
void KoShape::applyAbsoluteTransformation(const QMatrix &matrix)
338
{
339
    QMatrix globalMatrix = absoluteTransformation(0);
340 341
    // 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
342
    // to be relative to the local coordinate system
343
    QMatrix transformMatrix = globalMatrix * matrix * globalMatrix.inverted();
344
    applyTransformation(transformMatrix);
345 346
}

347
void KoShape::applyTransformation(const QMatrix &matrix)
348 349
{
    d->localMatrix = matrix * d->localMatrix;
350
    notifyChanged();
351
    d->shapeChanged(GenericMatrixChange);
352 353
}

354
void KoShape::setTransformation(const QMatrix &matrix)
355 356 357
{
    d->localMatrix = matrix;
    notifyChanged();
358
    d->shapeChanged(GenericMatrixChange);
359
}
Thomas Zander's avatar
Thomas Zander committed
360

361
QMatrix KoShape::transformation() const
362 363 364 365
{
    return d->localMatrix;
}

366 367
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
{
368
    int diff = s1->zIndex() - s2->zIndex();
369
    if (diff == 0) {
370
        KoShape *s = s1->parent();
371
        while (s) {
372
            if (s == s2) // s1 is a child of s2
373 374 375 376
                return false; // children are always on top of their parents.
            s = s->parent();
        }
        s = s2->parent();
377
        while (s) {
378
            if (s == s1) // s2 is a child of s1
379 380 381 382 383
                return true;
            s = s->parent();
        }
    }
    return diff < 0;
Thomas Zander's avatar
Thomas Zander committed
384 385
}

386 387 388
void KoShape::setParent(KoShapeContainer *parent)
{
    if (d->parent == parent)
389
        return;
390 391 392 393
    KoShapeContainer *oldParent = d->parent;
    d->parent = 0; // avoids recursive removing
    if (oldParent)
        oldParent->removeChild(this);
Thomas Zander's avatar
Thomas Zander committed
394
    if (parent && parent != this) {
Thomas Zander's avatar
Thomas Zander committed
395
        d->parent = parent;
396
        parent->addChild(this);
397
    }
398
    notifyChanged();
399
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
400 401
}

402 403 404
int KoShape::zIndex() const
{
    if (parent()) // we can't be under our parent...
405
        return qMax((int) d->zIndex, parent()->zIndex());
Thomas Zander's avatar
Thomas Zander committed
406
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
407 408
}

409 410
void KoShape::update() const
{
411 412 413
    if (!d->shapeManagers.empty()) {
        QRectF rect(boundingRect());
        foreach(KoShapeManager * manager, d->shapeManagers)
Jan Hambrecht's avatar
Jan Hambrecht committed
414
            manager->update(rect, this, true);
Thomas Zander's avatar
Thomas Zander committed
415 416 417
    }
}

418 419
void KoShape::update(const QRectF &shape) const
{
420
    if (!d->shapeManagers.empty() && isVisible()) {
421
        QRectF rect(absoluteTransformation(0).mapRect(shape));
422
        foreach(KoShapeManager * manager, d->shapeManagers) {
423
            manager->update(rect);
424 425
        }
    }
Thomas Zander's avatar
Thomas Zander committed
426 427
}

428 429
const QPainterPath KoShape::outline() const
{
Thomas Zander's avatar
Thomas Zander committed
430
    QPainterPath path;
431
    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
432 433 434
    return path;
}

435 436
QPointF KoShape::absolutePosition(KoFlake::Position anchor) const
{
437
    QPointF point;
438 439 440 441 442 443
    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;
444
    }
445
    return absoluteTransformation(0).map(point);
Thomas Zander's avatar
Thomas Zander committed
446 447
}

448 449
void KoShape::setAbsolutePosition(QPointF newPosition, KoFlake::Position anchor)
{
450
    QPointF currentAbsPosition = absolutePosition(anchor);
451
    QPointF translate = newPosition - currentAbsPosition;
Jan Hambrecht's avatar
Jan Hambrecht committed
452
    QMatrix translateMatrix;
453 454
    translateMatrix.translate(translate.x(), translate.y());
    applyAbsoluteTransformation(translateMatrix);
455
    notifyChanged();
456
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
457 458
}

459 460
void KoShape::copySettings(const KoShape *shape)
{
Thomas Zander's avatar
Thomas Zander committed
461 462
    d->size = shape->size();
    d->connectors.clear();
Jan Hambrecht's avatar
Jan Hambrecht committed
463 464
    foreach(const QPointF & point, shape->connectionPoints())
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
465 466
    d->zIndex = shape->zIndex();
    d->visible = shape->isVisible();
467

468 469 470 471 472
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
473

474
    d->geometryProtected = shape->isGeometryProtected();
Thomas Zander's avatar
Thomas Zander committed
475
    d->keepAspect = shape->keepAspectRatio();
476
    d->localMatrix = shape->d->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
477 478
}

Thomas Zander's avatar
Thomas Zander committed
479
void KoShape::notifyChanged()
480
{
481 482
    foreach(KoShapeManager * manager, d->shapeManagers) {
        manager->notifyShapeChanged(this);
483 484 485
    }
}

486 487
void KoShape::setUserData(KoShapeUserData *userData)
{
488
    delete d->userData;
Thomas Zander's avatar
Thomas Zander committed
489
    d->userData = userData;
490 491
}

492 493
KoShapeUserData *KoShape::userData() const
{
Thomas Zander's avatar
Thomas Zander committed
494
    return d->userData;
495 496
}

497 498
void KoShape::setApplicationData(KoShapeApplicationData *appData)
{
499
    // appdata is deleted by the application.
Thomas Zander's avatar
Thomas Zander committed
500
    d->appData = appData;
501 502
}

503 504
KoShapeApplicationData *KoShape::applicationData() const
{
Thomas Zander's avatar
Thomas Zander committed
505
    return d->appData;
506 507
}

508 509
bool KoShape::hasTransparency()
{
510
    if (! d->fill)
Thomas Zander's avatar
Thomas Zander committed
511
        return true;
512 513
    else
        return d->fill->hasTransparency();
Thomas Zander's avatar
Thomas Zander committed
514 515
}

516 517
KoInsets KoShape::borderInsets() const
{
518
    KoInsets answer;
519
    if (d->border)
520
        d->border->borderInsets(this, answer);
521 522 523
    return answer;
}

524 525
qreal KoShape::rotation() const
{
526
    // try to extract the rotation angle out of the local matrix
527 528 529
    // if it is a pure rotation matrix

    // check if the matrix has shearing mixed in
530
    if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
531
        return std::numeric_limits<qreal>::quiet_NaN();
532
    // check if the matrix has scaling mixed in
533
    if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
534
        return std::numeric_limits<qreal>::quiet_NaN();
535 536

    // calculate the angle from the matrix elements
537 538
    qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
    if (angle < 0.0)
539 540 541
        angle += 360.0;

    return angle;
Thomas Zander's avatar
Thomas Zander committed
542 543
}

544
QSizeF KoShape::size() const
545
{
Thomas Zander's avatar
Thomas Zander committed
546 547 548
    return d->size;
}

549 550
QPointF KoShape::position() const
{
551 552
    QPointF center(0.5*size().width(), 0.5*size().height());
    return d->localMatrix.map(center) - center;
553
    //return d->localMatrix.map( QPointF(0,0) );
Thomas Zander's avatar
Thomas Zander committed
554 555
}

556
void KoShape::addConnectionPoint(const QPointF &point)
557
{
558 559
    QSizeF s = size();
    // convert glue point from shape coordinates to factors of size
560
    d->connectors.append(QPointF(point.x() / s.width(), point.y() / s.height()));
Thomas Zander's avatar
Thomas Zander committed
561 562
}

563 564
QList<QPointF> KoShape::connectionPoints() const
{
565 566 567
    QList<QPointF> points;
    QSizeF s = size();
    // convert glue points to shape coordinates
Jan Hambrecht's avatar
Jan Hambrecht committed
568 569
    foreach(const QPointF & cp, d->connectors)
        points.append(QPointF(s.width() * cp.x(), s.height() * cp.y()));
570 571

    return points;
572 573
}

574
void KoShape::addEventAction(KoEventAction * action)
575
{
576 577
    if (! d->eventActions.contains(action)) {
        d->eventActions.append(action);
578 579 580
    }
}

581
void KoShape::removeEventAction(KoEventAction * action)
582
{
583 584
    if (d->eventActions.contains(action)) {
        d->eventActions.removeAll(action);
585 586 587 588 589 590 591 592
    }
}

QList<KoEventAction *> KoShape::eventActions() const
{
    return d->eventActions;
}

593
void KoShape::setBackground(KoShapeBackground * fill)
594 595
{
    if (d->fill)
596 597
        d->fill->removeUser();
    d->fill = fill;
598
    if (d->fill)
599
        d->fill->addUser();
600 601
    d->shapeChanged(BackgroundChanged);
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
602 603
}

604 605
KoShapeBackground * KoShape::background() const
{
606
    return d->fill;
Thomas Zander's avatar
Thomas Zander committed
607 608
}

609 610
void KoShape::setZIndex(int zIndex)
{
611
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
612 613 614
    d->zIndex = zIndex;
}

615 616
void KoShape::setVisible(bool on)
{
Thomas Zander's avatar
Thomas Zander committed
617 618 619
    d->visible = on;
}

620
bool KoShape::isVisible(bool recursive) const
621
{
622
    if (! recursive)
623
        return d->visible;
624
    if (recursive && ! d->visible)
Jan Hambrecht's avatar
Jan Hambrecht committed
625 626
        return false;

627
    KoShapeContainer * parentShape = parent();
628 629
    while (parentShape) {
        if (! parentShape->isVisible())
630 631 632 633
            return false;
        parentShape = parentShape->parent();
    }
    return true;
Thomas Zander's avatar
Thomas Zander committed
634 635
}

636 637 638 639
void KoShape::setPrintable(bool on)
{
    d->printable = on;
}
640

641 642
bool KoShape::isPrintable() const
{
643 644 645 646
    if (d->visible)
        return d->printable;
    else
        return false;
647 648
}

649 650
void KoShape::setSelectable(bool selectable)
{
Thomas Zander's avatar
Thomas Zander committed
651 652 653
    d->selectable = selectable;
}

654 655
bool KoShape::isSelectable() const
{
Thomas Zander's avatar
Thomas Zander committed
656 657 658
    return d->selectable;
}

659
void KoShape::setGeometryProtected(bool on)
660
{
661
    d->geometryProtected = on;
Thomas Zander's avatar
Thomas Zander committed
662 663
}

664
bool KoShape::isGeometryProtected() const
665
{
666
    return d->geometryProtected;
Thomas Zander's avatar
Thomas Zander committed
667 668
}

669 670 671 672 673 674 675 676 677 678
void KoShape::setContentProtected(bool protect)
{
    d->protectContent = protect;
}

bool KoShape::isContentProtected() const
{
    return d->protectContent;
}

679 680
KoShapeContainer *KoShape::parent() const
{
Thomas Zander's avatar
Thomas Zander committed
681 682 683
    return d->parent;
}

684 685
void KoShape::setKeepAspectRatio(bool keepAspect)
{
Thomas Zander's avatar
Thomas Zander committed
686 687 688
    d->keepAspect = keepAspect;
}

689 690
bool KoShape::keepAspectRatio() const
{
Thomas Zander's avatar
Thomas Zander committed
691 692 693
    return d->keepAspect;
}

694 695
const QString &KoShape::shapeId() const
{
Thomas Zander's avatar
Thomas Zander committed
696 697 698
    return d->shapeId;
}

699 700
void KoShape::setShapeId(const QString &id)
{
Thomas Zander's avatar
Thomas Zander committed
701 702 703
    d->shapeId = id;
}

704 705
void KoShape::setCollisionDetection(bool detect)
{
Thomas Zander's avatar
Thomas Zander committed
706 707 708
    d->detectCollision = detect;
}

709 710
bool KoShape::collisionDetection()
{
Thomas Zander's avatar
Thomas Zander committed
711 712 713
    return d->detectCollision;
}

714
void KoShape::addShapeManager(KoShapeManager * manager)
715
{
716
    d->shapeManagers.insert(manager);
Thomas Zander's avatar
Thomas Zander committed
717 718
}

719
void KoShape::removeShapeManager(KoShapeManager * manager)
720
{
721
    d->shapeManagers.remove(manager);
Thomas Zander's avatar
Thomas Zander committed
722 723
}

724 725
KoShapeBorderModel *KoShape::border() const
{
726 727 728
    return d->border;
}

729 730 731
void KoShape::setBorder(KoShapeBorderModel *border)
{
    if (d->border)
732
        d->border->removeUser();
733
    d->border = border;
734
    if (d->border)
735
        d->border->addUser();
736 737
    d->shapeChanged(BorderChanged);
    notifyChanged();
738 739
}

740
void KoShape::setShadow(KoShapeShadow * shadow)
741
{
742
    if (d->shadow)
743 744
        d->shadow->removeUser();
    d->shadow = shadow;
745
    if (d->shadow)
746 747 748 749 750 751 752 753 754 755
        d->shadow->addUser();
    d->shapeChanged(ShadowChanged);
    notifyChanged();
}

KoShapeShadow * KoShape::shadow() const
{
    return d->shadow;
}

756 757
const QMatrix& KoShape::matrix() const
{
758
    return d->localMatrix;
759 760
}

761
void KoShape::removeConnectionPoint(int index)
762
{
763 764
    if (index < d->connectors.count())
        d->connectors.remove(index);
765 766
}

767 768
QString KoShape::name() const
{
769 770 771
    return d->name;
}

772
void KoShape::setName(const QString & name)
773
{
774 775
    d->name = name;
}
776

777 778
void KoShape::deleteLater()
{
779
    foreach(KoShapeManager *manager, d->shapeManagers)
Jan Hambrecht's avatar
Jan Hambrecht committed
780
        manager->remove(this);
781 782 783 784
    d->shapeManagers.clear();
    new ShapeDeleter(this);
}

785 786
bool KoShape::isEditable() const
{
787
    if (!d->visible || d->geometryProtected)
788 789
        return false;

790
    if (d->parent && d->parent->isChildLocked(this))
791 792 793 794
        return false;

    return true;
}
795

796
// loading & saving methods
797
QString KoShape::saveStyle(KoGenStyle &style, KoShapeSavingContext &context) const
798 799 800
{
    // and fill the style
    KoShapeBorderModel * b = border();
801 802
    if (b) {
        b->fillStyle(style, context);
803
    }
804 805 806
    else {
        style.addProperty( "draw:stroke", "none" );
    }
807
    KoShapeShadow * s = shadow();
808 809
    if (s)
        s->fillStyle(style, context);
810

811
    KoShapeBackground * bg = background();
812
    if (bg) {
813
        bg->fillStyle(style, context);
814 815 816 817
    }
    else {
        style.addProperty( "draw:fill", "none" );
    }
818

819 820
    if (context.isSet(KoShapeSavingContext::AutoStyleInStyleXml)) {
        style.setAutoStyleInStylesDotXml(true);
821
    }
822

823
    QString value;
824
    if (isGeometryProtected())
825 826 827 828 829 830 831 832 833
        value = "position size";
    if (isContentProtected()) {
        if (! value.isEmpty())
            value += ' ';
        value += "content";
    }
    if (!value.isEmpty())
        d->additionalStyleAttributes.insert("style:protect", value);

834 835 836 837 838
    QMap<QByteArray, QString>::const_iterator it(d->additionalStyleAttributes.constBegin());
    for (; it != d->additionalStyleAttributes.constEnd(); ++it) {
        style.addProperty(it.key(), it.value());
    }

839
    return context.mainStyles().lookup(style, context.isSet(KoShapeSavingContext::PresentationShape) ? "pr" : "gr");
840 841
}

842
void KoShape::loadStyle(const KoXmlElement & element, KoShapeLoadingContext &context)
843
{
844
    KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
845 846 847
    styleStack.save();

    // fill the style stack with the shapes style
848 849 850 851 852 853
    if (element.hasAttributeNS(KoXmlNS::draw, "style-name")) {
        context.odfLoadingContext().fillStyleStack(element, KoXmlNS::draw, "style-name", "graphic");
        styleStack.setTypeProperties("graphic");
    } else if (element.hasAttributeNS(KoXmlNS::presentation, "style-name")) {
        context.odfLoadingContext().fillStyleStack(element, KoXmlNS::presentation, "style-name", "presentation");
        styleStack.setTypeProperties("graphic");
854 855
    }

856 857 858
    setBackground(loadOdfFill(element, context));
    setBorder(loadOdfStroke(element, context));
    setShadow(loadOdfShadow(element, context));
859 860

    styleStack.restore();
861 862
}

863
bool KoShape::loadOdfAttributes(const KoXmlElement & element, KoShapeLoadingContext &context, int attributes)
864
{
865 866 867 868 869 870 871
    if (attributes & OdfPosition) {
        QPointF pos(position());
        if (element.hasAttributeNS(KoXmlNS::svg, "x"))
            pos.setX(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "x", QString())));
        if (element.hasAttributeNS(KoXmlNS::svg, "y"))
            pos.setY(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "y", QString())));
        setPosition(pos);
872
    }
873

874 875 876 877 878 879 880
    if (attributes & OdfSize) {
        QSizeF s(size());
        if (element.hasAttributeNS(KoXmlNS::svg, "width"))
            s.setWidth(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "width", QString())));
        if (element.hasAttributeNS(KoXmlNS::svg, "height"))
            s.setHeight(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "height", QString())