KoShape.cpp 35.6 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-2007 Thomas Zander <zander@kde.org>
4
   Copyright (C) 2006-2008 Thorsten Zachmann <zachmann@kde.org>
5
   Copyright (C) 2007 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"
Thomas Zander's avatar
Thomas Zander committed
46

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

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

64 65
#include <kdebug.h>

66 67
#include <limits>

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

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

106
    void shapeChanged(ChangeType type) {
107
        if (parent)
108 109
            parent->model()->childChanged(me, type);
        me->shapeChanged(type);
110 111
        foreach( KoShape * shape, dependees )
            shape->notifyShapeChanged( me, type );
112 113
    }

Thomas Zander's avatar
Thomas Zander committed
114 115
    QSizeF size; // size in pt
    QString shapeId;
116
    QString name; ///< the shapes names
Thomas Zander's avatar
Thomas Zander committed
117

118
    QMatrix localMatrix; ///< the shapes local transformation matrix
Thomas Zander's avatar
Thomas Zander committed
119 120 121 122 123 124 125

    QVector<QPointF> connectors; // in pt

    int zIndex;
    KoShapeContainer *parent;

    bool visible;
126
    bool printable;
Thomas Zander's avatar
Thomas Zander committed
127 128 129 130 131 132 133 134
    bool locked;
    bool keepAspect;
    bool selectable;
    bool detectCollision;

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

Thomas Zander's avatar
Thomas Zander committed
144
KoShape::KoShape()
145
    : d(new Private(this))
Thomas Zander's avatar
Thomas Zander committed
146
{
147 148 149 150 151
    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 ) );

152
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
153 154 155 156
}

KoShape::~KoShape()
{
157
    d->shapeChanged( Deleted );
Thomas Zander's avatar
Thomas Zander committed
158
    delete d;
Thomas Zander's avatar
Thomas Zander committed
159 160
}

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

183
void KoShape::setScale( qreal sx, qreal sy )
Thomas Zander's avatar
Thomas Zander committed
184
{
185 186 187 188 189
    QPointF pos = position();
    QMatrix scaleMatrix;
    scaleMatrix.translate( pos.x(), pos.y() );
    scaleMatrix.scale( sx, sy );
    scaleMatrix.translate( -pos.x(), -pos.y() );
190
    d->localMatrix = d->localMatrix * scaleMatrix;
191

192
    notifyChanged();
193
    d->shapeChanged(ScaleChanged);
Thomas Zander's avatar
Thomas Zander committed
194 195
}

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

205
    notifyChanged();
206
    d->shapeChanged(RotationChanged);
Thomas Zander's avatar
Thomas Zander committed
207 208
}

209
void KoShape::setShear( qreal sx, qreal sy )
Thomas Zander's avatar
Thomas Zander committed
210
{
211 212 213 214 215
    QPointF pos = position();
    QMatrix shearMatrix;
    shearMatrix.translate( pos.x(), pos.y() );
    shearMatrix.shear( sx, sy );
    shearMatrix.translate( -pos.x(), -pos.y() );
216
    d->localMatrix = d->localMatrix * shearMatrix;
217

218
    notifyChanged();
219
    d->shapeChanged(ShearChanged);
Thomas Zander's avatar
Thomas Zander committed
220 221
}

222
void KoShape::setSize( const QSizeF &newSize )
Thomas Zander's avatar
Thomas Zander committed
223
{
224
    QSizeF oldSize( size() );
225
    if (oldSize == newSize )
Thomas Zander's avatar
Thomas Zander committed
226
        return;
227

Thomas Zander's avatar
Thomas Zander committed
228
    d->size = newSize;
229

230
    notifyChanged();
231
    d->shapeChanged(SizeChanged);
Thomas Zander's avatar
Thomas Zander committed
232 233
}

