00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "KoShape.h"
00024 #include "KoShape_p.h"
00025 #include "KoShapeContainer.h"
00026 #include "KoShapeLayer.h"
00027 #include "KoShapeContainerModel.h"
00028 #include "KoSelection.h"
00029 #include "KoPointerEvent.h"
00030 #include "KoInsets.h"
00031 #include "KoShapeBorderModel.h"
00032 #include "KoShapeBackground.h"
00033 #include "KoColorBackground.h"
00034 #include "KoGradientBackground.h"
00035 #include "KoPatternBackground.h"
00036 #include "KoShapeManager.h"
00037 #include "KoShapeUserData.h"
00038 #include "KoShapeApplicationData.h"
00039 #include "KoShapeSavingContext.h"
00040 #include "KoShapeLoadingContext.h"
00041 #include "KoViewConverter.h"
00042 #include "KoLineBorder.h"
00043 #include "ShapeDeleter_p.h"
00044 #include "KoShapeShadow.h"
00045 #include "KoEventAction.h"
00046 #include "KoEventActionRegistry.h"
00047 #include "KoOdfWorkaround.h"
00048 #include "KoFilterEffectStack.h"
00049
00050 #include <KoXmlReader.h>
00051 #include <KoXmlWriter.h>
00052 #include <KoXmlNS.h>
00053 #include <KoGenStyle.h>
00054 #include <KoGenStyles.h>
00055 #include <KoUnit.h>
00056 #include <KoOdfStylesReader.h>
00057 #include <KoOdfGraphicStyles.h>
00058 #include <KoOdfLoadingContext.h>
00059
00060 #include <QPainter>
00061 #include <QVariant>
00062 #include <QPainterPath>
00063 #include <QList>
00064 #include <QMap>
00065 #include <QByteArray>
00066
00067 #include <kdebug.h>
00068
00069 #include <limits>
00070
00071 KoShapePrivate::KoShapePrivate(KoShape *shape)
00072 : size(50, 50),
00073 parent(0),
00074 userData(0),
00075 appData(0),
00076 fill(0),
00077 border(0),
00078 q(shape),
00079 shadow(0),
00080 filterEffectStack(0),
00081 zIndex(0),
00082 visible(true),
00083 printable(true),
00084 geometryProtected(false),
00085 keepAspect(false),
00086 selectable(true),
00087 detectCollision(false),
00088 protectContent(false)
00089 {
00090 connectors.append(QPointF(0.5, 0.0));
00091 connectors.append(QPointF(1.0, 0.5));
00092 connectors.append(QPointF(0.5, 1.0));
00093 connectors.append(QPointF(0.0, 0.5));
00094 }
00095
00096 KoShapePrivate::~KoShapePrivate()
00097 {
00098 if (parent)
00099 parent->removeChild(q);
00100 foreach(KoShapeManager *manager, shapeManagers) {
00101 manager->remove(q);
00102 manager->removeAdditional(q);
00103 }
00104 delete userData;
00105 delete appData;
00106 if (border && ! border->removeUser())
00107 delete border;
00108 if (shadow && ! shadow->removeUser())
00109 delete shadow;
00110 if (fill && ! fill->removeUser())
00111 delete fill;
00112 if (filterEffectStack && ! filterEffectStack->removeUser())
00113 delete filterEffectStack;
00114 qDeleteAll(eventActions);
00115 }
00116
00117 void KoShapePrivate::shapeChanged(KoShape::ChangeType type)
00118 {
00119 if (parent)
00120 parent->model()->childChanged(q, type);
00121 q->shapeChanged(type);
00122 foreach(KoShape * shape, dependees)
00123 shape->shapeChanged(type, q);
00124 }
00125
00126 void KoShapePrivate::updateBorder()
00127 {
00128 if (border == 0)
00129 return;
00130 KoInsets insets;
00131 border->borderInsets(q, insets);
00132 QSizeF inner = q->size();
00133
00134 q->update(QRectF(-insets.left, -insets.top, insets.left,
00135 inner.height() + insets.top + insets.bottom));
00136
00137 q->update(QRectF(-insets.left, -insets.top,
00138 inner.width() + insets.left + insets.right, insets.top));
00139
00140 q->update(QRectF(inner.width(), -insets.top, insets.right,
00141 inner.height() + insets.top + insets.bottom));
00142
00143 q->update(QRectF(-insets.left, inner.height(),
00144 inner.width() + insets.left + insets.right, insets.bottom));
00145 }
00146
00147
00148 KoShape::KoShape()
00149 : d_ptr(new KoShapePrivate(this))
00150 {
00151 notifyChanged();
00152 }
00153
00154 KoShape::KoShape(KoShapePrivate &dd)
00155 : d_ptr(&dd)
00156 {
00157 }
00158
00159 KoShape::~KoShape()
00160 {
00161 Q_D(KoShape);
00162 d->shapeChanged(Deleted);
00163 delete d_ptr;
00164 }
00165
00166 void KoShape::paintDecorations(QPainter &painter, const KoViewConverter &converter, const KoCanvasBase *canvas)
00167 {
00168 Q_UNUSED(painter);
00169 Q_UNUSED(converter);
00170 Q_UNUSED(canvas);
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186 }
00187
00188 void KoShape::setScale(qreal sx, qreal sy)
00189 {
00190 Q_D(KoShape);
00191 QPointF pos = position();
00192 QMatrix scaleMatrix;
00193 scaleMatrix.translate(pos.x(), pos.y());
00194 scaleMatrix.scale(sx, sy);
00195 scaleMatrix.translate(-pos.x(), -pos.y());
00196 d->localMatrix = d->localMatrix * scaleMatrix;
00197
00198 notifyChanged();
00199 d->shapeChanged(ScaleChanged);
00200 }
00201
00202 void KoShape::rotate(qreal angle)
00203 {
00204 Q_D(KoShape);
00205 QPointF center = d->localMatrix.map(QPointF(0.5 * size().width(), 0.5 * size().height()));
00206 QMatrix rotateMatrix;
00207 rotateMatrix.translate(center.x(), center.y());
00208 rotateMatrix.rotate(angle);
00209 rotateMatrix.translate(-center.x(), -center.y());
00210 d->localMatrix = d->localMatrix * rotateMatrix;
00211
00212 notifyChanged();
00213 d->shapeChanged(RotationChanged);
00214 }
00215
00216 void KoShape::setShear(qreal sx, qreal sy)
00217 {
00218 Q_D(KoShape);
00219 QPointF pos = position();
00220 QMatrix shearMatrix;
00221 shearMatrix.translate(pos.x(), pos.y());
00222 shearMatrix.shear(sx, sy);
00223 shearMatrix.translate(-pos.x(), -pos.y());
00224 d->localMatrix = d->localMatrix * shearMatrix;
00225
00226 notifyChanged();
00227 d->shapeChanged(ShearChanged);
00228 }
00229
00230 void KoShape::setSize(const QSizeF &newSize)
00231 {
00232 Q_D(KoShape);
00233 QSizeF oldSize(size());
00234 if (oldSize == newSize)
00235 return;
00236
00237 QMatrix matrix;
00238 oldSize.setHeight(qMax((qreal) 1E-4, oldSize.height()));
00239 oldSize.setWidth(qMax((qreal) 1E-4, oldSize.width()));
00240 matrix.scale(newSize.width()/oldSize.width(), newSize.height()/oldSize.height());
00241
00242 KoGradientBackground * g = dynamic_cast<KoGradientBackground*>(d->fill);
00243 if (g) {
00244 g->setMatrix(g->matrix() * matrix);
00245 }
00246 KoLineBorder *l = dynamic_cast<KoLineBorder*>(d->border);
00247 if (l && l->lineBrush().gradient()) {
00248 QBrush brush = l->lineBrush();
00249 brush.setMatrix(brush.matrix() * matrix);
00250 l->setLineBrush(brush);
00251 }
00252
00253 d->size = newSize;
00254
00255 notifyChanged();
00256 d->shapeChanged(SizeChanged);
00257 }
00258
00259 void KoShape::setPosition(const QPointF &newPosition)
00260 {
00261 Q_D(KoShape);
00262 QPointF currentPos = position();
00263 if (newPosition == currentPos)
00264 return;
00265 QMatrix translateMatrix;
00266 translateMatrix.translate(newPosition.x() - currentPos.x(), newPosition.y() - currentPos.y());
00267 d->localMatrix = d->localMatrix * translateMatrix;
00268
00269 notifyChanged();
00270 d->shapeChanged(PositionChanged);
00271 }
00272
00273 bool KoShape::hitTest(const QPointF &position) const
00274 {
00275 Q_D(const KoShape);
00276 if (d->parent && d->parent->childClipped(this) && !d->parent->hitTest(position))
00277 return false;
00278
00279 QPointF point = absoluteTransformation(0).inverted().map(position);
00280 QRectF bb(QPointF(), size());
00281 if (d->border) {
00282 KoInsets insets;
00283 d->border->borderInsets(this, insets);
00284 bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
00285 }
00286 if (bb.contains(point))
00287 return true;
00288
00289
00290 if (! d->shadow)
00291 return false;
00292
00293
00294
00295 point = absoluteTransformation(0).inverted().map(position - d->shadow->offset());
00296
00297 return bb.contains(point);
00298 }
00299
00300 QRectF KoShape::boundingRect() const
00301 {
00302 Q_D(const KoShape);
00303 QSizeF mySize = size();
00304 QMatrix transform = absoluteTransformation(0);
00305 QRectF bb(QPointF(0, 0), mySize);
00306 if (d->border) {
00307 KoInsets insets;
00308 d->border->borderInsets(this, insets);
00309 bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
00310 }
00311 bb = transform.mapRect(bb);
00312 if (d->shadow) {
00313 KoInsets insets;
00314 d->shadow->insets(this, insets);
00315 bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
00316 }
00317 if (d->filterEffectStack) {
00318 QRectF clipRect = d->filterEffectStack->clipRectForBoundingRect(QRectF(QPointF(), mySize));
00319 bb |= transform.mapRect(clipRect);
00320 }
00321
00322 return bb;
00323 }
00324
00325 QMatrix KoShape::absoluteTransformation(const KoViewConverter *converter) const
00326 {
00327 Q_D(const KoShape);
00328 QMatrix matrix;
00329
00330 KoShapeContainer * container = d->parent;
00331 if (container) {
00332 if (container->childClipped(this))
00333 matrix = container->absoluteTransformation(0);
00334 else {
00335 QSizeF containerSize = container->size();
00336 QPointF containerPos = container->absolutePosition() - QPointF(0.5 * containerSize.width(), 0.5 * containerSize.height());
00337 if (converter)
00338 containerPos = converter->documentToView(containerPos);
00339 matrix.translate(containerPos.x(), containerPos.y());
00340 }
00341 }
00342
00343 if (converter) {
00344 QPointF pos = d->localMatrix.map(QPointF());
00345 QPointF trans = converter->documentToView(pos) - pos;
00346 matrix.translate(trans.x(), trans.y());
00347 }
00348
00349 return d->localMatrix * matrix;
00350 }
00351
00352 void KoShape::applyAbsoluteTransformation(const QMatrix &matrix)
00353 {
00354 QMatrix globalMatrix = absoluteTransformation(0);
00355
00356
00357
00358 QMatrix transformMatrix = globalMatrix * matrix * globalMatrix.inverted();
00359 applyTransformation(transformMatrix);
00360 }
00361
00362 void KoShape::applyTransformation(const QMatrix &matrix)
00363 {
00364 Q_D(KoShape);
00365 d->localMatrix = matrix * d->localMatrix;
00366 notifyChanged();
00367 d->shapeChanged(GenericMatrixChange);
00368 }
00369
00370 void KoShape::setTransformation(const QMatrix &matrix)
00371 {
00372 Q_D(KoShape);
00373 d->localMatrix = matrix;
00374 notifyChanged();
00375 d->shapeChanged(GenericMatrixChange);
00376 }
00377
00378 QMatrix KoShape::transformation() const
00379 {
00380 Q_D(const KoShape);
00381 return d->localMatrix;
00382 }
00383
00384 bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
00385 {
00386 bool foundCommonParent = false;
00387 KoShape *parentShapeS1 = s1;
00388 KoShape *parentShapeS2 = s2;
00389 int index1 = parentShapeS1->zIndex();
00390 int index2 = parentShapeS2->zIndex();
00391 while (parentShapeS1 && !foundCommonParent) {
00392 parentShapeS2 = s2;
00393 index2 = parentShapeS2->zIndex();
00394 while (parentShapeS2) {
00395 if (parentShapeS2 == parentShapeS1) {
00396 foundCommonParent = true;
00397 break;
00398 }
00399 index2 = parentShapeS2->zIndex();
00400 parentShapeS2 = parentShapeS2->parent();
00401 }
00402
00403 if (!foundCommonParent) {
00404 index1 = parentShapeS1->zIndex();
00405 parentShapeS1 = parentShapeS1->parent();
00406 }
00407 }
00408 if (s1 == parentShapeS2) {
00409 return true;
00410 }
00411 else if (s2 == parentShapeS1) {
00412 return false;
00413 }
00414 return index1 < index2;
00415 }
00416
00417 void KoShape::setParent(KoShapeContainer *parent)
00418 {
00419 Q_D(KoShape);
00420 if (d->parent == parent)
00421 return;
00422 KoShapeContainer *oldParent = d->parent;
00423 d->parent = 0;
00424 if (oldParent)
00425 oldParent->removeChild(this);
00426 if (parent && parent != this) {
00427 d->parent = parent;
00428 parent->addChild(this);
00429 }
00430 notifyChanged();
00431 d->shapeChanged(ParentChanged);
00432 }
00433
00434 int KoShape::zIndex() const
00435 {
00436 Q_D(const KoShape);
00437 return d->zIndex;
00438 }
00439
00440 void KoShape::update() const
00441 {
00442 Q_D(const KoShape);
00443 if (!d->shapeManagers.empty()) {
00444 QRectF rect(boundingRect());
00445 foreach(KoShapeManager * manager, d->shapeManagers)
00446 manager->update(rect, this, true);
00447 }
00448 }
00449
00450 void KoShape::update(const QRectF &shape) const
00451 {
00452 Q_D(const KoShape);
00453 if (!d->shapeManagers.empty() && isVisible()) {
00454 QRectF rect(absoluteTransformation(0).mapRect(shape));
00455 foreach(KoShapeManager * manager, d->shapeManagers) {
00456 manager->update(rect);
00457 }
00458 }
00459 }
00460
00461 QPainterPath KoShape::outline() const
00462 {
00463 Q_D(const KoShape);
00464 QPainterPath path;
00465 path.addRect(QRectF(QPointF(0, 0), QSizeF(qMax(d->size.width(), qreal(0.0001)), qMax(d->size.height(), qreal(0.0001)))));
00466 return path;
00467 }
00468
00469 QPointF KoShape::absolutePosition(KoFlake::Position anchor) const
00470 {
00471 QPointF point;
00472 switch (anchor) {
00473 case KoFlake::TopLeftCorner: break;
00474 case KoFlake::TopRightCorner: point = QPointF(size().width(), 0.0); break;
00475 case KoFlake::BottomLeftCorner: point = QPointF(0.0, size().height()); break;
00476 case KoFlake::BottomRightCorner: point = QPointF(size().width(), size().height()); break;
00477 case KoFlake::CenteredPosition: point = QPointF(size().width() / 2.0, size().height() / 2.0); break;
00478 }
00479 return absoluteTransformation(0).map(point);
00480 }
00481
00482 void KoShape::setAbsolutePosition(QPointF newPosition, KoFlake::Position anchor)
00483 {
00484 Q_D(KoShape);
00485 QPointF currentAbsPosition = absolutePosition(anchor);
00486 QPointF translate = newPosition - currentAbsPosition;
00487 QMatrix translateMatrix;
00488 translateMatrix.translate(translate.x(), translate.y());
00489 applyAbsoluteTransformation(translateMatrix);
00490 notifyChanged();
00491 d->shapeChanged(PositionChanged);
00492 }
00493
00494 void KoShape::copySettings(const KoShape *shape)
00495 {
00496 Q_D(KoShape);
00497 d->size = shape->size();
00498 d->connectors.clear();
00499 foreach(const QPointF & point, shape->connectionPoints())
00500 addConnectionPoint(point);
00501 d->zIndex = shape->zIndex();
00502 d->visible = shape->isVisible();
00503
00504
00505 if (!d->visible)
00506 d->printable = true;
00507 else
00508 d->printable = shape->isPrintable();
00509
00510 d->geometryProtected = shape->isGeometryProtected();
00511 d->keepAspect = shape->keepAspectRatio();
00512 d->localMatrix = shape->d_ptr->localMatrix;
00513 }
00514
00515 void KoShape::notifyChanged()
00516 {
00517 Q_D(KoShape);
00518 foreach(KoShapeManager * manager, d->shapeManagers) {
00519 manager->notifyShapeChanged(this);
00520 }
00521 }
00522
00523 void KoShape::setUserData(KoShapeUserData *userData)
00524 {
00525 Q_D(KoShape);
00526 delete d->userData;
00527 d->userData = userData;
00528 }
00529
00530 KoShapeUserData *KoShape::userData() const
00531 {
00532 Q_D(const KoShape);
00533 return d->userData;
00534 }
00535
00536 void KoShape::setApplicationData(KoShapeApplicationData *appData)
00537 {
00538 Q_D(KoShape);
00539
00540 d->appData = appData;
00541 }
00542
00543 KoShapeApplicationData *KoShape::applicationData() const
00544 {
00545 Q_D(const KoShape);
00546 return d->appData;
00547 }
00548
00549 bool KoShape::hasTransparency()
00550 {
00551 Q_D(KoShape);
00552 if (! d->fill)
00553 return true;
00554 else
00555 return d->fill->hasTransparency();
00556 }
00557
00558 KoInsets KoShape::borderInsets() const
00559 {
00560 Q_D(const KoShape);
00561 KoInsets answer;
00562 if (d->border)
00563 d->border->borderInsets(this, answer);
00564 return answer;
00565 }
00566
00567 qreal KoShape::rotation() const
00568 {
00569 Q_D(const KoShape);
00570
00571
00572
00573
00574 if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
00575 return std::numeric_limits<qreal>::quiet_NaN();
00576
00577 if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
00578 return std::numeric_limits<qreal>::quiet_NaN();
00579
00580
00581 qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
00582 if (angle < 0.0)
00583 angle += 360.0;
00584
00585 return angle;
00586 }
00587
00588 QSizeF KoShape::size() const
00589 {
00590 Q_D(const KoShape);
00591 return d->size;
00592 }
00593
00594 QPointF KoShape::position() const
00595 {
00596 Q_D(const KoShape);
00597 QPointF center(0.5*size().width(), 0.5*size().height());
00598 return d->localMatrix.map(center) - center;
00599
00600 }
00601
00602 void KoShape::addConnectionPoint(const QPointF &point)
00603 {
00604 Q_D(KoShape);
00605 QSizeF s = size();
00606
00607 d->connectors.append(QPointF(point.x() / s.width(), point.y() / s.height()));
00608 }
00609
00610 QList<QPointF> KoShape::connectionPoints() const
00611 {
00612 Q_D(const KoShape);
00613 QList<QPointF> points;
00614 QSizeF s = size();
00615
00616 foreach(const QPointF & cp, d->connectors)
00617 points.append(QPointF(s.width() * cp.x(), s.height() * cp.y()));
00618
00619 return points;
00620 }
00621
00622 void KoShape::addEventAction(KoEventAction * action)
00623 {
00624 Q_D(KoShape);
00625 if (! d->eventActions.contains(action)) {
00626 d->eventActions.append(action);
00627 }
00628 }
00629
00630 void KoShape::removeEventAction(KoEventAction * action)
00631 {
00632 Q_D(KoShape);
00633 if (d->eventActions.contains(action)) {
00634 d->eventActions.removeAll(action);
00635 }
00636 }
00637
00638 QList<KoEventAction *> KoShape::eventActions() const
00639 {
00640 Q_D(const KoShape);
00641 return d->eventActions;
00642 }
00643
00644 void KoShape::setBackground(KoShapeBackground * fill)
00645 {
00646 Q_D(KoShape);
00647 if (d->fill)
00648 d->fill->removeUser();
00649 d->fill = fill;
00650 if (d->fill)
00651 d->fill->addUser();
00652 d->shapeChanged(BackgroundChanged);
00653 notifyChanged();
00654 }
00655
00656 KoShapeBackground * KoShape::background() const
00657 {
00658 Q_D(const KoShape);
00659 return d->fill;
00660 }
00661
00662 void KoShape::setZIndex(int zIndex)
00663 {
00664 Q_D(KoShape);
00665 notifyChanged();
00666 d->zIndex = zIndex;
00667 }
00668
00669 void KoShape::setVisible(bool on)
00670 {
00671 Q_D(KoShape);
00672 d->visible = on;
00673 }
00674
00675 bool KoShape::isVisible(bool recursive) const
00676 {
00677 Q_D(const KoShape);
00678 if (! recursive)
00679 return d->visible;
00680 if (recursive && ! d->visible)
00681 return false;
00682
00683 KoShapeContainer * parentShape = parent();
00684 while (parentShape) {
00685 if (! parentShape->isVisible())
00686 return false;
00687 parentShape = parentShape->parent();
00688 }
00689 return true;
00690 }
00691
00692 void KoShape::setPrintable(bool on)
00693 {
00694 Q_D(KoShape);
00695 d->printable = on;
00696 }
00697
00698 bool KoShape::isPrintable() const
00699 {
00700 Q_D(const KoShape);
00701 if (d->visible)
00702 return d->printable;
00703 else
00704 return false;
00705 }
00706
00707 void KoShape::setSelectable(bool selectable)
00708 {
00709 Q_D(KoShape);
00710 d->selectable = selectable;
00711 }
00712
00713 bool KoShape::isSelectable() const
00714 {
00715 Q_D(const KoShape);
00716 return d->selectable;
00717 }
00718
00719 void KoShape::setGeometryProtected(bool on)
00720 {
00721 Q_D(KoShape);
00722 d->geometryProtected = on;
00723 }
00724
00725 bool KoShape::isGeometryProtected() const
00726 {
00727 Q_D(const KoShape);
00728 return d->geometryProtected;
00729 }
00730
00731 void KoShape::setContentProtected(bool protect)
00732 {
00733 Q_D(KoShape);
00734 d->protectContent = protect;
00735 }
00736
00737 bool KoShape::isContentProtected() const
00738 {
00739 Q_D(const KoShape);
00740 return d->protectContent;
00741 }
00742
00743 KoShapeContainer *KoShape::parent() const
00744 {
00745 Q_D(const KoShape);
00746 return d->parent;
00747 }
00748
00749 void KoShape::setKeepAspectRatio(bool keepAspect)
00750 {
00751 Q_D(KoShape);
00752 d->keepAspect = keepAspect;
00753 }
00754
00755 bool KoShape::keepAspectRatio() const
00756 {
00757 Q_D(const KoShape);
00758 return d->keepAspect;
00759 }
00760
00761 QString KoShape::shapeId() const
00762 {
00763 Q_D(const KoShape);
00764 return d->shapeId;
00765 }
00766
00767 void KoShape::setShapeId(const QString &id)
00768 {
00769 Q_D(KoShape);
00770 d->shapeId = id;
00771 }
00772
00773 void KoShape::setCollisionDetection(bool detect)
00774 {
00775 Q_D(KoShape);
00776 d->detectCollision = detect;
00777 }
00778
00779 bool KoShape::collisionDetection()
00780 {
00781 Q_D(KoShape);
00782 return d->detectCollision;
00783 }
00784
00785 void KoShape::addShapeManager(KoShapeManager * manager)
00786 {
00787 Q_D(KoShape);
00788 d->shapeManagers.insert(manager);
00789 }
00790
00791 void KoShape::removeShapeManager(KoShapeManager * manager)
00792 {
00793 Q_D(KoShape);
00794 d->shapeManagers.remove(manager);
00795 }
00796
00797 KoShapeBorderModel *KoShape::border() const
00798 {
00799 Q_D(const KoShape);
00800 return d->border;
00801 }
00802
00803 void KoShape::setBorder(KoShapeBorderModel *border)
00804 {
00805 Q_D(KoShape);
00806 if (border)
00807 border->addUser();
00808 d->updateBorder();
00809 if (d->border)
00810 d->border->removeUser();
00811 d->border = border;
00812 d->updateBorder();
00813 d->shapeChanged(BorderChanged);
00814 notifyChanged();
00815 }
00816
00817 void KoShape::setShadow(KoShapeShadow * shadow)
00818 {
00819 Q_D(KoShape);
00820 if (d->shadow)
00821 d->shadow->removeUser();
00822 d->shadow = shadow;
00823 if (d->shadow) {
00824 d->shadow->addUser();
00825
00826 }
00827 d->shapeChanged(ShadowChanged);
00828 notifyChanged();
00829 }
00830
00831 KoShapeShadow * KoShape::shadow() const
00832 {
00833 Q_D(const KoShape);
00834 return d->shadow;
00835 }
00836
00837 QMatrix KoShape::matrix() const
00838 {
00839 Q_D(const KoShape);
00840 return d->localMatrix;
00841 }
00842
00843 void KoShape::removeConnectionPoint(int index)
00844 {
00845 Q_D(KoShape);
00846 if (index < d->connectors.count())
00847 d->connectors.remove(index);
00848 }
00849
00850 QString KoShape::name() const
00851 {
00852 Q_D(const KoShape);
00853 return d->name;
00854 }
00855
00856 void KoShape::setName(const QString & name)
00857 {
00858 Q_D(KoShape);
00859 d->name = name;
00860 }
00861
00862 void KoShape::waitUntilReady(const KoViewConverter &converter, bool asynchronous) const
00863 {
00864 Q_UNUSED(converter);
00865 Q_UNUSED(asynchronous);
00866 }
00867
00868 void KoShape::deleteLater()
00869 {
00870 Q_D(KoShape);
00871 foreach(KoShapeManager *manager, d->shapeManagers)
00872 manager->remove(this);
00873 d->shapeManagers.clear();
00874 new ShapeDeleter(this);
00875 }
00876
00877 bool KoShape::isEditable() const
00878 {
00879 Q_D(const KoShape);
00880 if (!d->visible || d->geometryProtected)
00881 return false;
00882
00883 if (d->parent && d->parent->isChildLocked(this))
00884 return false;
00885
00886 return true;
00887 }
00888
00889
00890 QString KoShape::saveStyle(KoGenStyle &style, KoShapeSavingContext &context) const
00891 {
00892 Q_D(const KoShape);
00893
00894 KoShapeBorderModel * b = border();
00895 if (b) {
00896 b->fillStyle(style, context);
00897 }
00898 else {
00899 style.addProperty( "draw:stroke", "none" );
00900 }
00901 KoShapeShadow * s = shadow();
00902 if (s)
00903 s->fillStyle(style, context);
00904
00905 KoShapeBackground * bg = background();
00906 if (bg) {
00907 bg->fillStyle(style, context);
00908 }
00909 else {
00910 style.addProperty( "draw:fill", "none" );
00911 }
00912
00913 if (context.isSet(KoShapeSavingContext::AutoStyleInStyleXml)) {
00914 style.setAutoStyleInStylesDotXml(true);
00915 }
00916
00917 QString value;
00918 if (isGeometryProtected())
00919 value = "position size";
00920 if (isContentProtected()) {
00921 if (! value.isEmpty())
00922 value += ' ';
00923 value += "content";
00924 }
00925 if (!value.isEmpty())
00926 (const_cast<KoShapePrivate*>(d))->additionalStyleAttributes.insert("style:protect", value);
00927
00928 QMap<QByteArray, QString>::const_iterator it(d->additionalStyleAttributes.constBegin());
00929 for (; it != d->additionalStyleAttributes.constEnd(); ++it) {
00930 style.addProperty(it.key(), it.value());
00931 }
00932
00933 return context.mainStyles().lookup(style, context.isSet(KoShapeSavingContext::PresentationShape) ? "pr" : "gr");
00934 }
00935
00936 void KoShape::loadStyle(const KoXmlElement & element, KoShapeLoadingContext &context)
00937 {
00938 Q_D(KoShape);
00939
00940 KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
00941 styleStack.save();
00942
00943
00944 if (element.hasAttributeNS(KoXmlNS::draw, "style-name")) {
00945 context.odfLoadingContext().fillStyleStack(element, KoXmlNS::draw, "style-name", "graphic");
00946 styleStack.setTypeProperties("graphic");
00947 } else if (element.hasAttributeNS(KoXmlNS::presentation, "style-name")) {
00948 context.odfLoadingContext().fillStyleStack(element, KoXmlNS::presentation, "style-name", "presentation");
00949 styleStack.setTypeProperties("graphic");
00950 }
00951
00952 if(d->fill && !d->fill->removeUser()) {
00953 delete d->fill;
00954 d->fill = 0;
00955 }
00956 if(d->border && !d->border->removeUser()) {
00957 delete d->border;
00958 d->border = 0;
00959 }
00960 if(d->shadow && !d->shadow->removeUser()) {
00961 delete d->shadow;
00962 d->shadow = 0;
00963 }
00964 setBackground(loadOdfFill(element, context));
00965 setBorder(loadOdfStroke(element, context));
00966 setShadow(loadOdfShadow(element, context));
00967
00968 styleStack.restore();
00969 }
00970
00971 bool KoShape::loadOdfAttributes(const KoXmlElement & element, KoShapeLoadingContext &context, int attributes)
00972 {
00973 Q_D(KoShape);
00974 if (attributes & OdfPosition) {
00975 QPointF pos(position());
00976 if (element.hasAttributeNS(KoXmlNS::svg, "x"))
00977 pos.setX(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "x", QString())));
00978 if (element.hasAttributeNS(KoXmlNS::svg, "y"))
00979 pos.setY(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "y", QString())));
00980 setPosition(pos);
00981 }
00982
00983 if (attributes & OdfSize) {
00984 QSizeF s(size());
00985 if (element.hasAttributeNS(KoXmlNS::svg, "width"))
00986 s.setWidth(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "width", QString())));
00987 if (element.hasAttributeNS(KoXmlNS::svg, "height"))
00988 s.setHeight(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "height", QString())));
00989 setSize(s);
00990 }
00991
00992 if (attributes & OdfLayer) {
00993 if (element.hasAttributeNS(KoXmlNS::draw, "layer")) {
00994 KoShapeLayer * layer = context.layer(element.attributeNS(KoXmlNS::draw, "layer"));
00995 if (layer) {
00996 setParent(layer);
00997 }
00998 }
00999 }
01000
01001 if (attributes & OdfId) {
01002 if (element.hasAttributeNS(KoXmlNS::draw, "id")) {
01003 QString id = element.attributeNS(KoXmlNS::draw, "id");
01004 if (!id.isNull()) {
01005 context.addShapeId(this, id);
01006 }
01007 }
01008 }
01009
01010 if (attributes & OdfZIndex) {
01011 if (element.hasAttributeNS(KoXmlNS::draw, "z-index")) {
01012 setZIndex(element.attributeNS(KoXmlNS::draw, "z-index").toInt());
01013 }
01014 else {
01015 setZIndex(context.zIndex());
01016 }
01017 }
01018
01019 if (attributes & OdfName) {
01020 if (element.hasAttributeNS(KoXmlNS::draw, "name")) {
01021 setName(element.attributeNS(KoXmlNS::draw, "name"));
01022 }
01023 }
01024
01025 if (attributes & OdfStyle) {
01026 loadStyle(element, context);
01027 }
01028
01029 if (attributes & OdfTransformation) {
01030 QString transform = element.attributeNS(KoXmlNS::draw, "transform", QString());
01031 if (! transform.isEmpty())
01032 applyAbsoluteTransformation(parseOdfTransform(transform));
01033 }
01034
01035 if (attributes & OdfAdditionalAttributes) {
01036 QSet<KoShapeLoadingContext::AdditionalAttributeData> additionalAttributeData = KoShapeLoadingContext::additionalAttributeData();
01037 foreach(const KoShapeLoadingContext::AdditionalAttributeData & attributeData, additionalAttributeData) {
01038 if (element.hasAttributeNS(attributeData.ns, attributeData.tag)) {
01039 QString value = element.attributeNS(attributeData.ns, attributeData.tag);
01040
01041 setAdditionalAttribute(attributeData.name, value);
01042 }
01043 }
01044 }
01045
01046 if (attributes & OdfCommonChildElements) {
01047 const KoXmlElement eventActionsElement(KoXml::namedItemNS(element, KoXmlNS::office, "event-listeners"));
01048 if (!eventActionsElement.isNull()) {
01049 d->eventActions = KoEventActionRegistry::instance()->createEventActionsFromOdf(eventActionsElement, context);
01050 }
01051
01052 }
01053
01054 return true;
01055 }
01056
01057 QString KoShape::getStyleProperty(const char *property, const KoXmlElement & element, KoShapeLoadingContext & context)
01058 {
01059 Q_UNUSED(element);
01060 KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
01061 QString value;
01062
01063 if (styleStack.hasProperty(KoXmlNS::draw, property)) {
01064 value = styleStack.property(KoXmlNS::draw, property);
01065 }
01066
01067 return value;
01068 }
01069
01070 KoShapeBackground * KoShape::loadOdfFill(const KoXmlElement & element, KoShapeLoadingContext & context)
01071 {
01072 QString fill = getStyleProperty("fill", element, context);
01073 KoShapeBackground * bg = 0;
01074 if (fill == "solid" || fill == "hatch")
01075 bg = new KoColorBackground();
01076 else if (fill == "gradient")
01077 bg = new KoGradientBackground(new QLinearGradient());
01078 else if (fill == "bitmap")
01079 bg = new KoPatternBackground(context.imageCollection());
01080
01081 if (! bg)
01082 return 0;
01083
01084 if (! bg->loadStyle(context.odfLoadingContext(), size())) {
01085 delete bg;
01086 return 0;
01087 }
01088
01089 return bg;
01090 }
01091
01092 KoShapeBorderModel * KoShape::loadOdfStroke(const KoXmlElement & element, KoShapeLoadingContext & context)
01093 {
01094 KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
01095 KoOdfStylesReader &stylesReader = context.odfLoadingContext().stylesReader();
01096
01097 QString stroke = getStyleProperty("stroke", element, context);
01098 if (stroke == "solid" || stroke == "dash") {
01099 QPen pen = KoOdfGraphicStyles::loadOdfStrokeStyle(styleStack, stroke, stylesReader);
01100
01101 KoLineBorder * border = new KoLineBorder();
01102
01103 if (styleStack.hasProperty(KoXmlNS::koffice, "stroke-gradient")) {
01104 QString gradientName = styleStack.property(KoXmlNS::koffice, "stroke-gradient");
01105 QBrush brush = KoOdfGraphicStyles::loadOdfGradientStyleByName(stylesReader, gradientName, size());
01106 border->setLineBrush(brush);
01107 } else {
01108 border->setColor(pen.color());
01109 }
01110
01111 #ifndef NWORKAROUND_ODF_BUGS
01112 KoOdfWorkaround::fixPenWidth(pen, context);
01113 #endif
01114 border->setLineWidth(pen.widthF());
01115 border->setJoinStyle(pen.joinStyle());
01116 border->setLineStyle(pen.style(), pen.dashPattern());
01117
01118 return border;
01119 } else {
01120 return 0;
01121 }
01122 }
01123
01124 KoShapeShadow * KoShape::loadOdfShadow(const KoXmlElement & element, KoShapeLoadingContext & context)
01125 {
01126 KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
01127 QString shadowStyle = getStyleProperty("shadow", element, context);
01128 if (shadowStyle == "visible" || shadowStyle == "hidden") {
01129 KoShapeShadow * shadow = new KoShapeShadow();
01130 QColor shadowColor(styleStack.property(KoXmlNS::draw, "shadow-color"));
01131 qreal offsetX = KoUnit::parseValue(styleStack.property(KoXmlNS::draw, "shadow-offset-x"));
01132 qreal offsetY = KoUnit::parseValue(styleStack.property(KoXmlNS::draw, "shadow-offset-y"));
01133 shadow->setOffset(QPointF(offsetX, offsetY));
01134
01135 QString opacity = styleStack.property(KoXmlNS::draw, "shadow-opacity");
01136 if (! opacity.isEmpty() && opacity.right(1) == "%")
01137 shadowColor.setAlphaF(opacity.left(opacity.length() - 1).toFloat() / 100.0);
01138 shadow->setColor(shadowColor);
01139 shadow->setVisibility(shadowStyle == "visible");
01140
01141 return shadow;
01142 }
01143 return 0;
01144 }
01145
01146 QMatrix KoShape::parseOdfTransform(const QString &transform)
01147 {
01148 QMatrix matrix;
01149
01150
01151 QStringList subtransforms = transform.split(')', QString::SkipEmptyParts);
01152 QStringList::ConstIterator it = subtransforms.constBegin();
01153 QStringList::ConstIterator end = subtransforms.constEnd();
01154 for (; it != end; ++it) {
01155 QStringList subtransform = (*it).split('(', QString::SkipEmptyParts);
01156
01157 subtransform[0] = subtransform[0].trimmed().toLower();
01158 subtransform[1] = subtransform[1].simplified();
01159 QRegExp reg("[,( ]");
01160 QStringList params = subtransform[1].split(reg, QString::SkipEmptyParts);
01161
01162 if (subtransform[0].startsWith(';') || subtransform[0].startsWith(','))
01163 subtransform[0] = subtransform[0].right(subtransform[0].length() - 1);
01164
01165 QString cmd = subtransform[0].toLower();
01166
01167 if (cmd == "rotate") {
01168 QMatrix rotMatrix;
01169 if (params.count() == 3) {
01170 qreal x = KoUnit::parseValue(params[1]);
01171 qreal y = KoUnit::parseValue(params[2]);
01172
01173 rotMatrix.translate(x, y);
01174
01175 rotMatrix.rotate(-params[0].toDouble()*180.0 / M_PI);
01176 rotMatrix.translate(-x, -y);
01177 } else {
01178
01179 rotMatrix.rotate(-params[0].toDouble()*180.0 / M_PI);
01180 }
01181 matrix = matrix * rotMatrix;
01182 } else if (cmd == "translate") {
01183 QMatrix moveMatrix;
01184 if (params.count() == 2) {
01185 qreal x = KoUnit::parseValue(params[0]);
01186 qreal y = KoUnit::parseValue(params[1]);
01187 moveMatrix.translate(x, y);
01188 } else
01189 moveMatrix.translate(KoUnit::parseValue(params[0]) , 0);
01190 matrix = matrix * moveMatrix;
01191 } else if (cmd == "scale") {
01192 QMatrix scaleMatrix;
01193 if (params.count() == 2)
01194 scaleMatrix.scale(params[0].toDouble(), params[1].toDouble());
01195 else
01196 scaleMatrix.scale(params[0].toDouble(), params[0].toDouble());
01197 matrix = matrix * scaleMatrix;
01198 } else if (cmd == "skewx") {
01199 QPointF p = absolutePosition(KoFlake::TopLeftCorner);
01200 QMatrix shearMatrix;
01201 shearMatrix.translate(p.x(), p.y());
01202 shearMatrix.shear(tan(-params[0].toDouble()), 0.0F);
01203 shearMatrix.translate(-p.x(), -p.y());
01204 matrix = matrix * shearMatrix;
01205 } else if (cmd == "skewy") {
01206 QPointF p = absolutePosition(KoFlake::TopLeftCorner);
01207 QMatrix shearMatrix;
01208 shearMatrix.translate(p.x(), p.y());
01209 shearMatrix.shear(0.0F, tan(-params[0].toDouble()));
01210 shearMatrix.translate(-p.x(), -p.y());
01211 matrix = matrix * shearMatrix;
01212 } else if (cmd == "matrix") {
01213 QMatrix m;
01214 if (params.count() >= 6)
01215 m.setMatrix(params[0].toDouble(), params[1].toDouble(), params[2].toDouble(), params[3].toDouble(),
01216 KoUnit::parseValue(params[4]), KoUnit::parseValue(params[5]));
01217 matrix = matrix * m;
01218 }
01219 }
01220
01221 return matrix;
01222 }
01223
01224 void KoShape::saveOdfAttributes(KoShapeSavingContext &context, int attributes) const
01225 {
01226 Q_D(const KoShape);
01227 if (attributes & OdfStyle) {
01228 KoGenStyle style;
01229
01230 if (context.isSet(KoShapeSavingContext::PresentationShape)) {
01231 style = KoGenStyle(KoGenStyle::StylePresentationAuto, "presentation");
01232 context.xmlWriter().addAttribute("presentation:style-name", saveStyle(style, context));
01233 } else {
01234 style = KoGenStyle(KoGenStyle::StyleGraphicAuto, "graphic");
01235 context.xmlWriter().addAttribute("draw:style-name", saveStyle(style, context));
01236 }
01237 }
01238
01239 if (attributes & OdfId) {
01240 if (context.isSet(KoShapeSavingContext::DrawId)) {
01241 context.xmlWriter().addAttribute("draw:id", context.drawId(this));
01242 }
01243 }
01244
01245 if (attributes & OdfName) {
01246 if (! name().isEmpty())
01247 context.xmlWriter().addAttribute("draw:name", name());
01248 }
01249
01250 if (attributes & OdfLayer) {
01251 KoShape *parent = d->parent;
01252 while (parent) {
01253 if (dynamic_cast<KoShapeLayer*>(parent)) {
01254 context.xmlWriter().addAttribute("draw:layer", parent->name());
01255 break;
01256 }
01257 parent = parent->parent();
01258 }
01259 }
01260
01261 if (attributes & OdfSize) {
01262 const QSizeF s(size());
01263 context.xmlWriter().addAttributePt("svg:width", s.width());
01264 context.xmlWriter().addAttributePt("svg:height", s.height());
01265 }
01266
01267
01268
01269 if ((attributes & OdfPosition) && !(attributes & OdfTransformation)) {
01270 const QPointF p(position() * context.shapeOffset(this));
01271 context.xmlWriter().addAttributePt("svg:x", p.x());
01272 context.xmlWriter().addAttributePt("svg:y", p.y());
01273 }
01274
01275 if (attributes & OdfTransformation) {
01276 QMatrix matrix = absoluteTransformation(0) * context.shapeOffset(this);
01277 if (! matrix.isIdentity()) {
01278 if (qAbs(matrix.m11() - 1) < 1E-5
01279 && qAbs(matrix.m12()) < 1E-5
01280 && qAbs(matrix.m21()) < 1E-5
01281 && qAbs(matrix.m22() - 1) < 1E-5) {
01282 context.xmlWriter().addAttribute("svg:x", QString("%1pt").arg(matrix.dx()));
01283 context.xmlWriter().addAttribute("svg:y", QString("%1pt").arg(matrix.dy()));
01284 } else {
01285 QString m = QString("matrix(%1 %2 %3 %4 %5pt %6pt)")
01286 .arg(matrix.m11()).arg(matrix.m12())
01287 .arg(matrix.m21()).arg(matrix.m22())
01288 .arg(matrix.dx()) .arg(matrix.dy());
01289 context.xmlWriter().addAttribute("draw:transform", m);
01290 }
01291 }
01292 }
01293
01294 if (attributes & OdfViewbox) {
01295 const QSizeF s(size());
01296 QString viewBox = QString("0 0 %1 %2").arg(qRound(s.width())).arg(qRound(s.height()));
01297 context.xmlWriter().addAttribute("svg:viewBox", viewBox);
01298 }
01299
01300 if (attributes & OdfAdditionalAttributes) {
01301 QMap<QByteArray, QString>::const_iterator it(d->additionalAttributes.constBegin());
01302 for (; it != d->additionalAttributes.constEnd(); ++it) {
01303 context.xmlWriter().addAttribute(it.key(), it.value());
01304 }
01305 }
01306 }
01307
01308 void KoShape::saveOdfCommonChildElements(KoShapeSavingContext &context) const
01309 {
01310 Q_D(const KoShape);
01311
01312 if (d->eventActions.size() > 0) {
01313 context.xmlWriter().startElement("office:event-listeners");
01314 foreach(KoEventAction * action, d->eventActions) {
01315 action->saveOdf(context);
01316 }
01317 context.xmlWriter().endElement();
01318 }
01319
01320
01321 }
01322
01323
01324
01325 void KoShape::init(const QMap<QString, KoDataCenter *> & dataCenterMap)
01326 {
01327 Q_UNUSED(dataCenterMap);
01328 }
01329
01330
01331
01332 void KoShape::applyConversion(QPainter &painter, const KoViewConverter &converter)
01333 {
01334 qreal zoomX, zoomY;
01335 converter.zoom(&zoomX, &zoomY);
01336 painter.scale(zoomX, zoomY);
01337 }
01338
01339 QPointF KoShape::shapeToDocument(const QPointF &point) const
01340 {
01341 return absoluteTransformation(0).map(point);
01342 }
01343
01344 QRectF KoShape::shapeToDocument(const QRectF &rect) const
01345 {
01346 return absoluteTransformation(0).mapRect(rect);
01347 }
01348
01349 QPointF KoShape::documentToShape(const QPointF &point) const
01350 {
01351 return absoluteTransformation(0).inverted().map(point);
01352 }
01353
01354 QRectF KoShape::documentToShape(const QRectF &rect) const
01355 {
01356 return absoluteTransformation(0).inverted().mapRect(rect);
01357 }
01358
01359 bool KoShape::addDependee(KoShape * shape)
01360 {
01361 Q_D(KoShape);
01362 if (! shape)
01363 return false;
01364
01365
01366 if (shape->hasDependee(this))
01367 return false;
01368
01369 if (! d->dependees.contains(shape))
01370 d->dependees.append(shape);
01371
01372 return true;
01373 }
01374
01375 void KoShape::removeDependee(KoShape * shape)
01376 {
01377 Q_D(KoShape);
01378 int index = d->dependees.indexOf(shape);
01379 if (index >= 0)
01380 d->dependees.removeAt(index);
01381 }
01382
01383 bool KoShape::hasDependee(KoShape * shape) const
01384 {
01385 Q_D(const KoShape);
01386 return d->dependees.contains(shape);
01387 }
01388
01389 void KoShape::shapeChanged(ChangeType type, KoShape *shape)
01390 {
01391 Q_UNUSED(type);
01392 Q_UNUSED(shape);
01393 }
01394
01395 KoSnapData KoShape::snapData() const
01396 {
01397 return KoSnapData();
01398 }
01399
01400 void KoShape::setAdditionalAttribute(const char * name, const QString & value)
01401 {
01402 Q_D(KoShape);
01403 d->additionalAttributes.insert(name, value);
01404 }
01405
01406 void KoShape::removeAdditionalAttribute(const char * name)
01407 {
01408 Q_D(KoShape);
01409 d->additionalAttributes.remove(name);
01410 }
01411
01412 bool KoShape::hasAdditionalAttribute(const char * name) const
01413 {
01414 Q_D(const KoShape);
01415 return d->additionalAttributes.contains(name);
01416 }
01417
01418 QString KoShape::additionalAttribute(const char * name) const
01419 {
01420 Q_D(const KoShape);
01421 return d->additionalAttributes.value(name);
01422 }
01423
01424 void KoShape::setAdditionalStyleAttribute(const char * name, const QString & value)
01425 {
01426 Q_D(KoShape);
01427 d->additionalStyleAttributes.insert(name, value);
01428 }
01429
01430 void KoShape::removeAdditionalStyleAttribute(const char * name)
01431 {
01432 Q_D(KoShape);
01433 d->additionalStyleAttributes.remove(name);
01434 }
01435
01436 KoFilterEffectStack * KoShape::filterEffectStack() const
01437 {
01438 Q_D(const KoShape);
01439 return d->filterEffectStack;
01440 }
01441
01442 void KoShape::setFilterEffectStack(KoFilterEffectStack * filterEffectStack)
01443 {
01444 Q_D(KoShape);
01445 if (d->filterEffectStack)
01446 d->filterEffectStack->removeUser();
01447 d->filterEffectStack = filterEffectStack;
01448 if (d->filterEffectStack) {
01449 d->filterEffectStack->addUser();
01450 }
01451 notifyChanged();
01452 }