KoShape.cpp 27.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>
3
   Copyright (C) 2006-2007 Thomas Zander <zander@kde.org>
4
   Copyright (C) 2006-2007 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
#include "KoShapeManager.h"
32
#include "KoShapeUserData.h"
33
#include "KoShapeApplicationData.h"
34
#include "KoShapeSavingContext.h"
35
#include "KoShapeLoadingContext.h"
36
#include "KoViewConverter.h"
Thomas Zander's avatar
Thomas Zander committed
37

38
#include <KoXmlReader.h>
39
#include <KoXmlWriter.h>
40
#include <KoXmlNS.h>
41 42
#include <KoGenStyles.h>
#include <KoGenStyle.h>
43
#include <KoUnit.h>
44
#include <KoOasisStyles.h>
45
#include <KoOasisLoadingContext.h>
46

Thomas Zander's avatar
Thomas Zander committed
47
#include <QPainter>
48
#include <QVariant>
Thomas Zander's avatar
Thomas Zander committed
49
#include <QPainterPath>
50
#include <QList>
Thomas Zander's avatar
Thomas Zander committed
51

52 53
#include <kdebug.h>

54
class KoShape::Private {
Thomas Zander's avatar
Thomas Zander committed
55
public:
56
    Private(KoShape *shape)
57
        : size( 50, 50 ),
Thomas Zander's avatar
Thomas Zander committed
58 59 60 61 62 63 64 65 66
        pos( 0, 0 ),
        zIndex( 0 ),
        parent( 0 ),
        visible( true ),
        locked( false ),
        keepAspect( false ),
        selectable( true ),
        detectCollision( false ),
        userData(0),
67 68
        appData(0),
        backgroundBrush(Qt::NoBrush),
69 70
        border(0),
        me(shape)
Thomas Zander's avatar
Thomas Zander committed
71 72 73
    {
    }

74
    ~Private() {
75 76
        foreach(KoShapeManager *manager, shapeManagers)
            manager->remove(me);
Thomas Zander's avatar
Thomas Zander committed
77 78 79 80
        delete userData;
        delete appData;
    }

81 82 83 84 85 86
    void shapeChanged(ChangeType type) {
        if(parent)
            parent->model()->childChanged(me, type);
        me->shapeChanged(type);
    }

Thomas Zander's avatar
Thomas Zander committed
87 88 89
    QSizeF size; // size in pt
    QPointF pos; // position (top left) in pt
    QString shapeId;
90
    QString name; ///< the shapes names
Thomas Zander's avatar
Thomas Zander committed
91

92
    QMatrix localMatrix; ///< the shapes local transformation matrix
Thomas Zander's avatar
Thomas Zander committed
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107

    QVector<QPointF> connectors; // in pt

    int zIndex;
    KoShapeContainer *parent;

    bool visible;
    bool locked;
    bool keepAspect;
    bool selectable;
    bool detectCollision;

    QSet<KoShapeManager *> shapeManagers;
    KoShapeUserData *userData;
    KoShapeApplicationData *appData;
108 109
    QBrush backgroundBrush; ///< Stands for the background color / fill etc.
    KoShapeBorderModel *border; ///< points to a border, or 0 if there is no border
110
    QList<KoShapeConnection*> connections;
111
    KoShape *me;
Thomas Zander's avatar
Thomas Zander committed
112 113
};

Thomas Zander's avatar
Thomas Zander committed
114
KoShape::KoShape()
115
    : d(new Private(this))
Thomas Zander's avatar
Thomas Zander committed
116
{
117
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
118 119 120 121
}

KoShape::~KoShape()
{
Thomas Zander's avatar
Thomas Zander committed
122
    delete d;
Thomas Zander's avatar
Thomas Zander committed
123 124
}

125
void KoShape::paintDecorations(QPainter &painter, const KoViewConverter &converter, const KoCanvasBase *canvas) {
Thomas Zander's avatar
Thomas Zander committed
126 127 128
    Q_UNUSED(painter);
    Q_UNUSED(converter);
    Q_UNUSED(canvas);
129
/* 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
130 131 132 133 134 135 136
    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
137
        for ( int i = 0; i < d->connectors.size(); ++i )
Thomas Zander's avatar
Thomas Zander committed
138
        {
Thomas Zander's avatar
Thomas Zander committed
139
            QPointF p = converter.documentToView(d->connectors[ i ]);
Thomas Zander's avatar
Thomas Zander committed
140 141 142
            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 ) );
        }
143
    }*/
Thomas Zander's avatar
Thomas Zander committed
144 145 146 147
}