234
void KoShape::setPosition( const QPointF &newPosition )
Thomas Zander's avatar
Thomas Zander committed
235
{
236
    QPointF currentPos = position();
237
    if ( newPosition == currentPos )
Thomas Zander's avatar
Thomas Zander committed
238
        return;
239
    QMatrix translateMatrix;
240
    translateMatrix.translate( newPosition.x()-currentPos.x(), newPosition.y()-currentPos.y() );
241 242 243
    d->localMatrix = d->localMatrix * translateMatrix;

    notifyChanged();
244
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
245 246 247 248
}

bool KoShape::hitTest( const QPointF &position ) const
{
249
    if (d->parent && d->parent->childClipped(this) && !d->parent->hitTest(position))
Thomas Zander's avatar
Thomas Zander committed
250 251
        return false;

252
    QPointF point = absoluteTransformation(0).inverted().map( position );
253
    QRectF bb( QPointF(), size() );
254
    if (d->border)
255 256
    {
        KoInsets insets;
257
        d->border->borderInsets(this, insets);
258 259
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
260
    if ( bb.contains( point ) )
261 262 263
        return true;

    // if there is no shadow we can as well just leave
264
    if ( ! d->shadow )
265
        return false;
Thomas Zander's avatar
Thomas Zander committed
266

267 268 269 270 271
    // the shadow has an offset to the shape, so we simply
    // check if the position minus the shadow offset hits the shape
    point = absoluteTransformation(0).inverted().map( position-d->shadow->offset() );

    return bb.contains( point );
Thomas Zander's avatar
Thomas Zander committed
272 273
}

274
QRectF KoShape::boundingRect() const
Thomas Zander's avatar
Thomas Zander committed
275
{
276
    QRectF bb( QPointF(0, 0), size() );
277
    if (d->border) {
278 279 280 281
        KoInsets insets;
        d->border->borderInsets(this, insets);
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
282
    bb = absoluteTransformation(0).mapRect( bb );
283
    if ( d->shadow )
284 285 286 287 288 289
    {
        KoInsets insets;
        d->shadow->insets( this, insets );
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
    return bb;
Thomas Zander's avatar
Thomas Zander committed
290 291
}

292 293
QMatrix KoShape::absoluteTransformation(const KoViewConverter *converter) const
{
Thomas Zander's avatar
Thomas Zander committed
294 295
    QMatrix matrix;
    // apply parents matrix to inherit any transformations done there.
296
    KoShapeContainer * container = d->parent;
297 298
    if ( container ) {
        if ( container->childClipped(this) )
299
            matrix = container->absoluteTransformation(0);
Thomas Zander's avatar
Thomas Zander committed
300
        else {
301 302
            QSizeF containerSize = container->size();
            QPointF containerPos = container->absolutePosition() - QPointF( 0.5*containerSize.width(), 0.5*containerSize.height() );
303
            if (converter)
304
                containerPos = converter->documentToView(containerPos);
305
            matrix.translate( containerPos.x(), containerPos.y() );
Thomas Zander's avatar
Thomas Zander committed
306 307 308
        }
    }

309
    if (converter) {
Jan Hambrecht's avatar
Jan Hambrecht committed
310
        QPointF pos = d->localMatrix.map( QPointF() );
311 312
        QPointF trans = converter->documentToView( pos ) - pos;
        matrix.translate( trans.x(), trans.y() );
Thomas Zander's avatar
Thomas Zander committed
313
    }
314 315

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
316 317
}

318
void KoShape::applyAbsoluteTransformation( const QMatrix &matrix )
319
{
320
    QMatrix globalMatrix = absoluteTransformation(0);
321 322
    // 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
323
    // to be relative to the local coordinate system
324
    QMatrix transformMatrix = globalMatrix * matrix * globalMatrix.inverted();
325 326 327 328 329 330
    applyTransformation( transformMatrix );
}

void KoShape::applyTransformation( const QMatrix &matrix )
{
    d->localMatrix = matrix * d->localMatrix;
331
    notifyChanged();
332
    d->shapeChanged(GenericMatrixChange);
333 334 335 336 337 338
}

void KoShape::setTransformation( const QMatrix &matrix )
{
    d->localMatrix = matrix;
    notifyChanged();
339
    d->shapeChanged(GenericMatrixChange);
340
}
Thomas Zander's avatar
Thomas Zander committed
341

342
QMatrix KoShape::transformation() const
343 344 345 346
{
    return d->localMatrix;
}

347 348
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
{
349
    int diff = s1->zIndex() - s2->zIndex();
350
    if (diff == 0) {
351 352
        KoShape *s = s1->parent();
        while(s) {
353
            if (s == s2) // s1 is a child of s2
354 355 356 357 358
                return false; // children are always on top of their parents.
            s = s->parent();
        }
        s = s2->parent();
        while(s) {
359
            if (s == s1) // s2 is a child of s1
360 361 362 363 364
                return true;
            s = s->parent();
        }
    }
    return diff < 0;
Thomas Zander's avatar
Thomas Zander committed
365 366
}

367 368 369
void KoShape::setParent(KoShapeContainer *parent)
{
    if (d->parent == parent)
370
        return;
371
    if (parent && dynamic_cast<KoShape*>(parent) != this) {
372 373
        if (d->parent)
            d->parent->removeChild(this);
Thomas Zander's avatar
Thomas Zander committed
374
        d->parent = parent;
375 376
        parent->addChild(this);
    }
Thomas Zander's avatar
Thomas Zander committed
377
    else
Thomas Zander's avatar
Thomas Zander committed
378
        d->parent = 0;
379
    notifyChanged();
380
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
381 382
}

383 384 385
int KoShape::zIndex() const
{
    if (parent()) // we can't be under our parent...
Thomas Zander's avatar
Thomas Zander committed
386 387
        return qMax(d->zIndex, parent()->zIndex());
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
388 389
}

390 391
void KoShape::update() const
{
Thomas Zander's avatar
Thomas Zander committed
392
    if ( !d->shapeManagers.empty() )
393
    {
Thorsten Zachmann's avatar
Thorsten Zachmann committed
394
        QRectF rect( boundingRect() );
Thomas Zander's avatar
Thomas Zander committed
395
        foreach( KoShapeManager * manager, d->shapeManagers )
396
            manager->update( rect, this, true );
Thomas Zander's avatar
Thomas Zander committed
397 398 399
    }
}

400 401
void KoShape::update(const QRectF &shape) const
{
Thomas Zander's avatar
Thomas Zander committed
402
    if ( !d->shapeManagers.empty() && isVisible() )
403
    {
404
        QRectF rect(absoluteTransformation(0).mapRect(shape));
Thomas Zander's avatar
Thomas Zander committed
405
        foreach( KoShapeManager * manager, d->shapeManagers )
406
        {
407
            manager->update(rect);
408 409
        }
    }
Thomas Zander's avatar
Thomas Zander committed
410 411
}

412 413
const QPainterPath KoShape::outline() const
{
Thomas Zander's avatar
Thomas Zander committed
414
    QPainterPath path;
415
    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
416 417 418
    return path;
}

419 420
QPointF KoShape::absolutePosition(KoFlake::Position anchor) const
{
421 422 423
    QPointF point;
    switch(anchor) {
        case KoFlake::TopLeftCorner: break;
424 425 426
        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;
Jan Hambrecht's avatar
Jan Hambrecht committed
427
        case KoFlake::CenteredPosition: point = QPointF(size().width() / 2.0, size().height() / 2.0); break;
428
    }
429
    return absoluteTransformation(0).map(point);
Thomas Zander's avatar
Thomas Zander committed
430 431
}

432 433
void KoShape::setAbsolutePosition(QPointF newPosition, KoFlake::Position anchor)
{
434 435
    QPointF currentAbsPosition = absolutePosition( anchor );
    QPointF translate = newPosition - currentAbsPosition;
Jan Hambrecht's avatar
Jan Hambrecht committed
436 437
    QMatrix translateMatrix;
    translateMatrix.translate( translate.x(), translate.y() );
438
    applyAbsoluteTransformation( translateMatrix );
439
    notifyChanged();
440
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
441 442
}

443 444
void KoShape::copySettings(const KoShape *shape)
{
Thomas Zander's avatar
Thomas Zander committed
445 446
    d->size = shape->size();
    d->connectors.clear();
Jan Hambrecht's avatar
Jan Hambrecht committed
447
    foreach(QPointF point, shape->connectionPoints())
Thomas Zander's avatar
Thomas Zander committed
448
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
449 450
    d->zIndex = shape->zIndex();
    d->visible = shape->isVisible();
451 452 453 454 455 456 457
    
    // Ensure printable is true by default
    if (!d->visible)
        d->printable = true;
    else
        d->printable = shape->isPrintable();
        
Thomas Zander's avatar
Thomas Zander committed
458 459
    d->locked = shape->isLocked();
    d->keepAspect = shape->keepAspectRatio();
460
    d->localMatrix = shape->d->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
461 462
}

Thomas Zander's avatar
Thomas Zander committed
463
void KoShape::notifyChanged()
464
{
Thomas Zander's avatar
Thomas Zander committed
465
    foreach( KoShapeManager * manager, d->shapeManagers )
466
    {
Thomas Zander's avatar
Thomas Zander committed
467
        manager->notifyShapeChanged( this );
468 469 470
    }
}

471 472
void KoShape::setUserData(KoShapeUserData *userData)
{
473
    delete d->userData;
Thomas Zander's avatar
Thomas Zander committed
474
    d->userData = userData;
475 476
}

477 478
KoShapeUserData *KoShape::userData() const
{
Thomas Zander's avatar
Thomas Zander committed
479
    return d->userData;
480 481
}

482 483
void KoShape::setApplicationData(KoShapeApplicationData *appData)
{
484
    // appdata is deleted by the application.
Thomas Zander's avatar
Thomas Zander committed
485
    d->appData = appData;
486 487
}

488 489
KoShapeApplicationData *KoShape::applicationData() const
{
Thomas Zander's avatar
Thomas Zander committed
490
    return d->appData;
491 492
}

493 494 495
bool KoShape::hasTransparency()
{
    if ( ! d->fill )
Thomas Zander's avatar
Thomas Zander committed
496
        return true;
497 498
    else
        return d->fill->hasTransparency();
Thomas Zander's avatar
Thomas Zander committed
499 500
}

501 502
KoInsets KoShape::borderInsets() const
{
503
    KoInsets answer;
504
    if (d->border)
505
        d->border->borderInsets(this, answer);
506 507 508
    return answer;
}

509 510
qreal KoShape::rotation() const
{
511
    // try to extract the rotation angle out of the local matrix
512 513 514
    // if it is a pure rotation matrix

    // check if the matrix has shearing mixed in
515
    if ( fabs( fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21()) ) > 1e-10 )
516
        return std::numeric_limits<qreal>::quiet_NaN();
517
    // check if the matrix has scaling mixed in
518
    if ( fabs( d->localMatrix.m11() - d->localMatrix.m22() ) > 1e-10 )
519
        return std::numeric_limits<qreal>::quiet_NaN();
520 521

    // calculate the angle from the matrix elements
522
    qreal angle = atan2( -d->localMatrix.m21(), d->localMatrix.m11() ) * 180.0 / M_PI;
523
    if ( angle < 0.0 )
524 525 526
        angle += 360.0;

    return angle;
Thomas Zander's avatar
Thomas Zander committed
527 528
}

529 530
QSizeF KoShape::size () const
{
Thomas Zander's avatar
Thomas Zander committed
531 532 533
    return d->size;
}

534 535
QPointF KoShape::position() const
{
536 537 538
    QPointF center( 0.5*size().width(), 0.5*size().height() );
    return d->localMatrix.map( center ) - center;
    //return d->localMatrix.map( QPointF(0,0) );
Thomas Zander's avatar
Thomas Zander committed
539 540
}

541 542
void KoShape::addConnectionPoint( const QPointF &point )
{
543 544 545
    QSizeF s = size();
    // convert glue point from shape coordinates to factors of size
    d->connectors.append( QPointF( point.x() / s.width(), point.y() / s.height() ) );
Thomas Zander's avatar
Thomas Zander committed
546 547
}

548 549
QList<QPointF> KoShape::connectionPoints() const
{
550 551 552 553 554 555 556
    QList<QPointF> points;
    QSizeF s = size();
    // convert glue points to shape coordinates
    foreach( QPointF cp, d->connectors )
        points.append( QPointF( s.width() * cp.x(), s.height() * cp.y() ) );

    return points;
557 558
}

559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
void KoShape::addEventAction( KoEventAction * action )
{
    if ( ! d->eventActions.contains( action ) ) {
        d->eventActions.append( action );
    }
}

void KoShape::removeEventAction( KoEventAction * action )
{
    if ( d->eventActions.contains( action ) ) {
        d->eventActions.removeAll( action );
    }
}

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

578 579 580
void KoShape::setBackground( KoShapeBackground * fill )
{
    if (d->fill)
581 582
        d->fill->removeUser();
    d->fill = fill;
583
    if (d->fill)
584
        d->fill->addUser();
585 586
    d->shapeChanged(BackgroundChanged);
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
587 588
}

589 590
KoShapeBackground * KoShape::background() const
{
591
    return d->fill;
Thomas Zander's avatar
Thomas Zander committed
592 593
}

594 595
void KoShape::setZIndex(int zIndex)
{
596
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
597 598 599
    d->zIndex = zIndex;
}

600 601
void KoShape::setVisible(bool on)
{
Thomas Zander's avatar
Thomas Zander committed
602 603 604
    d->visible = on;
}

605 606 607
bool KoShape::isVisible( bool recursive ) const
{
    if ( ! recursive )
608
        return d->visible;
609
    if ( recursive && ! d->visible )
Jan Hambrecht's avatar
Jan Hambrecht committed
610 611
        return false;

612 613 614
    KoShapeContainer * parentShape = parent();
    while( parentShape )
    {
615
        if ( ! parentShape->isVisible() )
616 617 618 619
            return false;
        parentShape = parentShape->parent();
    }
    return true;
Thomas Zander's avatar
Thomas Zander committed
620 621
}

622 623 624 625
void KoShape::setPrintable(bool on)
{
    d->printable = on;
}
626

627 628
bool KoShape::isPrintable() const
{
629 630 631 632
    if (d->visible)
        return d->printable;
    else
        return false;
633 634
}

635 636
void KoShape::setSelectable(bool selectable)
{
Thomas Zander's avatar
Thomas Zander committed
637 638 639
    d->selectable = selectable;
}

640 641
bool KoShape::isSelectable() const
{
Thomas Zander's avatar
Thomas Zander committed
642 643 644
    return d->selectable;
}

645 646
void KoShape::setLocked(bool locked)
{
Thomas Zander's avatar
Thomas Zander committed
647 648 649
    d->locked = locked;
}

650 651
bool KoShape::isLocked() const
{
Thomas Zander's avatar
Thomas Zander committed
652 653 654
    return d->locked;
}

655 656
KoShapeContainer *KoShape::parent() const
{
Thomas Zander's avatar
Thomas Zander committed
657 658 659
    return d->parent;
}

660 661
void KoShape::setKeepAspectRatio(bool keepAspect)
{
Thomas Zander's avatar
Thomas Zander committed
662 663 664
    d->keepAspect = keepAspect;
}

665 666
bool KoShape::keepAspectRatio() const
{
Thomas Zander's avatar
Thomas Zander committed
667 668 669
    return d->keepAspect;
}

670 671
const QString &KoShape::shapeId() const
{
Thomas Zander's avatar
Thomas Zander committed
672 673 674
    return d->shapeId;
}

675 676
void KoShape::setShapeId(const QString &id)
{
Thomas Zander's avatar
Thomas Zander committed
677 678 679
    d->shapeId = id;
}

680 681
void KoShape::setCollisionDetection(bool detect)
{
Thomas Zander's avatar
Thomas Zander committed
682 683 684
    d->detectCollision = detect;
}

685 686
bool KoShape::collisionDetection()
{
Thomas Zander's avatar
Thomas Zander committed
687 688 689
    return d->detectCollision;
}

690 691
void KoShape::addShapeManager( KoShapeManager * manager )
{
Thomas Zander's avatar
Thomas Zander committed
692 693 694
    d->shapeManagers.insert( manager );
}

695 696
void KoShape::removeShapeManager( KoShapeManager * manager )
{
Thomas Zander's avatar
Thomas Zander committed
697 698 699
    d->shapeManagers.remove( manager );
}

700 701
KoShapeBorderModel *KoShape::border() const
{
702 703 704
    return d->border;
}

705 706 707
void KoShape::setBorder(KoShapeBorderModel *border)
{
    if (d->border)
708
        d->border->removeUser();
709
    d->border = border;
710
    if (d->border)
711
        d->border->addUser();
712 713
    d->shapeChanged(BorderChanged);
    notifyChanged();
714 715
}

716 717
void KoShape::setShadow( KoShapeShadow * shadow )
{
718
    if ( d->shadow )
719 720
        d->shadow->removeUser();
    d->shadow = shadow;
721
    if ( d->shadow )
722 723 724 725 726 727 728 729 730 731
        d->shadow->addUser();
    d->shapeChanged(ShadowChanged);
    notifyChanged();
}

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

732 733
const QMatrix& KoShape::matrix() const
{
734
    return d->localMatrix;
735 736
}

737
void KoShape::removeConnectionPoint( int index )
738
{
739
    if ( index < d->connectors.count() )
740 741 742
        d->connectors.remove( index );
}

743 744
QString KoShape::name() const
{
745 746 747
    return d->name;
}

748 749
void KoShape::setName( const QString & name )
{
750 751
    d->name = name;
}
752

753 754
void KoShape::deleteLater()
{
755 756 757 758 759 760
    foreach(KoShapeManager *manager, d->shapeManagers)
        manager->remove(this);
    d->shapeManagers.clear();
    new ShapeDeleter(this);
}

761 762 763
bool KoShape::isEditable() const
{
    if ( !d->visible || d->locked )
764 765 766
        return false;

    KoShapeContainer * p = parent();
767
    if (p && p->isChildLocked(this))
768 769 770
        return false;
    while( p )
    {
771
        if ( ! p->isVisible() )
772 773 774 775 776 777
            return false;
        p = p->parent();
    }

    return true;
}
778

779
// loading & saving methods
780
QString KoShape::saveStyle( KoGenStyle &style, KoShapeSavingContext &context ) const
781 782 783 784 785 786 787
{
    // and fill the style
    KoShapeBorderModel * b = border();
    if ( b )
    {
        b->fillStyle( style, context );
    }
788
    KoShapeShadow * s = shadow();
789
    if ( s )
790
        s->fillStyle( style, context );
791

792
    KoShapeBackground * bg = background();
793
    if ( bg )
794 795 796 797 798
        bg->fillStyle( style, context );

    if ( context.isSet( KoShapeSavingContext::AutoStyleInStyleXml ) ) {
        style.setAutoStyleInStylesDotXml( true );
    }
799

800
    return context.mainStyles().lookup( style, context.isSet( KoShapeSavingContext::PresentationShape ) ? "pr" : "gr" );
801 802
}

803 804
void KoShape::loadStyle( const KoXmlElement & element, KoShapeLoadingContext &context )
{
805
    KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
806 807 808
    styleStack.save();

    // fill the style stack with the shapes style
809
    if ( element.hasAttributeNS( KoXmlNS::draw, "style-name" ) )
810
    {
811
        context.odfLoadingContext().fillStyleStack( element, KoXmlNS::draw, "style-name", "graphic" );
812 813
        styleStack.setTypeProperties( "graphic" );
    }
814
    else if ( element.hasAttributeNS( KoXmlNS::presentation, "style-name" ) )
815
    {
816
        context.odfLoadingContext().fillStyleStack( element, KoXmlNS::presentation, "style-name", "presentation" );
817
        styleStack.setTypeProperties( "graphic" );
818 819
    }

820 821
    setBackground( loadOdfFill( element, context ) );
    setBorder( loadOdfStroke( element, context ) );
822
    setShadow( loadOdfShadow( element, context ) );
823 824

    styleStack.restore();
825 826
}

827 828
bool KoShape::loadOdfAttributes( const KoXmlElement & element, KoShapeLoadingContext &context, int attributes )
{
829 830 831 832 833 834
    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() ) ) );
