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) {
407
    delete d->userData;
Thomas Zander's avatar
Thomas Zander committed
408
    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) {
416
    // appdata is deleted by the application.
Thomas Zander's avatar
Thomas Zander committed
417
    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 477 478
    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
479 480 481 482 483 484 485 486 487 488 489
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

683 684 685
    return true;
}

686 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
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();
}

713 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
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
777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793
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
794
        if(subtransform[0].startsWith(';') || subtransform[0].startsWith(','))
Jan Hambrecht's avatar
 
Jan Hambrecht committed
795 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
            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;
}

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

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

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

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

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

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

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

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

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

// end loading & saving methods


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