void KoShape::scale( double sx, double sy )
{
148 149 150 151 152 153 154
    QPointF pos = position();
    QMatrix scaleMatrix;
    scaleMatrix.translate( pos.x(), pos.y() );
    scaleMatrix.scale( sx, sy );
    scaleMatrix.translate( -pos.x(), -pos.y() );
    applyTransformation( scaleMatrix );

155
    notifyChanged();
156
    d->shapeChanged(ScaleChanged);
Thomas Zander's avatar
Thomas Zander committed
157 158 159 160
}

void KoShape::rotate( double angle )
{
161 162 163 164 165 166 167
    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() );
    d->localMatrix = rotateMatrix * d->localMatrix;

168
    notifyChanged();
169
    d->shapeChanged(RotationChanged);
Thomas Zander's avatar
Thomas Zander committed
170 171 172 173
}

void KoShape::shear( double sx, double sy )
{
174 175 176 177 178 179 180
    QPointF pos = position();
    QMatrix shearMatrix;
    shearMatrix.translate( pos.x(), pos.y() );
    shearMatrix.shear( sx, sy );
    shearMatrix.translate( -pos.x(), -pos.y() );
    d->localMatrix = shearMatrix * d->localMatrix;

181
    notifyChanged();
182
    d->shapeChanged(ShearChanged);
Thomas Zander's avatar
Thomas Zander committed
183 184
}

185
void KoShape::resize( const QSizeF &newSize )
Thomas Zander's avatar
Thomas Zander committed
186
{
187 188
    QSizeF s( size() );
    if(s == newSize)
Thomas Zander's avatar
Thomas Zander committed
189
        return;
190

191 192
    double fx = newSize.width() / s.width();
    double fy = newSize.height() / s.height();
193

Thomas Zander's avatar
Thomas Zander committed
194
    d->size = newSize;
195

Thomas Zander's avatar
Thomas Zander committed
196
    for ( int i = 0; i < d->connectors.size(); ++i )
197
    {
Thomas Zander's avatar
Thomas Zander committed
198
        QPointF &point = d->connectors[i];
199 200 201
        point.setX(point.x() * fx);
        point.setY(point.y() * fy);
    }
202
    notifyChanged();
203
    d->shapeChanged(SizeChanged);
Thomas Zander's avatar
Thomas Zander committed
204 205 206 207
}

void KoShape::setPosition( const QPointF &position )
{
208 209
    QPointF currentPos = d->localMatrix.map( QPointF(0,0) );
    if( position == currentPos )
Thomas Zander's avatar
Thomas Zander committed
210
        return;
211 212 213 214 215
    QMatrix translateMatrix;
    translateMatrix.translate( position.x()-currentPos.x(), position.y()-currentPos.y() );
    d->localMatrix = d->localMatrix * translateMatrix;

    notifyChanged();
216
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
217 218 219 220
}

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

224
    QPointF point = transformationMatrix(0).inverted().map( position );
225
    KoInsets insets(0, 0, 0, 0);
226 227
    if(d->border)
        d->border->borderInsets(this, insets);
Thomas Zander's avatar
Thomas Zander committed
228

229 230
    QSizeF s( size() );
    return point.x() >= -insets.left && point.x() <= s.width() + insets.right &&
231
           point.y() >= -insets.top && point.y() <= s.height() + insets.bottom;
Thomas Zander's avatar
Thomas Zander committed
232 233
}

234
QRectF KoShape::boundingRect() const
Thomas Zander's avatar
Thomas Zander committed
235
{
236
    QRectF bb( QPointF(0, 0), size() );
237 238 239 240 241
    if(d->border) {
        KoInsets insets;
        d->border->borderInsets(this, insets);
        bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
    }
242
    return transformationMatrix(0).mapRect( bb );
Thomas Zander's avatar
Thomas Zander committed
243 244
}

