KoShape.cpp 30 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"
37
#include "KoLineBorder.h"
Thomas Zander's avatar
Thomas Zander committed
38

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

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

53 54
#include <kdebug.h>

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

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

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

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

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

    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;
109 110
    QBrush backgroundBrush; ///< Stands for the background color / fill etc.
    KoShapeBorderModel *border; ///< points to a border, or 0 if there is no border
111
    QList<KoShapeConnection*> connections;
112
    KoShape *me;
Thomas Zander's avatar
Thomas Zander committed
113 114
};

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
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
287

288
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2) {
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
    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
305 306 307
}

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

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

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

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

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

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

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

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

Thomas Zander's avatar
Thomas Zander committed
393 394 395
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
396 397
}

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

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

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

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

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

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

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

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

double KoShape::rotation() const {
446 447 448 449 450
    // 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
451
        return NAN;
452 453
    // 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
454
        return NAN;
455 456 457 458 459 460 461

    // 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
462 463 464
}

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

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

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

QPointF KoShape::position() const {
477 478 479
    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
480 481 482 483 484 485 486 487 488 489 490
}

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 ) {
491
    d->backgroundBrush = brush;
Thomas Zander's avatar
Thomas Zander committed
492 493
}

494
QBrush KoShape::background() const {
495
    return d->backgroundBrush;
Thomas Zander's avatar
Thomas Zander committed
496 497 498
}

void KoShape::setZIndex(int zIndex) {
499
    notifyChanged();
Thomas Zander's avatar
Thomas Zander committed
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 560 561 562
    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 );
}

563 564 565 566 567 568 569 570 571
KoShapeBorderModel *KoShape::border() const {
    return d->border;
}

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

const QMatrix& KoShape::matrix() const {
572
    return d->localMatrix;
573 574
}

575 576 577 578 579 580 581 582 583 584 585 586 587
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;
}
588

589
QString KoShape::name() const {
590 591 592 593 594 595
    return d->name;
}

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

// loading & saving methods
598
void KoShape::saveOdfConnections(KoShapeSavingContext &context) const {
599
    // TODO  save "draw-glue-point" elements (9.2.19)
600
    Q_UNUSED( context );
601
}
602

603
QString KoShape::style( KoShapeSavingContext &context ) const
604 605
{
    KoGenStyle style;
606
    if ( context.isSet( KoShapeSavingContext::PresentationShape ) ) {
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
        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.
626
            KoOasisStyles::saveOasisFillStyle( style, context.mainStyles(), bg );
627 628 629
            break;
    }

630
    if ( context.isSet( KoShapeSavingContext::AutoStyleInStyleXml ) ) {
631 632 633
        style.setAutoStyleInStylesDotXml( true );
    }

634
    return context.mainStyles().lookup( style, context.isSet( KoShapeSavingContext::PresentationShape ) ? "pr" : "gr" );
635 636
}

637 638 639 640 641 642 643 644 645
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 );
            }
        }
646 647 648 649 650 651
        if ( element.hasAttributeNS( KoXmlNS::draw, "id" ) ) {
            QString id = element.attributeNS( KoXmlNS::draw, "id" );
            if ( !id.isNull() ) {
                context.addShapeId( this, id );
            }
        }
652
        if ( element.hasAttributeNS( KoXmlNS::draw, "z-index" ) ) {
Jan Hambrecht's avatar
Jan Hambrecht committed
653 654
            // TODO what do we do in case of copy/paste
            setZIndex( element.attributeNS( KoXmlNS::draw, "z-index" ).toInt() );
655 656 657 658 659
        }
        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
        }
660 661

        setBackground( loadOdfFill( element, context ) );
662
        setBorder( loadOdfStroke( element, context ) );
663
    }
664

665 666
    if ( attributes & OdfSize ) {
        QPointF pos;
Ariya Hidayat's avatar
Ariya Hidayat committed
667 668
        pos.setX( KoUnit::parseValue( element.attributeNS( KoXmlNS::svg, "x", QString() ) ) );
        pos.setY( KoUnit::parseValue( element.attributeNS( KoXmlNS::svg, "y", QString() ) ) );
669 670 671
        setPosition( pos );

        QSizeF size;
Ariya Hidayat's avatar
Ariya Hidayat committed
672 673
        size.setWidth( KoUnit::parseValue( element.attributeNS( KoXmlNS::svg, "width", QString() ) ) );
        size.setHeight( KoUnit::parseValue( element.attributeNS( KoXmlNS::svg, "height", QString() ) ) );
674
        resize( size );
675 676
    }

Jan Hambrecht's avatar
 
Jan Hambrecht committed
677 678 679 680 681 682 683
    if( attributes & OdfTransformation )
    {
        QString transform = element.attributeNS( KoXmlNS::draw, "transform", QString() );
        if( ! transform.isEmpty() )
            applyTransformation( parseOdfTransform( transform ) );
    }

684 685 686
    return true;
}

687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713
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();
}