835
        setPosition( pos );
836
    }
837

838 839 840 841 842 843 844
    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() ) ) );
        setSize( s );
845 846
    }

847
    if ( attributes & OdfLayer ) {
848 849 850 851 852 853
        if ( element.hasAttributeNS( KoXmlNS::draw, "layer" ) ) {
            KoShapeLayer * layer = context.layer( element.attributeNS( KoXmlNS::draw, "layer" ) );
            if ( layer ) {
                setParent( layer );
            }
        }
854 855 856
    }

    if ( attributes & OdfId ) {
857 858 859 860 861 862
        if ( element.hasAttributeNS( KoXmlNS::draw, "id" ) ) {
            QString id = element.attributeNS( KoXmlNS::draw, "id" );
            if ( !id.isNull() ) {
                context.addShapeId( this, id );
            }
        }
863 864 865
    }

    if ( attributes & OdfZIndex ) {
866
        if ( element.hasAttributeNS( KoXmlNS::draw, "z-index" ) ) {
867
            context.addShapeZIndex( this, element.attributeNS( KoXmlNS::draw, "z-index" ).toInt() );
868
        }
869
        setZIndex( context.zIndex() );
870 871 872
    }

    if ( attributes & OdfName ) {
873

874
        if ( element.hasAttributeNS( KoXmlNS::draw, "name" ) ) {
Jan Hambrecht's avatar
Jan Hambrecht committed
875
            setName( element.attributeNS( KoXmlNS::draw, "name" ) );
876
        }
877
    }