245
QMatrix KoShape::transformationMatrix(const KoViewConverter *converter) const {
Thomas Zander's avatar
Thomas Zander committed
246 247
    QMatrix matrix;
    // apply parents matrix to inherit any transformations done there.
248 249 250 251
    KoShapeContainer * container = d->parent;
    if( container ) {
        if( container->childClipped(this) )
            matrix = container->transformationMatrix(0);
Thomas Zander's avatar
Thomas Zander committed
252
        else {
253 254
            QSizeF containerSize = container->size();
            QPointF containerPos = container->absolutePosition() - QPointF( 0.5*containerSize.width(), 0.5*containerSize.height() );
Thomas Zander's avatar
Thomas Zander committed
255
            if(converter)
256
                containerPos = converter->documentToView(containerPos);
257
            matrix.translate( containerPos.x(), containerPos.y() );
Thomas Zander's avatar
Thomas Zander committed
258 259 260
        }
    }

261 262 263 264
    if(converter) {
        QPointF pos = position();
        QPointF trans = converter->documentToView( pos ) - pos;
        matrix.translate( trans.x(), trans.y() );
Thomas Zander's avatar
Thomas Zander committed
265
    }
266 267

    return d->localMatrix * matrix;
Thomas Zander's avatar
Thomas Zander committed
268 269
}

270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
void KoShape::applyTransformation( const QMatrix &matrix )
{
    QMatrix globalMatrix = transformationMatrix(0);
    // the transformation is relative to the global coordinate system
    // but we want to change the local matrix, so convert the matrix
    // to relate to the local coordinate system
    QMatrix transformMatrix = globalMatrix * matrix * globalMatrix.inverted();
    d->localMatrix = transformMatrix * d->localMatrix;
    notifyChanged();
}

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

287
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2) {
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
    int diff = s1->zIndex() - s2->zIndex();
    if(diff == 0) {
        KoShape *s = s1->parent();
        while(s) {
            if(s == s2) // s1 is a child of s2
                return false; // children are always on top of their parents.
            s = s->parent();
        }
        s = s2->parent();
        while(s) {
            if(s == s1) // s2 is a child of s1
                return true;
            s = s->parent();
        }
    }
    return diff < 0;
Thomas Zander's avatar
Thomas Zander committed
304 305 306
}

void KoShape::setParent(KoShapeContainer *parent) {
307 308 309
    if(d->parent == parent)
        return;
    if(parent && dynamic_cast<KoShape*>(parent) != this) {
Thomas Zander's avatar
Thomas Zander committed
310
        d->parent = parent;
311 312
        parent->addChild(this);
    }
Thomas Zander's avatar
Thomas Zander committed
313
    else
Thomas Zander's avatar
Thomas Zander committed
314
        d->parent = 0;
315
    notifyChanged();
316
    d->shapeChanged(ParentChanged);
Thomas Zander's avatar
Thomas Zander committed
317 318 319 320
}

int KoShape::zIndex() const {
    if(parent()) // we can't be under our parent...
Thomas Zander's avatar
Thomas Zander committed
321 322
        return qMax(d->zIndex, parent()->zIndex());
    return d->zIndex;
Thomas Zander's avatar
Thomas Zander committed
323 324 325
}

void KoShape::repaint() const {
Thomas Zander's avatar
Thomas Zander committed
326
    if ( !d->shapeManagers.empty() )
327
    {
328 329
        QRectF rect(QPointF(0, 0), size() );
        if(d->border) {
330
            KoInsets insets;
331 332 333
            d->border->borderInsets(this, insets);
            rect.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
        }
334
        rect = transformationMatrix(0).mapRect(rect);
Thomas Zander's avatar
Thomas Zander committed
335
        foreach( KoShapeManager * manager, d->shapeManagers )
336
            manager->repaint( rect, this, true );
Thomas Zander's avatar
Thomas Zander committed
337 338 339
    }
}

340
void KoShape::repaint(const QRectF &shape) const {
Thomas Zander's avatar
Thomas Zander committed
341
    if ( !d->shapeManagers.empty() && isVisible() )
342
    {
343
        QRectF rect(transformationMatrix(0).mapRect(shape));
Thomas Zander's avatar
Thomas Zander committed
344
        foreach( KoShapeManager * manager, d->shapeManagers )
345 346 347 348
        {
            manager->repaint(rect);
        }
    }
Thomas Zander's avatar
Thomas Zander committed
349 350 351 352
}

const QPainterPath KoShape::outline() const {
    QPainterPath path;
353
    path.addRect(QRectF( QPointF(0, 0), size() ));
Thomas Zander's avatar
Thomas Zander committed
354 355 356
    return path;
}