714 715 716 717 718 719 720 721 722 723 724 725 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
KoShapeBorderModel * KoShape::loadOdfStroke( const KoXmlElement & element, KoShapeLoadingContext & context )
{
    KoStyleStack &styleStack = context.koLoadingContext().styleStack();
    QString stroke;
    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, "stroke" ) )
            stroke = styleStack.property( KoXmlNS::draw, "stroke" );
    }
    else if( element.hasAttributeNS( KoXmlNS::draw, "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, "stroke" ) )
            stroke = styleStack.property( KoXmlNS::presentation, "stroke" );
    }

    KoLineBorder * border = 0;

    if( stroke == "solid" )
    {
        border = new KoLineBorder();
    }
    else if( stroke == "dash" )
    {
        border = new KoLineBorder();
        if( styleStack.hasProperty( KoXmlNS::draw, "stroke-dash" ) )
        {
            QString dashStyleName = styleStack.property( KoXmlNS::draw, "stroke-dash" );
            // TODO load dashes
        }
    }
    else 
        return 0;

    if ( styleStack.hasProperty( KoXmlNS::svg, "stroke-color" ) )
        border->setColor( styleStack.property( KoXmlNS::svg, "stroke-color" ) );
    if ( styleStack.hasProperty( KoXmlNS::svg, "stroke-opacity" ) )
    {
        QColor color = border->color();
        QString opacity = styleStack.property( KoXmlNS::svg, "stroke-opacity" );
        color.setAlphaF( opacity.toDouble() );
        border->setColor( color );
    }
    if( styleStack.hasProperty( KoXmlNS::svg, "stroke-width" ) )
        border->setLineWidth( KoUnit::parseValue( styleStack.property( KoXmlNS::svg, "stroke-width" ) ) );
    if( styleStack.hasProperty( KoXmlNS::draw, "stroke-linejoin" ) )
    {
        QString join = styleStack.property( KoXmlNS::draw, "stroke-linejoin" );
        if( join == "bevel" )
            border->setJoinStyle( Qt::BevelJoin );
        else if( join == "round" )
            border->setJoinStyle( Qt::RoundJoin );
        else
            border->setJoinStyle( Qt::MiterJoin );
    }

    return border;
}

Jan Hambrecht's avatar
 
Jan Hambrecht committed
778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794
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
795
        if(subtransform[0].startsWith(';') || subtransform[0].startsWith(','))
Jan Hambrecht's avatar
 
Jan Hambrecht committed
796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850
            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;
}

851
void KoShape::saveOdfFrameAttributes(KoShapeSavingContext &context) const {
852
    saveOdfAttributes(context, FrameAttributes);
853
    context.addOption(KoShapeSavingContext::FrameOpened);
854 855
}

856
void KoShape::saveOdfAttributes(KoShapeSavingContext &context, int attributes) const {
857 858
    if(attributes & OdfMandatories) {
        // all items that should be written to 'draw:frame' and any other 'draw:' object that inherits this shape
859
        context.xmlWriter().addAttribute( context.isSet( KoShapeSavingContext::PresentationShape ) ?
860 861
                                          "presentation:style-name": "draw:style-name",
                                          style( context ) );
862

863
        if ( context.isSet( KoShapeSavingContext::DrawId ) )
864
        {
865
            context.xmlWriter().addAttribute( "draw:id", context.drawId( this ) );
866 867
        }

868
        if(d->parent && dynamic_cast<KoShapeLayer*> (d->parent))
869
            context.xmlWriter().addAttribute("draw:layer", d->parent->name());
870 871
    }

872 873
    // all items after this should not be written out when they have already be written in
    // a 'draw:frame' attribute.
874 875
    if(context.isSet(KoShapeSavingContext::FrameOpened)) {
        context.removeOption(KoShapeSavingContext::FrameOpened);
876
        return;
877 878
    }

879 880
    if(attributes & OdfSize) {
        QSizeF s( size() );
881 882 883 884
        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() );
885 886
    }

887
    if(attributes & OdfMandatories) {
888
        context.xmlWriter().addAttribute("draw:z-index", zIndex());
889 890
    }

891 892 893
    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.
894
        /*
895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915
        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() );
916
            context.xmlWriter().addAttribute( "draw:transform", m );
917 918 919 920 921 922 923 924 925 926 927 928 929 930
        }
        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 += ')';
            }

931
            context.xmlWriter().addAttribute( "draw:transform", transform );
932
        }
933 934 935
        */
        if( ! d->localMatrix.isIdentity() )
        {
Jan Hambrecht's avatar
 
Jan Hambrecht committed
936 937
            QString m = QString( "matrix(%1 %2 %3 %4 %5pt %6pt)" )
                    .arg( d->localMatrix.m11() ).arg( d->localMatrix.m12() )
938 939 940 941
                    .arg( d->localMatrix.m21() ).arg( d->localMatrix.m22() )
                    .arg( d->localMatrix.dx() ) .arg( d->localMatrix.dy() );
            context.xmlWriter().addAttribute( "draw:transform", m );
        }
942 943 944 945 946 947 948 949 950 951 952 953 954
    }
}

// end loading & saving methods


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