Jan Hambrecht's avatar
Jan Hambrecht committed
878

879
    if ( attributes & OdfStyle ) {
880
        loadStyle( element, context );
881
    }
882

883
    if ( attributes & OdfTransformation )
Jan Hambrecht's avatar
 
Jan Hambrecht committed
884 885
    {
        QString transform = element.attributeNS( KoXmlNS::draw, "transform", QString() );
886
        if ( ! transform.isEmpty() )
887
            applyAbsoluteTransformation( parseOdfTransform( transform ) );
Jan Hambrecht's avatar
 
Jan Hambrecht committed
888 889
    }

890
    if ( attributes & OdfAdditionalAttributes ) {
891 892 893 894 895
        QSet<KoShapeLoadingContext::AdditionalAttributeData> additionalAttributeData = KoShapeLoadingContext::additionalAttributeData();
        foreach ( KoShapeLoadingContext::AdditionalAttributeData attributeData, additionalAttributeData ) {
            if ( element.hasAttributeNS( attributeData.ns, attributeData.tag ) ) {
                QString value = element.attributeNS( attributeData.ns, attributeData.tag );
                //kDebug(30006) << "load additional attribute" << attributeData.tag << value;
896
                setAdditionalAttribute( attributeData.name, value );
897 898
            }
        }
899 900
    }

Thorsten Zachmann's avatar
Thorsten Zachmann committed
901 902 903 904 905 906 907 908
    if ( attributes & OdfCommonChildElements ) {
        const KoXmlElement eventActionsElement( KoXml::namedItemNS( element, KoXmlNS::office, "event-listeners" ) );
        if ( !eventActionsElement.isNull() ) {
            d->eventActions = KoEventActionRegistry::instance()->createEventActionsFromOdf( eventActionsElement, context );
        }
        // load glue points (connection points)
    }

909 910 911
    return true;
}

912
QString KoShape::getStyleProperty( const char *property, const KoXmlElement & element, KoShapeLoadingContext & context )
913
{
Jan Hambrecht's avatar
Jan Hambrecht committed
914
    Q_UNUSED(element);
915
    KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
916
    QString value;
917

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

922 923 924