357 358 359 360
QPointF KoShape::absolutePosition(KoFlake::Position anchor) const {
    QPointF point;
    switch(anchor) {
        case KoFlake::TopLeftCorner: break;
361 362 363 364
        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::CenteredPositon: point = QPointF(size().width() / 2.0, size().height() / 2.0); break;
365
    }
366
    return transformationMatrix(0).map(point);
Thomas Zander's avatar
Thomas Zander committed
367 368
}

369
void KoShape::setAbsolutePosition(QPointF newPosition, KoFlake::Position anchor) {
370 371
    QPointF currentAbsPosition = absolutePosition( anchor );
    QPointF translate = newPosition - currentAbsPosition;
Jan Hambrecht's avatar
Jan Hambrecht committed
372 373 374
    QMatrix translateMatrix;
    translateMatrix.translate( translate.x(), translate.y() );
    d->localMatrix = d->localMatrix * translateMatrix;
375
    notifyChanged();
376
    d->shapeChanged(PositionChanged);
Thomas Zander's avatar
Thomas Zander committed
377 378
}

Thomas Zander's avatar
Thomas Zander committed
379
void KoShape::copySettings(const KoShape *shape) {
Thomas Zander's avatar
Thomas Zander committed
380 381 382
    d->pos = shape->position();
    d->size = shape->size();
    d->connectors.clear();
Thomas Zander's avatar
Thomas Zander committed
383 384
    foreach(QPointF point, shape->connectors())
        addConnectionPoint(point);
Thomas Zander's avatar
Thomas Zander committed
385 386 387 388
    d->zIndex = shape->zIndex();
    d->visible = shape->isVisible();
    d->locked = shape->isLocked();
    d->keepAspect = shape->keepAspectRatio();
389
    d->localMatrix = shape->d->localMatrix;
Thomas Zander's avatar
Thomas Zander committed
390 391
}

Thomas Zander's avatar
Thomas Zander committed
392 393 394
void KoShape::moveBy(double distanceX, double distanceY) {
    QPointF p = absolutePosition();
    setAbsolutePosition(QPointF(p.x() + distanceX, p.y() + distanceY));
Thomas Zander's avatar
Thomas Zander committed
395 396
}

Thomas Zander's avatar
Thomas Zander committed
397
void KoShape::notifyChanged()
398
{
Thomas Zander's avatar
Thomas Zander committed
399
    foreach( KoShapeManager * manager, d->shapeManagers )
400
    {
Thomas Zander's avatar
Thomas Zander committed
401
        manager->notifyShapeChanged( this );
402 403 404
    }
}

405
void KoShape::setUserData(KoShapeUserData *userData) {
Thomas Zander's avatar
Thomas Zander committed
406 407 408
    if(d->userData)
        delete d->userData;
    d->userData = userData;
409 410 411
}

KoShapeUserData *KoShape::userData() const {
Thomas Zander's avatar
Thomas Zander committed
412
    return d->userData;
413 414
}

415
void KoShape::setApplicationData(KoShapeApplicationData *appData) {
Thomas Zander's avatar
Thomas Zander committed
416 417
    delete d->appData;
    d->appData = appData;
418 419 420
}

KoShapeApplicationData *KoShape::applicationData() const {
Thomas Zander's avatar
Thomas Zander committed
421
    return d->appData;
422 423
}

424
bool KoShape::hasTransparency() {
425
    if(d->backgroundBrush.style() == Qt::NoBrush)
Thomas Zander's avatar
Thomas Zander committed
426
        return true;
427
    return !d->backgroundBrush.isOpaque();
Thomas Zander's avatar
Thomas Zander committed
428 429
}

430 431
KoInsets KoShape::borderInsets() const {
    KoInsets answer;
432 433
    if(d->border)
        d->border->borderInsets(this, answer);
434 435 436
    return answer;
}

Thomas Zander's avatar
Thomas Zander committed
437
double KoShape::scaleX() const {
438
    return 0.0;
Thomas Zander's avatar
Thomas Zander committed
439 440
}
double KoShape::scaleY() const {
441
    return 0.0;
Thomas Zander's avatar
Thomas Zander committed
442 443 444
}

double KoShape::rotation() const {
445 446 447 448 449
    // try to extract the rotation angle out of the local matrix 
    // if it is a pure rotation matrix

    // check if the matrix has shearing mixed in
    if( fabs( fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21()) ) > 1e-10 )
Jarosław Staniek's avatar
Jarosław Staniek committed
450
        return NAN;
451 452
    // check if the matrix has scaling mixed in
    if( fabs( d->localMatrix.m11() - d->localMatrix.m22() ) > 1e-10 )
Jarosław Staniek's avatar
Jarosław Staniek committed
453
        return NAN;
454 455 456 457 458 459 460

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

    return angle;
Thomas Zander's avatar
Thomas Zander committed
461 462 463
}

double KoShape::shearX() const {
464
    return 0.0;
Thomas Zander's avatar
Thomas Zander committed
465 466 467
}

double KoShape::shearY() const {
468
    return 0.0;
Thomas Zander's avatar
Thomas Zander committed
469 470 471 472 473 474 475
}

QSizeF KoShape::size () const {
    return d->size;
}

QPointF KoShape::position() const {
476
    return d->localMatrix.map( QPointF(0,0) );
Thomas Zander's avatar
Thomas Zander committed
477 478 479 480 481 482 483 484 485 486 487
}

void KoShape::addConnectionPoint( const QPointF &point ) {
    d->connectors.append( point );
}

QList<QPointF> KoShape::connectors() const {
    return d->connectors.toList();
}

void KoShape::setBackground ( const QBrush & brush ) {
488
    d->backgroundBrush = brush;
Thomas Zander's avatar
Thomas Zander committed
489 490
}

491
QBrush KoShape::background() const {
492
    return d->backgroundBrush;
Thomas Zander's avatar
Thomas Zander committed
493 494 495
}

void KoShape::setZIndex(int zIndex) {
496
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559
    d->zIndex = zIndex;
}

void KoShape::setVisible(bool on) {
    d->visible = on;
}

bool KoShape::isVisible() const {
    return d->visible;
}

void KoShape::setSelectable(bool selectable) {
    d->selectable = selectable;
}

bool KoShape::isSelectable() const {
    return d->selectable;
}

void KoShape::setLocked(bool locked) {
    d->locked = locked;
}

bool KoShape::isLocked() const {
    return d->locked;
}

KoShapeContainer *KoShape::parent() const {
    return d->parent;
}

void KoShape::setKeepAspectRatio(bool keepAspect) {
    d->keepAspect = keepAspect;
}

bool KoShape::keepAspectRatio() const {
    return d->keepAspect;
}

const QString &KoShape::shapeId() const {
    return d->shapeId;
}

void KoShape::setShapeId(const QString &id) {
    d->shapeId = id;
}

void KoShape::setCollisionDetection(bool detect) {
    d->detectCollision = detect;
}

bool KoShape::collisionDetection() {
    return d->detectCollision;
}

void KoShape::addShapeManager( KoShapeManager * manager ) {
    d->shapeManagers.insert( manager );
}

void KoShape::removeShapeManager( KoShapeManager * manager ) {
    d->shapeManagers.remove( manager );
}

560 561 562 563 564 565 566 567 568
KoShapeBorderModel *KoShape::border() const {
    return d->border;
}

void KoShape::setBorder(KoShapeBorderModel *border) {
    d->border = border;
}

const QMatrix& KoShape::matrix() const {
569
    return d->localMatrix;
570 571
}

572 573 574 575 576 577 578 579 580 581 582 583 584
void KoShape::addConnection(KoShapeConnection *connection) {
    d->connections.append(connection);
    foreach(KoShapeManager *sm, d->shapeManagers)
        sm->addShapeConnection( connection );
}

void KoShape::removeConnection(KoShapeConnection *connection) {
    d->connections.removeAll(connection);
}

QList<KoShapeConnection*> KoShape::connections() const {
    return d->connections;
}
585

586
QString KoShape::name() const {
587 588 589 590 591 592
    return d->name;
}

void KoShape::setName( const QString & name ) {
    d->name = name;
}
593 594

// loading & saving methods
595
void KoShape::saveOdfConnections(KoShapeSavingContext &context) const {
596 597
    // TODO  save "draw-glue-point" elements (9.2.19)
}
598

599
QString KoShape::style( KoShapeSavingContext &context ) const
600 601
{
    KoGenStyle style;
602
    if ( context.isSet( KoShapeSavingContext::PresentationShape ) ) {
603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621
        style = KoGenStyle( KoGenStyle::STYLE_PRESENTATIONAUTO, "presentation" );
    }
    else {
        style = KoGenStyle( KoGenStyle::STYLE_GRAPHICAUTO, "graphic" );
    }

    // and fill the style
    KoShapeBorderModel * b = border();
    if ( b )
    {
        b->fillStyle( style, context );
    }
    QBrush bg( background() );
    switch ( bg.style() )
    {
        case Qt::NoBrush:
            style.addProperty( "draw:fill","none" );
            break;
        default:    // TODO all the other ones.
622
            KoOasisStyles::saveOasisFillStyle( style, context.mainStyles(), bg );
623 624 625
            break;
    }

626
    if ( context.isSet( KoShapeSavingContext::AutoStyleInStyleXml ) ) {
627 628 629
        style.setAutoStyleInStylesDotXml( true );
    }

630
    return context.mainStyles().lookup( style, context.isSet( KoShapeSavingContext::PresentationShape ) ? "pr" : "gr" );
631 632
}

633 634 635 636 637 638 639 640 641
bool KoShape::loadOdfAttributes( const KoXmlElement & element, KoShapeLoadingContext &context, int attributes )
{
    if ( attributes & OdfMandatories ) {
        if ( element.hasAttributeNS( KoXmlNS::draw, "layer" ) ) {
            KoShapeLayer * layer = context.layer( element.attributeNS( KoXmlNS::draw, "layer" ) );
            if ( layer ) {
                setParent( layer );
            }
        }
642 643 644 645 646 647
        if ( element.hasAttributeNS( KoXmlNS::draw, "id" ) ) {
            QString id = element.attributeNS( KoXmlNS::draw, "id" );
            if ( !id.isNull() ) {
                context.addShapeId( this, id );
            }
        }
648 649 650 651 652 653 654
        if ( element.hasAttributeNS( KoXmlNS::draw, "z-index" ) ) {
            // what do we do in case of copy/paste
        }
        else {
            // TODO what do we do in the case the z-index is not there then the order in the doc
            // is the the order of the z-index
        }
655 656

        setBackground( loadOdfFill( element, context ) );
657
    }
658

659 660
    if ( attributes & OdfSize ) {
        QPointF pos;
Ariya Hidayat's avatar
Ariya Hidayat committed
661 662
        pos.setX( KoUnit::parseValue( element.attributeNS( KoXmlNS::svg, "x", QString() ) ) );
        pos.setY( KoUnit::parseValue( element.attributeNS( KoXmlNS::svg, "y", QString() ) ) );
663 664 665
        setPosition( pos );

        QSizeF size;
Ariya Hidayat's avatar
Ariya Hidayat committed
666 667
        size.setWidth( KoUnit::parseValue( element.attributeNS( KoXmlNS::svg, "width", QString() ) ) );
        size.setHeight( KoUnit::parseValue( element.attributeNS( KoXmlNS::svg, "height", QString() ) ) );
668
        resize( size );
669 670
    }

Jan Hambrecht's avatar
 
Jan Hambrecht committed
671 672 673 674 675 676 677
    if( attributes & OdfTransformation )
    {
        QString transform = element.attributeNS( KoXmlNS::draw, "transform", QString() );
        if( ! transform.isEmpty() )
            applyTransformation( parseOdfTransform( transform ) );
    }

678 679 680
    return true;
}

681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707
QBrush KoShape::loadOdfFill( const KoXmlElement & element, KoShapeLoadingContext & context )
{
    KoStyleStack &styleStack = context.koLoadingContext().styleStack();
    QString fill;
    if( element.hasAttributeNS( KoXmlNS::draw, "style-name" ) )
    {
        // fill the style stack with the shapes style
        context.koLoadingContext().fillStyleStack( element, KoXmlNS::draw, "style-name", "graphic" );
        styleStack.setTypeProperties( "graphic" );
        if( styleStack.hasProperty( KoXmlNS::draw, "fill" ) )
            fill = styleStack.property( KoXmlNS::draw, "fill" );
    }
    else if( element.hasAttributeNS( KoXmlNS::presentation, "style-name" ) )
    {
        // fill the style stack with the shapes style
        context.koLoadingContext().fillStyleStack( element, KoXmlNS::presentation, "style-name", "presentation" );
        styleStack.setTypeProperties( "presentation" );
        if ( styleStack.hasProperty( KoXmlNS::presentation, "fill" ) )
            fill = styleStack.property( KoXmlNS::presentation, "fill" );
    }

    if ( fill == "solid" || fill == "hatch" )
        return KoOasisStyles::loadOasisFillStyle( styleStack, fill, context.koLoadingContext().oasisStyles() );

    return QBrush();
}

Jan Hambrecht's avatar
 
Jan Hambrecht committed
708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724
QMatrix KoShape::parseOdfTransform( const QString &transform )
{
    QMatrix matrix;

    // Split string for handling 1 transform statement at a time
    QStringList subtransforms = transform.split(')', QString::SkipEmptyParts);
    QStringList::ConstIterator it = subtransforms.begin();
    QStringList::ConstIterator end = subtransforms.end();
    for(; it != end; ++it)
    {
        QStringList subtransform = (*it).split('(', QString::SkipEmptyParts);

        subtransform[0] = subtransform[0].trimmed().toLower();
        subtransform[1] = subtransform[1].simplified();
        QRegExp reg("[,( ]");
        QStringList params = subtransform[1].split(reg, QString::SkipEmptyParts);

Thomas Zander's avatar
Thomas Zander committed
725
        if(subtransform[0].startsWith(';') || subtransform[0].startsWith(','))
Jan Hambrecht's avatar
 
Jan Hambrecht committed
726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
            subtransform[0] = subtransform[0].right(subtransform[0].length() - 1);

        if(subtransform[0] == "rotate")
        {
            // TODO find out what oo2 really does when rotating, it seems severly broken
            if(params.count() == 3)
            {
                double x = KoUnit::parseValue( params[1] );
                double y = KoUnit::parseValue( params[2] );

                matrix.translate(x, y);
                // oo2 rotates by radians
                matrix.rotate( params[0].toDouble()*180.0/M_PI );
                matrix.translate(-x, -y);
            }
            else
            {
                // oo2 rotates by radians
                matrix.rotate( params[0].toDouble()*180.0/M_PI );
            }
        }
        else if(subtransform[0] == "translate")
        {
            if(params.count() == 2)
            {
                double x = KoUnit::parseValue( params[0] );
                double y = KoUnit::parseValue( params[1] );
                matrix.translate(x, y);
            }
            else    // Spec : if only one param given, assume 2nd param to be 0
                matrix.translate( KoUnit::parseValue( params[0] ) , 0);
        }
        else if(subtransform[0] == "scale")
        {
            if(params.count() == 2)
                matrix.scale(params[0].toDouble(), params[1].toDouble());
            else    // Spec : if only one param given, assume uniform scaling
                matrix.scale(params[0].toDouble(), params[0].toDouble());
        }
        else if(subtransform[0] == "skewx")
            matrix.shear(tan(params[0].toDouble()), 0.0F);
        else if(subtransform[0] == "skewy")
            matrix.shear(tan(params[0].toDouble()), 0.0F);
        else if(subtransform[0] == "skewy")
            matrix.shear(0.0F, tan(params[0].toDouble()));
        else if(subtransform[0] == "matrix")
        {
            if(params.count() >= 6)
                matrix.setMatrix(params[0].toDouble(), params[1].toDouble(), params[2].toDouble(), params[3].toDouble(), KoUnit::parseValue( params[4] ), KoUnit::parseValue( params[5] ) );
        }
    }

    return matrix;
}

781
void KoShape::saveOdfFrameAttributes(KoShapeSavingContext &context) const {
782
    saveOdfAttributes(context, FrameAttributes);
783
    context.addOption(KoShapeSavingContext::FrameOpened);
784 785
}

786
void KoShape::saveOdfAttributes(KoShapeSavingContext &context, int attributes) const {
787 788
    if(attributes & OdfMandatories) {
        // all items that should be written to 'draw:frame' and any other 'draw:' object that inherits this shape
789
        context.xmlWriter().addAttribute( context.isSet( KoShapeSavingContext::PresentationShape ) ?
790 791
                                          "presentation:style-name": "draw:style-name",
                                          style( context ) );
792

793
        if ( context.isSet( KoShapeSavingContext::DrawId ) )
794
        {
795
            context.xmlWriter().addAttribute( "draw:id", context.drawId( this ) );
796 797
        }

798
        if(d->parent && dynamic_cast<KoShapeLayer*> (d->parent))
799
            context.xmlWriter().addAttribute("draw:layer", d->parent->name());
800 801
    }

802 803
    // all items after this should not be written out when they have already be written in
    // a 'draw:frame' attribute.
804 805
    if(context.isSet(KoShapeSavingContext::FrameOpened)) {
        context.removeOption(KoShapeSavingContext::FrameOpened);
806
        return;
807 808
    }

809 810
    if(attributes & OdfSize) {
        QSizeF s( size() );
811 812 813 814
        context.xmlWriter().addAttributePt( "svg:width", s.width() );
        context.xmlWriter().addAttributePt( "svg:height", s.height() );
        context.xmlWriter().addAttributePt( "svg:x", d->pos.x() );
        context.xmlWriter().addAttributePt( "svg:y", d->pos.y() );
815 816
    }

817
    if(attributes & OdfMandatories) {
818
        context.xmlWriter().addAttribute("draw:z-index", zIndex());
819 820
    }

821 822 823
    if(attributes & OdfTransformation) {
        // just like in shapes; ODF allows you to manipulate the 'matrix' after setting an
        // ofset on the shape (using the x and y positions).   Lets save them here.
824
        /*
825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845
        bool rotate = qAbs(d->angle) > 1E-6;
        bool skew = qAbs(d->shearX) > 1E-6 || qAbs(d->shearY) > 1E-6;
        bool scale = qAbs(d->scaleX - 1) > 1E-6 || qAbs(d->scaleY -1) > 1E-6;

        if(rotate && (skew || scale)) {
            QMatrix matrix; // can't use transformationMatrix() as that includes transformation of the container as well.
            QSizeF size(this->size());
            if ( d->angle != 0 )
            {
                matrix.translate( size.width() / 2.0 * d->scaleX, size.height() / 2.0 * d->scaleY );
                matrix.translate( size.height() / 2.0 * d->shearX, size.width() / 2.0 * d->shearY );
                matrix.rotate( d->angle );
                matrix.translate( -size.width() / 2.0 * d->scaleX, -size.height() / 2.0 * d->scaleY );
                matrix.translate( -size.height() / 2.0 * d->shearX, -size.width() / 2.0 * d->shearY );
            }
            matrix.shear( d->shearX, d->shearY );
            matrix.scale( d->scaleX, d->scaleY );

            QString m = QString( "matrix(0 0 %3 %4 %5pt %6pt)" ).arg( matrix.m11() ).arg( matrix.m12() )
                .arg( matrix.m21() ).arg( matrix.m22() )
                .arg( matrix.dx() ) .arg( matrix.dy() );
846
            context.xmlWriter().addAttribute( "draw:transform", m );
847 848 849 850 851 852 853 854 855 856 857 858 859 860
        }
        else if(rotate || skew || scale) {
            QString transform;
            if(rotate)
                transform = "rotate("+ QString::number(d->angle) +')';
            if(skew)
                transform = "skewX("+ QString::number(d->shearX) +") skewY("+ QString::number(d->shearY) +')';
            if(scale) {
                transform += "scale("+ QString::number(d->scaleX);
                if(d->scaleX != d->scaleY)
                    transform += ','+ QString::number(d->scaleY);
                transform += ')';
            }

861
            context.xmlWriter().addAttribute( "draw:transform", transform );
862
        }
863 864 865
        */
        if( ! d->localMatrix.isIdentity() )
        {
Jan Hambrecht's avatar
 
Jan Hambrecht committed
866 867
            QString m = QString( "matrix(%1 %2 %3 %4 %5pt %6pt)" )
                    .arg( d->localMatrix.m11() ).arg( d->localMatrix.m12() )
868 869 870 871
                    .arg( d->localMatrix.m21() ).arg( d->localMatrix.m22() )
                    .arg( d->localMatrix.dx() ) .arg( d->localMatrix.dy() );
            context.xmlWriter().addAttribute( "draw:transform", m );
        }
872 873 874 875 876 877 878 879 880 881 882 883 884
    }
}

// end loading & saving methods


// static
void KoShape::applyConversion(QPainter &painter, const KoViewConverter &converter) {
    double zoomX, zoomY;
    converter.zoom(&zoomX, &zoomY);
    painter.scale(zoomX, zoomY);
}