Warning, file /office/calligra/libs/flake/KoShape.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* This file is part of the KDE project 0002 Copyright (C) 2006 C. Boemann Rasmussen <cbo@boemann.dk> 0003 Copyright (C) 2006-2010 Thomas Zander <zander@kde.org> 0004 Copyright (C) 2006-2010 Thorsten Zachmann <zachmann@kde.org> 0005 Copyright (C) 2007-2009,2011 Jan Hambrecht <jaham@gmx.net> 0006 CopyRight (C) 2010 Boudewijn Rempt <boud@kogmbh.com> 0007 0008 This library is free software; you can redistribute it and/or 0009 modify it under the terms of the GNU Library General Public 0010 License as published by the Free Software Foundation; either 0011 version 2 of the License, or (at your option) any later version. 0012 0013 This library is distributed in the hope that it will be useful, 0014 but WITHOUT ANY WARRANTY; without even the implied warranty of 0015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0016 Library General Public License for more details. 0017 0018 You should have received a copy of the GNU Library General Public License 0019 along with this library; see the file COPYING.LIB. If not, write to 0020 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0021 Boston, MA 02110-1301, USA. 0022 */ 0023 0024 #include "KoShape.h" 0025 #include "KoShape_p.h" 0026 #include "KoShapeContainer.h" 0027 #include "KoShapeLayer.h" 0028 #include "KoShapeContainerModel.h" 0029 #include "KoSelection.h" 0030 #include "KoPointerEvent.h" 0031 #include "KoInsets.h" 0032 #include "KoShapeStrokeModel.h" 0033 #include "KoShapeBackground.h" 0034 #include "KoColorBackground.h" 0035 #include "KoHatchBackground.h" 0036 #include "KoGradientBackground.h" 0037 #include "KoPatternBackground.h" 0038 #include "KoShapeManager.h" 0039 #include "KoShapeUserData.h" 0040 #include "KoShapeApplicationData.h" 0041 #include "KoShapeSavingContext.h" 0042 #include "KoShapeLoadingContext.h" 0043 #include "KoViewConverter.h" 0044 #include "KoShapeStroke.h" 0045 #include "KoShapeShadow.h" 0046 #include "KoClipPath.h" 0047 #include "KoPathShape.h" 0048 #include "KoEventAction.h" 0049 #include "KoEventActionRegistry.h" 0050 #include "KoOdfWorkaround.h" 0051 #include "KoFilterEffectStack.h" 0052 #include <KoSnapData.h> 0053 #include <KoElementReference.h> 0054 0055 #include <KoXmlReader.h> 0056 #include <KoXmlWriter.h> 0057 #include <KoXmlNS.h> 0058 #include <KoGenStyle.h> 0059 #include <KoGenStyles.h> 0060 #include <KoUnit.h> 0061 #include <KoOdfStylesReader.h> 0062 #include <KoOdfGraphicStyles.h> 0063 #include <KoOdfLoadingContext.h> 0064 #include <KoStyleStack.h> 0065 #include <KoBorder.h> 0066 0067 #include <QPainter> 0068 #include <QVariant> 0069 #include <QPainterPath> 0070 #include <QList> 0071 #include <QMap> 0072 #include <QByteArray> 0073 #include <FlakeDebug.h> 0074 0075 #include <limits> 0076 #include "KoOdfGradientBackground.h" 0077 0078 // KoShapePrivate 0079 0080 KoShapePrivate::KoShapePrivate(KoShape *shape) 0081 : q_ptr(shape), 0082 size(50, 50), 0083 parent(0), 0084 userData(0), 0085 appData(0), 0086 stroke(0), 0087 shadow(0), 0088 border(0), 0089 clipPath(0), 0090 filterEffectStack(0), 0091 transparency(0.0), 0092 zIndex(0), 0093 runThrough(0), 0094 visible(true), 0095 printable(true), 0096 keepAspect(false), 0097 detectCollision(false), 0098 textRunAroundSide(KoShape::BiggestRunAroundSide), 0099 textRunAroundDistanceLeft(0.0), 0100 textRunAroundDistanceTop(0.0), 0101 textRunAroundDistanceRight(0.0), 0102 textRunAroundDistanceBottom(0.0), 0103 textRunAroundThreshold(0.0), 0104 textRunAroundContour(KoShape::ContourFull), 0105 anchor(0), 0106 minimumHeight(0.0) 0107 { 0108 // All interactions allowed by default 0109 allowedInteractions = KoShape::MoveAllowed 0110 | KoShape::ResizeAllowed 0111 | KoShape::ShearingAllowed 0112 | KoShape::RotationAllowed 0113 | KoShape::SelectionAllowed 0114 | KoShape::ContentChangeAllowed 0115 | KoShape::DeletionAllowed; 0116 0117 connectors[KoConnectionPoint::TopConnectionPoint] = KoConnectionPoint::defaultConnectionPoint(KoConnectionPoint::TopConnectionPoint); 0118 connectors[KoConnectionPoint::RightConnectionPoint] = KoConnectionPoint::defaultConnectionPoint(KoConnectionPoint::RightConnectionPoint); 0119 connectors[KoConnectionPoint::BottomConnectionPoint] = KoConnectionPoint::defaultConnectionPoint(KoConnectionPoint::BottomConnectionPoint); 0120 connectors[KoConnectionPoint::LeftConnectionPoint] = KoConnectionPoint::defaultConnectionPoint(KoConnectionPoint::LeftConnectionPoint); 0121 connectors[KoConnectionPoint::FirstCustomConnectionPoint] = KoConnectionPoint(QPointF(0.5, 0.5), KoConnectionPoint::AllDirections, KoConnectionPoint::AlignCenter); 0122 } 0123 0124 KoShapePrivate::~KoShapePrivate() 0125 { 0126 Q_Q(KoShape); 0127 if (parent) 0128 parent->removeShape(q); 0129 foreach(KoShapeManager *manager, shapeManagers) { 0130 manager->remove(q); 0131 manager->removeAdditional(q); 0132 } 0133 delete userData; 0134 delete appData; 0135 if (stroke && !stroke->deref()) 0136 delete stroke; 0137 if (shadow && !shadow->deref()) 0138 delete shadow; 0139 if (filterEffectStack && !filterEffectStack->deref()) 0140 delete filterEffectStack; 0141 delete clipPath; 0142 qDeleteAll(eventActions); 0143 } 0144 0145 void KoShapePrivate::shapeChanged(KoShape::ChangeType type) 0146 { 0147 Q_Q(KoShape); 0148 if (parent) 0149 parent->model()->childChanged(q, type); 0150 q->shapeChanged(type); 0151 foreach(KoShape * shape, dependees) 0152 shape->shapeChanged(type, q); 0153 } 0154 0155 void KoShapePrivate::updateStroke() 0156 { 0157 Q_Q(KoShape); 0158 if (stroke == 0) 0159 return; 0160 KoInsets insets; 0161 stroke->strokeInsets(q, insets); 0162 QSizeF inner = q->size(); 0163 // update left 0164 q->update(QRectF(-insets.left, -insets.top, insets.left, 0165 inner.height() + insets.top + insets.bottom)); 0166 // update top 0167 q->update(QRectF(-insets.left, -insets.top, 0168 inner.width() + insets.left + insets.right, insets.top)); 0169 // update right 0170 q->update(QRectF(inner.width(), -insets.top, insets.right, 0171 inner.height() + insets.top + insets.bottom)); 0172 // update bottom 0173 q->update(QRectF(-insets.left, inner.height(), 0174 inner.width() + insets.left + insets.right, insets.bottom)); 0175 } 0176 0177 void KoShapePrivate::addShapeManager(KoShapeManager *manager) 0178 { 0179 shapeManagers.insert(manager); 0180 } 0181 0182 void KoShapePrivate::removeShapeManager(KoShapeManager *manager) 0183 { 0184 shapeManagers.remove(manager); 0185 } 0186 0187 void KoShapePrivate::convertFromShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const 0188 { 0189 switch(point.alignment) { 0190 case KoConnectionPoint::AlignNone: 0191 point.position = KoFlake::toRelative(point.position, shapeSize); 0192 point.position.rx() = qBound<qreal>(0.0, point.position.x(), 1.0); 0193 point.position.ry() = qBound<qreal>(0.0, point.position.y(), 1.0); 0194 break; 0195 case KoConnectionPoint::AlignRight: 0196 point.position.rx() -= shapeSize.width(); 0197 case KoConnectionPoint::AlignLeft: 0198 point.position.ry() = 0.5*shapeSize.height(); 0199 break; 0200 case KoConnectionPoint::AlignBottom: 0201 point.position.ry() -= shapeSize.height(); 0202 case KoConnectionPoint::AlignTop: 0203 point.position.rx() = 0.5*shapeSize.width(); 0204 break; 0205 case KoConnectionPoint::AlignTopLeft: 0206 // nothing to do here 0207 break; 0208 case KoConnectionPoint::AlignTopRight: 0209 point.position.rx() -= shapeSize.width(); 0210 break; 0211 case KoConnectionPoint::AlignBottomLeft: 0212 point.position.ry() -= shapeSize.height(); 0213 break; 0214 case KoConnectionPoint::AlignBottomRight: 0215 point.position.rx() -= shapeSize.width(); 0216 point.position.ry() -= shapeSize.height(); 0217 break; 0218 case KoConnectionPoint::AlignCenter: 0219 point.position.rx() -= 0.5 * shapeSize.width(); 0220 point.position.ry() -= 0.5 * shapeSize.height(); 0221 break; 0222 } 0223 } 0224 0225 void KoShapePrivate::convertToShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const 0226 { 0227 switch(point.alignment) { 0228 case KoConnectionPoint::AlignNone: 0229 point.position = KoFlake::toAbsolute(point.position, shapeSize); 0230 break; 0231 case KoConnectionPoint::AlignRight: 0232 point.position.rx() += shapeSize.width(); 0233 case KoConnectionPoint::AlignLeft: 0234 point.position.ry() = 0.5*shapeSize.height(); 0235 break; 0236 case KoConnectionPoint::AlignBottom: 0237 point.position.ry() += shapeSize.height(); 0238 case KoConnectionPoint::AlignTop: 0239 point.position.rx() = 0.5*shapeSize.width(); 0240 break; 0241 case KoConnectionPoint::AlignTopLeft: 0242 // nothing to do here 0243 break; 0244 case KoConnectionPoint::AlignTopRight: 0245 point.position.rx() += shapeSize.width(); 0246 break; 0247 case KoConnectionPoint::AlignBottomLeft: 0248 point.position.ry() += shapeSize.height(); 0249 break; 0250 case KoConnectionPoint::AlignBottomRight: 0251 point.position.rx() += shapeSize.width(); 0252 point.position.ry() += shapeSize.height(); 0253 break; 0254 case KoConnectionPoint::AlignCenter: 0255 point.position.rx() += 0.5 * shapeSize.width(); 0256 point.position.ry() += 0.5 * shapeSize.height(); 0257 break; 0258 } 0259 } 0260 0261 // static 0262 QString KoShapePrivate::getStyleProperty(const char *property, KoShapeLoadingContext &context) 0263 { 0264 KoStyleStack &styleStack = context.odfLoadingContext().styleStack(); 0265 QString value; 0266 0267 if (styleStack.hasProperty(KoXmlNS::draw, property)) { 0268 value = styleStack.property(KoXmlNS::draw, property); 0269 } 0270 0271 return value; 0272 } 0273 0274 0275 0276 // ======== KoShape 0277 KoShape::KoShape() 0278 : d_ptr(new KoShapePrivate(this)) 0279 { 0280 notifyChanged(); 0281 } 0282 0283 KoShape::KoShape(KoShapePrivate &dd) 0284 : d_ptr(&dd) 0285 { 0286 } 0287 0288 KoShape::~KoShape() 0289 { 0290 Q_D(KoShape); 0291 d->shapeChanged(Deleted); 0292 delete d_ptr; 0293 } 0294 0295 void KoShape::scale(qreal sx, qreal sy) 0296 { 0297 Q_D(KoShape); 0298 QPointF pos = position(); 0299 QTransform scaleMatrix; 0300 scaleMatrix.translate(pos.x(), pos.y()); 0301 scaleMatrix.scale(sx, sy); 0302 scaleMatrix.translate(-pos.x(), -pos.y()); 0303 d->localMatrix = d->localMatrix * scaleMatrix; 0304 0305 notifyChanged(); 0306 d->shapeChanged(ScaleChanged); 0307 } 0308 0309 void KoShape::rotate(qreal angle) 0310 { 0311 Q_D(KoShape); 0312 QPointF center = d->localMatrix.map(QPointF(0.5 * size().width(), 0.5 * size().height())); 0313 QTransform rotateMatrix; 0314 rotateMatrix.translate(center.x(), center.y()); 0315 rotateMatrix.rotate(angle); 0316 rotateMatrix.translate(-center.x(), -center.y()); 0317 d->localMatrix = d->localMatrix * rotateMatrix; 0318 0319 notifyChanged(); 0320 d->shapeChanged(RotationChanged); 0321 } 0322 0323 void KoShape::shear(qreal sx, qreal sy) 0324 { 0325 Q_D(KoShape); 0326 QPointF pos = position(); 0327 QTransform shearMatrix; 0328 shearMatrix.translate(pos.x(), pos.y()); 0329 shearMatrix.shear(sx, sy); 0330 shearMatrix.translate(-pos.x(), -pos.y()); 0331 d->localMatrix = d->localMatrix * shearMatrix; 0332 0333 notifyChanged(); 0334 d->shapeChanged(ShearChanged); 0335 } 0336 0337 void KoShape::setSize(const QSizeF &newSize) 0338 { 0339 Q_D(KoShape); 0340 QSizeF oldSize(size()); 0341 0342 // always set size, as d->size and size() may vary 0343 d->size = newSize; 0344 0345 if (oldSize == newSize) 0346 return; 0347 0348 notifyChanged(); 0349 d->shapeChanged(SizeChanged); 0350 } 0351 0352 void KoShape::setPosition(const QPointF &newPosition) 0353 { 0354 Q_D(KoShape); 0355 QPointF currentPos = position(); 0356 if (newPosition == currentPos) 0357 return; 0358 QTransform translateMatrix; 0359 translateMatrix.translate(newPosition.x() - currentPos.x(), newPosition.y() - currentPos.y()); 0360 d->localMatrix = d->localMatrix * translateMatrix; 0361 0362 notifyChanged(); 0363 d->shapeChanged(PositionChanged); 0364 } 0365 0366 bool KoShape::hitTest(const QPointF &position) const 0367 { 0368 Q_D(const KoShape); 0369 if (d->parent && d->parent->isClipped(this) && !d->parent->hitTest(position)) 0370 return false; 0371 0372 QPointF point = absoluteTransformation(0).inverted().map(position); 0373 QRectF bb(QPointF(), size()); 0374 if (d->stroke) { 0375 KoInsets insets; 0376 d->stroke->strokeInsets(this, insets); 0377 bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom); 0378 } 0379 if (bb.contains(point)) 0380 return true; 0381 0382 // if there is no shadow we can as well just leave 0383 if (! d->shadow) 0384 return false; 0385 0386 // the shadow has an offset to the shape, so we simply 0387 // check if the position minus the shadow offset hits the shape 0388 point = absoluteTransformation(0).inverted().map(position - d->shadow->offset()); 0389 0390 return bb.contains(point); 0391 } 0392 0393 QRectF KoShape::boundingRect() const 0394 { 0395 Q_D(const KoShape); 0396 0397 QTransform transform = absoluteTransformation(0); 0398 QRectF bb = outlineRect(); 0399 if (d->stroke) { 0400 KoInsets insets; 0401 d->stroke->strokeInsets(this, insets); 0402 bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom); 0403 } 0404 bb = transform.mapRect(bb); 0405 if (d->shadow) { 0406 KoInsets insets; 0407 d->shadow->insets(insets); 0408 bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom); 0409 } 0410 if (d->filterEffectStack) { 0411 QRectF clipRect = d->filterEffectStack->clipRectForBoundingRect(outlineRect()); 0412 bb |= transform.mapRect(clipRect); 0413 } 0414 0415 return bb; 0416 } 0417 0418 QTransform KoShape::absoluteTransformation(const KoViewConverter *converter) const 0419 { 0420 Q_D(const KoShape); 0421 QTransform matrix; 0422 // apply parents matrix to inherit any transformations done there. 0423 KoShapeContainer * container = d->parent; 0424 if (container) { 0425 if (container->inheritsTransform(this)) { 0426 // We do need to pass the converter here, otherwise the parent's 0427 // translation is not inherited. 0428 matrix = container->absoluteTransformation(converter); 0429 } else { 0430 QSizeF containerSize = container->size(); 0431 QPointF containerPos = container->absolutePosition() - QPointF(0.5 * containerSize.width(), 0.5 * containerSize.height()); 0432 if (converter) 0433 containerPos = converter->documentToView(containerPos); 0434 matrix.translate(containerPos.x(), containerPos.y()); 0435 } 0436 } 0437 0438 if (converter) { 0439 QPointF pos = d->localMatrix.map(QPointF()); 0440 QPointF trans = converter->documentToView(pos) - pos; 0441 matrix.translate(trans.x(), trans.y()); 0442 } 0443 0444 return d->localMatrix * matrix; 0445 } 0446 0447 void KoShape::applyAbsoluteTransformation(const QTransform &matrix) 0448 { 0449 QTransform globalMatrix = absoluteTransformation(0); 0450 // the transformation is relative to the global coordinate system 0451 // but we want to change the local matrix, so convert the matrix 0452 // to be relative to the local coordinate system 0453 QTransform transformMatrix = globalMatrix * matrix * globalMatrix.inverted(); 0454 applyTransformation(transformMatrix); 0455 } 0456 0457 void KoShape::applyTransformation(const QTransform &matrix) 0458 { 0459 Q_D(KoShape); 0460 d->localMatrix = matrix * d->localMatrix; 0461 notifyChanged(); 0462 d->shapeChanged(GenericMatrixChange); 0463 } 0464 0465 void KoShape::setTransformation(const QTransform &matrix) 0466 { 0467 Q_D(KoShape); 0468 d->localMatrix = matrix; 0469 notifyChanged(); 0470 d->shapeChanged(GenericMatrixChange); 0471 } 0472 0473 QTransform KoShape::transformation() const 0474 { 0475 Q_D(const KoShape); 0476 return d->localMatrix; 0477 } 0478 0479 KoShape::ChildZOrderPolicy KoShape::childZOrderPolicy() 0480 { 0481 return ChildZDefault; 0482 } 0483 0484 bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2) 0485 { 0486 // First sort according to runThrough which is sort of a master level 0487 KoShape *parentShapeS1 = s1->parent(); 0488 KoShape *parentShapeS2 = s2->parent(); 0489 int runThrough1 = s1->runThrough(); 0490 int runThrough2 = s2->runThrough(); 0491 while (parentShapeS1) { 0492 if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) { 0493 runThrough1 = parentShapeS1->runThrough(); 0494 } else { 0495 runThrough1 = runThrough1 + parentShapeS1->runThrough(); 0496 } 0497 parentShapeS1 = parentShapeS1->parent(); 0498 } 0499 0500 while (parentShapeS2) { 0501 if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) { 0502 runThrough2 = parentShapeS2->runThrough(); 0503 } else { 0504 runThrough2 = runThrough2 + parentShapeS2->runThrough(); 0505 } 0506 parentShapeS2 = parentShapeS2->parent(); 0507 } 0508 0509 if (runThrough1 > runThrough2) { 0510 return false; 0511 } 0512 if (runThrough1 < runThrough2) { 0513 return true; 0514 } 0515 0516 // If on the same runThrough level then the zIndex is all that matters. 0517 // 0518 // We basically walk up through the parents until we find a common base parent 0519 // To do that we need two loops where the inner loop walks up through the parents 0520 // of s2 every time we step up one parent level on s1 0521 // 0522 // We don't update the index value until after we have seen that it's not a common base 0523 // That way we ensure that two children of a common base are sorted according to their respective 0524 // z value 0525 bool foundCommonParent = false; 0526 int index1 = s1->zIndex(); 0527 int index2 = s2->zIndex(); 0528 parentShapeS1 = s1; 0529 parentShapeS2 = s2; 0530 while (parentShapeS1 && !foundCommonParent) { 0531 parentShapeS2 = s2; 0532 index2 = parentShapeS2->zIndex(); 0533 while (parentShapeS2) { 0534 if (parentShapeS2 == parentShapeS1) { 0535 foundCommonParent = true; 0536 break; 0537 } 0538 if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) { 0539 index2 = parentShapeS2->zIndex(); 0540 } 0541 parentShapeS2 = parentShapeS2->parent(); 0542 } 0543 0544 if (!foundCommonParent) { 0545 if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) { 0546 index1 = parentShapeS1->zIndex(); 0547 } 0548 parentShapeS1 = parentShapeS1->parent(); 0549 } 0550 } 0551 0552 // If the one shape is a parent/child of the other then sort so. 0553 if (s1 == parentShapeS2) { 0554 return true; 0555 } 0556 if (s2 == parentShapeS1) { 0557 return false; 0558 } 0559 0560 // If we went that far then the z-Index is used for sorting. 0561 return index1 < index2; 0562 } 0563 0564 void KoShape::setParent(KoShapeContainer *parent) 0565 { 0566 Q_D(KoShape); 0567 if (d->parent == parent) 0568 return; 0569 KoShapeContainer *oldParent = d->parent; 0570 d->parent = 0; // avoids recursive removing 0571 if (oldParent) 0572 oldParent->removeShape(this); 0573 if (parent && parent != this) { 0574 d->parent = parent; 0575 parent->addShape(this); 0576 } 0577 notifyChanged(); 0578 d->shapeChanged(ParentChanged); 0579 } 0580 0581 int KoShape::zIndex() const 0582 { 0583 Q_D(const KoShape); 0584 return d->zIndex; 0585 } 0586 0587 void KoShape::update() const 0588 { 0589 Q_D(const KoShape); 0590 0591 if (!d->shapeManagers.empty()) { 0592 QRectF rect(boundingRect()); 0593 foreach(KoShapeManager * manager, d->shapeManagers) { 0594 manager->update(rect, this, true); 0595 } 0596 } 0597 } 0598 0599 void KoShape::update(const QRectF &rect) const 0600 { 0601 0602 if (rect.isEmpty() && !rect.isNull()) { 0603 return; 0604 } 0605 0606 Q_D(const KoShape); 0607 0608 if (!d->shapeManagers.empty() && isVisible()) { 0609 QRectF rc(absoluteTransformation(0).mapRect(rect)); 0610 foreach(KoShapeManager * manager, d->shapeManagers) { 0611 manager->update(rc); 0612 } 0613 } 0614 } 0615 0616 QPainterPath KoShape::outline() const 0617 { 0618 QPainterPath path; 0619 path.addRect(outlineRect()); 0620 return path; 0621 } 0622 0623 QRectF KoShape::outlineRect() const 0624 { 0625 const QSizeF s = size(); 0626 return QRectF(QPointF(0, 0), QSizeF(qMax(s.width(), qreal(0.0001)), 0627 qMax(s.height(), qreal(0.0001)))); 0628 } 0629 0630 QPainterPath KoShape::shadowOutline() const 0631 { 0632 Q_D(const KoShape); 0633 if (d->fill) { 0634 return outline(); 0635 } 0636 0637 return QPainterPath(); 0638 } 0639 0640 QPointF KoShape::absolutePosition(KoFlake::Position anchor) const 0641 { 0642 QPointF point; 0643 switch (anchor) { 0644 case KoFlake::TopLeftCorner: break; 0645 case KoFlake::TopRightCorner: point = QPointF(size().width(), 0.0); break; 0646 case KoFlake::BottomLeftCorner: point = QPointF(0.0, size().height()); break; 0647 case KoFlake::BottomRightCorner: point = QPointF(size().width(), size().height()); break; 0648 case KoFlake::CenteredPosition: point = QPointF(size().width() / 2.0, size().height() / 2.0); break; 0649 } 0650 return absoluteTransformation(0).map(point); 0651 } 0652 0653 void KoShape::setAbsolutePosition(const QPointF &newPosition, KoFlake::Position anchor) 0654 { 0655 Q_D(KoShape); 0656 QPointF currentAbsPosition = absolutePosition(anchor); 0657 QPointF translate = newPosition - currentAbsPosition; 0658 QTransform translateMatrix; 0659 translateMatrix.translate(translate.x(), translate.y()); 0660 applyAbsoluteTransformation(translateMatrix); 0661 notifyChanged(); 0662 d->shapeChanged(PositionChanged); 0663 } 0664 0665 void KoShape::copySettings(const KoShape *shape) 0666 { 0667 Q_D(KoShape); 0668 d->size = shape->size(); 0669 d->connectors.clear(); 0670 foreach(const KoConnectionPoint &point, shape->connectionPoints()) 0671 addConnectionPoint(point); 0672 d->zIndex = shape->zIndex(); 0673 d->visible = shape->isVisible(); 0674 0675 // Ensure printable is true by default 0676 if (!d->visible) 0677 d->printable = true; 0678 else 0679 d->printable = shape->isPrintable(); 0680 0681 d->allowedInteractions = shape->allowedInteractions(); 0682 d->keepAspect = shape->keepAspectRatio(); 0683 d->localMatrix = shape->d_ptr->localMatrix; 0684 } 0685 0686 void KoShape::notifyChanged() 0687 { 0688 Q_D(KoShape); 0689 foreach(KoShapeManager * manager, d->shapeManagers) { 0690 manager->notifyShapeChanged(this); 0691 } 0692 } 0693 0694 void KoShape::setUserData(KoShapeUserData *userData) 0695 { 0696 Q_D(KoShape); 0697 delete d->userData; 0698 d->userData = userData; 0699 } 0700 0701 KoShapeUserData *KoShape::userData() const 0702 { 0703 Q_D(const KoShape); 0704 return d->userData; 0705 } 0706 0707 void KoShape::setApplicationData(KoShapeApplicationData *appData) 0708 { 0709 Q_D(KoShape); 0710 // appdata is deleted by the application. 0711 d->appData = appData; 0712 } 0713 0714 KoShapeApplicationData *KoShape::applicationData() const 0715 { 0716 Q_D(const KoShape); 0717 return d->appData; 0718 } 0719 0720 bool KoShape::hasTransparency() const 0721 { 0722 Q_D(const KoShape); 0723 if (! d->fill) 0724 return true; 0725 else 0726 return d->fill->hasTransparency() || d->transparency > 0.0; 0727 } 0728 0729 void KoShape::setTransparency(qreal transparency) 0730 { 0731 Q_D(KoShape); 0732 d->transparency = qBound<qreal>(0.0, transparency, 1.0); 0733 } 0734 0735 qreal KoShape::transparency(bool recursive) const 0736 { 0737 Q_D(const KoShape); 0738 if (!recursive || !parent()) { 0739 return d->transparency; 0740 } else { 0741 const qreal parentOpacity = 1.0-parent()->transparency(recursive); 0742 const qreal childOpacity = 1.0-d->transparency; 0743 return 1.0-(parentOpacity*childOpacity); 0744 } 0745 } 0746 0747 KoInsets KoShape::strokeInsets() const 0748 { 0749 Q_D(const KoShape); 0750 KoInsets answer; 0751 if (d->stroke) 0752 d->stroke->strokeInsets(this, answer); 0753 return answer; 0754 } 0755 0756 qreal KoShape::rotation() const 0757 { 0758 Q_D(const KoShape); 0759 // try to extract the rotation angle out of the local matrix 0760 // if it is a pure rotation matrix 0761 0762 // check if the matrix has shearing mixed in 0763 if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10) 0764 return std::numeric_limits<qreal>::quiet_NaN(); 0765 // check if the matrix has scaling mixed in 0766 if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10) 0767 return std::numeric_limits<qreal>::quiet_NaN(); 0768 0769 // calculate the angle from the matrix elements 0770 qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI; 0771 if (angle < 0.0) 0772 angle += 360.0; 0773 0774 return angle; 0775 } 0776 0777 QSizeF KoShape::size() const 0778 { 0779 Q_D(const KoShape); 0780 return d->size; 0781 } 0782 0783 QPointF KoShape::position() const 0784 { 0785 Q_D(const KoShape); 0786 QPointF center(0.5*size().width(), 0.5*size().height()); 0787 return d->localMatrix.map(center) - center; 0788 } 0789 0790 int KoShape::addConnectionPoint(const KoConnectionPoint &point) 0791 { 0792 Q_D(KoShape); 0793 0794 // get next glue point id 0795 int nextConnectionPointId = KoConnectionPoint::FirstCustomConnectionPoint; 0796 if (d->connectors.size()) 0797 nextConnectionPointId = qMax(nextConnectionPointId, (--d->connectors.end()).key()+1); 0798 0799 KoConnectionPoint p = point; 0800 d->convertFromShapeCoordinates(p, size()); 0801 d->connectors[nextConnectionPointId] = p; 0802 0803 return nextConnectionPointId; 0804 } 0805 0806 bool KoShape::setConnectionPoint(int connectionPointId, const KoConnectionPoint &point) 0807 { 0808 Q_D(KoShape); 0809 if (connectionPointId < 0) 0810 return false; 0811 0812 const bool insertPoint = !hasConnectionPoint(connectionPointId); 0813 0814 switch(connectionPointId) { 0815 case KoConnectionPoint::TopConnectionPoint: 0816 case KoConnectionPoint::RightConnectionPoint: 0817 case KoConnectionPoint::BottomConnectionPoint: 0818 case KoConnectionPoint::LeftConnectionPoint: 0819 { 0820 KoConnectionPoint::PointId id = static_cast<KoConnectionPoint::PointId>(connectionPointId); 0821 d->connectors[id] = KoConnectionPoint::defaultConnectionPoint(id); 0822 break; 0823 } 0824 default: 0825 { 0826 KoConnectionPoint p = point; 0827 d->convertFromShapeCoordinates(p, size()); 0828 d->connectors[connectionPointId] = p; 0829 break; 0830 } 0831 } 0832 0833 if(!insertPoint) 0834 d->shapeChanged(ConnectionPointChanged); 0835 0836 return true; 0837 } 0838 0839 bool KoShape::hasConnectionPoint(int connectionPointId) const 0840 { 0841 Q_D(const KoShape); 0842 return d->connectors.contains(connectionPointId); 0843 } 0844 0845 KoConnectionPoint KoShape::connectionPoint(int connectionPointId) const 0846 { 0847 Q_D(const KoShape); 0848 KoConnectionPoint p = d->connectors.value(connectionPointId, KoConnectionPoint()); 0849 // convert glue point to shape coordinates 0850 d->convertToShapeCoordinates(p, size()); 0851 return p; 0852 } 0853 0854 KoConnectionPoints KoShape::connectionPoints() const 0855 { 0856 Q_D(const KoShape); 0857 QSizeF s = size(); 0858 KoConnectionPoints points = d->connectors; 0859 KoConnectionPoints::iterator point = points.begin(); 0860 KoConnectionPoints::iterator lastPoint = points.end(); 0861 // convert glue points to shape coordinates 0862 for(; point != lastPoint; ++point) { 0863 d->convertToShapeCoordinates(point.value(), s); 0864 } 0865 0866 return points; 0867 } 0868 0869 void KoShape::removeConnectionPoint(int connectionPointId) 0870 { 0871 Q_D(KoShape); 0872 d->connectors.remove(connectionPointId); 0873 d->shapeChanged(ConnectionPointChanged); 0874 } 0875 0876 void KoShape::clearConnectionPoints() 0877 { 0878 Q_D(KoShape); 0879 d->connectors.clear(); 0880 } 0881 0882 void KoShape::addEventAction(KoEventAction *action) 0883 { 0884 Q_D(KoShape); 0885 d->eventActions.insert(action); 0886 } 0887 0888 void KoShape::removeEventAction(KoEventAction *action) 0889 { 0890 Q_D(KoShape); 0891 d->eventActions.remove(action); 0892 } 0893 0894 QSet<KoEventAction *> KoShape::eventActions() const 0895 { 0896 Q_D(const KoShape); 0897 return d->eventActions; 0898 } 0899 0900 KoShape::TextRunAroundSide KoShape::textRunAroundSide() const 0901 { 0902 Q_D(const KoShape); 0903 return d->textRunAroundSide; 0904 } 0905 0906 void KoShape::setTextRunAroundSide(TextRunAroundSide side, RunThroughLevel runThrought) 0907 { 0908 Q_D(KoShape); 0909 0910 if (side == RunThrough) { 0911 if (runThrought == Background) { 0912 setRunThrough(-1); 0913 } else { 0914 setRunThrough(1); 0915 } 0916 } else { 0917 setRunThrough(0); 0918 } 0919 0920 if ( d->textRunAroundSide == side) { 0921 return; 0922 } 0923 0924 d->textRunAroundSide = side; 0925 notifyChanged(); 0926 d->shapeChanged(TextRunAroundChanged); 0927 } 0928 0929 qreal KoShape::textRunAroundDistanceTop() const 0930 { 0931 Q_D(const KoShape); 0932 return d->textRunAroundDistanceTop; 0933 } 0934 0935 void KoShape::setTextRunAroundDistanceTop(qreal distance) 0936 { 0937 Q_D(KoShape); 0938 d->textRunAroundDistanceTop = distance; 0939 } 0940 0941 qreal KoShape::textRunAroundDistanceLeft() const 0942 { 0943 Q_D(const KoShape); 0944 return d->textRunAroundDistanceLeft; 0945 } 0946 0947 void KoShape::setTextRunAroundDistanceLeft(qreal distance) 0948 { 0949 Q_D(KoShape); 0950 d->textRunAroundDistanceLeft = distance; 0951 } 0952 0953 qreal KoShape::textRunAroundDistanceRight() const 0954 { 0955 Q_D(const KoShape); 0956 return d->textRunAroundDistanceRight; 0957 } 0958 0959 void KoShape::setTextRunAroundDistanceRight(qreal distance) 0960 { 0961 Q_D(KoShape); 0962 d->textRunAroundDistanceRight = distance; 0963 } 0964 0965 qreal KoShape::textRunAroundDistanceBottom() const 0966 { 0967 Q_D(const KoShape); 0968 return d->textRunAroundDistanceBottom; 0969 } 0970 0971 void KoShape::setTextRunAroundDistanceBottom(qreal distance) 0972 { 0973 Q_D(KoShape); 0974 d->textRunAroundDistanceBottom = distance; 0975 } 0976 0977 qreal KoShape::textRunAroundThreshold() const 0978 { 0979 Q_D(const KoShape); 0980 return d->textRunAroundThreshold; 0981 } 0982 0983 void KoShape::setTextRunAroundThreshold(qreal threshold) 0984 { 0985 Q_D(KoShape); 0986 d->textRunAroundThreshold = threshold; 0987 } 0988 0989 KoShape::TextRunAroundContour KoShape::textRunAroundContour() const 0990 { 0991 Q_D(const KoShape); 0992 return d->textRunAroundContour; 0993 } 0994 0995 void KoShape::setTextRunAroundContour(KoShape::TextRunAroundContour contour) 0996 { 0997 Q_D(KoShape); 0998 d->textRunAroundContour = contour; 0999 } 1000 1001 void KoShape::setAnchor(KoShapeAnchor *anchor) 1002 { 1003 Q_D(KoShape); 1004 d->anchor = anchor; 1005 } 1006 1007 KoShapeAnchor *KoShape::anchor() const 1008 { 1009 Q_D(const KoShape); 1010 return d->anchor; 1011 } 1012 1013 void KoShape::setMinimumHeight(qreal height) 1014 { 1015 Q_D(KoShape); 1016 d->minimumHeight = height; 1017 } 1018 1019 qreal KoShape::minimumHeight() const 1020 { 1021 Q_D(const KoShape); 1022 return d->minimumHeight; 1023 } 1024 1025 1026 void KoShape::setBackground(QSharedPointer<KoShapeBackground> fill) 1027 { 1028 Q_D(KoShape); 1029 d->fill = fill; 1030 d->shapeChanged(BackgroundChanged); 1031 notifyChanged(); 1032 } 1033 1034 QSharedPointer<KoShapeBackground> KoShape::background() const 1035 { 1036 Q_D(const KoShape); 1037 return d->fill; 1038 } 1039 1040 void KoShape::setZIndex(int zIndex) 1041 { 1042 Q_D(KoShape); 1043 if (d->zIndex == zIndex) 1044 return; 1045 d->zIndex = zIndex; 1046 notifyChanged(); 1047 } 1048 1049 int KoShape::runThrough() 1050 { 1051 Q_D(const KoShape); 1052 return d->runThrough; 1053 } 1054 1055 void KoShape::setRunThrough(short int runThrough) 1056 { 1057 Q_D(KoShape); 1058 d->runThrough = runThrough; 1059 } 1060 1061 void KoShape::setVisible(bool on) 1062 { 1063 Q_D(KoShape); 1064 int _on = (on ? 1 : 0); 1065 if (d->visible == _on) return; 1066 d->visible = _on; 1067 } 1068 1069 bool KoShape::isVisible(bool recursive) const 1070 { 1071 Q_D(const KoShape); 1072 if (! recursive) 1073 return d->visible; 1074 if (recursive && ! d->visible) 1075 return false; 1076 1077 KoShapeContainer * parentShape = parent(); 1078 while (parentShape) { 1079 if (! parentShape->isVisible()) 1080 return false; 1081 parentShape = parentShape->parent(); 1082 } 1083 return true; 1084 } 1085 1086 void KoShape::setPrintable(bool on) 1087 { 1088 Q_D(KoShape); 1089 d->printable = on; 1090 } 1091 1092 bool KoShape::isPrintable() const 1093 { 1094 Q_D(const KoShape); 1095 if (d->visible) 1096 return d->printable; 1097 else 1098 return false; 1099 } 1100 1101 void KoShape::setSelectable(bool selectable) 1102 { 1103 Q_D(KoShape); 1104 #if QT_VERSION >= 0x050700 1105 d->allowedInteractions.setFlag(SelectionAllowed, selectable); 1106 #else 1107 selectable ? (d->allowedInteractions |= SelectionAllowed) : (d->allowedInteractions &= ~SelectionAllowed); 1108 #endif 1109 } 1110 1111 bool KoShape::isSelectable() const 1112 { 1113 Q_D(const KoShape); 1114 return d->allowedInteractions.testFlag(SelectionAllowed); 1115 } 1116 1117 void KoShape::setGeometryProtected(bool on) 1118 { 1119 Q_D(KoShape); 1120 #if QT_VERSION >= 0x050700 1121 d->allowedInteractions.setFlag(MoveAllowed, !on); 1122 d->allowedInteractions.setFlag(ResizeAllowed, !on); 1123 #else 1124 (!on) ? (d->allowedInteractions |= MoveAllowed) : (d->allowedInteractions &= ~MoveAllowed); 1125 (!on) ? (d->allowedInteractions |= ResizeAllowed) : (d->allowedInteractions &= ~ResizeAllowed); 1126 #endif 1127 } 1128 1129 bool KoShape::isGeometryProtected() const 1130 { 1131 Q_D(const KoShape); 1132 return !d->allowedInteractions.testFlag(MoveAllowed) || !d->allowedInteractions.testFlag(ResizeAllowed); 1133 } 1134 1135 void KoShape::setContentProtected(bool protect) 1136 { 1137 Q_D(KoShape); 1138 #if QT_VERSION >= 0x050700 1139 d->allowedInteractions.setFlag(ContentChangeAllowed, !protect); 1140 #else 1141 (!protect) ? (d->allowedInteractions |= ContentChangeAllowed) : (d->allowedInteractions &= ~ContentChangeAllowed); 1142 #endif 1143 } 1144 1145 bool KoShape::isContentProtected() const 1146 { 1147 Q_D(const KoShape); 1148 return !d->allowedInteractions.testFlag(ContentChangeAllowed); 1149 } 1150 1151 void KoShape::setDeletable(bool deletable) 1152 { 1153 Q_D(KoShape); 1154 #if QT_VERSION >= 0x050700 1155 d->allowedInteractions.setFlag(DeletionAllowed, deletable); 1156 #else 1157 deletable ? (d->allowedInteractions |= DeletionAllowed) : (d->allowedInteractions &= ~DeletionAllowed); 1158 #endif 1159 } 1160 1161 bool KoShape::isDeletable() const 1162 { 1163 Q_D(const KoShape); 1164 return d->allowedInteractions.testFlag(DeletionAllowed); 1165 } 1166 1167 void KoShape::setAllowedInteraction(KoShape::AllowedInteraction flag, bool value) 1168 { 1169 Q_D(KoShape); 1170 #if QT_VERSION >= 0x050700 1171 d->allowedInteractions.setFlag(flag, value); 1172 #else 1173 value ? (d->allowedInteractions |= flag) : (d->allowedInteractions &= ~flag); 1174 #endif 1175 } 1176 1177 bool KoShape::allowedInteraction(KoShape::AllowedInteraction flag, bool recursive) const 1178 { 1179 return allowedInteractions(recursive).testFlag(flag); 1180 } 1181 1182 void KoShape::setAllowedInteractions(KoShape::AllowedInteractions interactions) 1183 { 1184 Q_D(KoShape); 1185 d->allowedInteractions = interactions; 1186 } 1187 1188 KoShape::AllowedInteractions KoShape::allowedInteractions(bool recursive) const 1189 { 1190 Q_D(const KoShape); 1191 if (!recursive) { 1192 return d->allowedInteractions; 1193 } 1194 AllowedInteractions state; 1195 if (!d->visible) { 1196 return state; 1197 } 1198 state = d->allowedInteractions; 1199 if (state && d->parent) { 1200 state &= d->parent->allowedInteractions(this); 1201 } 1202 return state; 1203 } 1204 1205 KoShapeContainer *KoShape::parent() const 1206 { 1207 Q_D(const KoShape); 1208 return d->parent; 1209 } 1210 1211 void KoShape::setKeepAspectRatio(bool keepAspect) 1212 { 1213 Q_D(KoShape); 1214 d->keepAspect = keepAspect; 1215 } 1216 1217 bool KoShape::keepAspectRatio() const 1218 { 1219 Q_D(const KoShape); 1220 return d->keepAspect; 1221 } 1222 1223 QString KoShape::shapeId() const 1224 { 1225 Q_D(const KoShape); 1226 return d->shapeId; 1227 } 1228 1229 void KoShape::setShapeId(const QString &id) 1230 { 1231 Q_D(KoShape); 1232 d->shapeId = id; 1233 } 1234 1235 void KoShape::setCollisionDetection(bool detect) 1236 { 1237 Q_D(KoShape); 1238 d->detectCollision = detect; 1239 } 1240 1241 bool KoShape::collisionDetection() 1242 { 1243 Q_D(KoShape); 1244 return d->detectCollision; 1245 } 1246 1247 KoShapeStrokeModel *KoShape::stroke() const 1248 { 1249 Q_D(const KoShape); 1250 return d->stroke; 1251 } 1252 1253 void KoShape::setStroke(KoShapeStrokeModel *stroke) 1254 { 1255 Q_D(KoShape); 1256 if (stroke) 1257 stroke->ref(); 1258 d->updateStroke(); 1259 if (d->stroke) 1260 d->stroke->deref(); 1261 d->stroke = stroke; 1262 d->updateStroke(); 1263 d->shapeChanged(StrokeChanged); 1264 notifyChanged(); 1265 } 1266 1267 void KoShape::setShadow(KoShapeShadow *shadow) 1268 { 1269 Q_D(KoShape); 1270 if (d->shadow) 1271 d->shadow->deref(); 1272 d->shadow = shadow; 1273 if (d->shadow) { 1274 d->shadow->ref(); 1275 // TODO update changed area 1276 } 1277 d->shapeChanged(ShadowChanged); 1278 notifyChanged(); 1279 } 1280 1281 KoShapeShadow *KoShape::shadow() const 1282 { 1283 Q_D(const KoShape); 1284 return d->shadow; 1285 } 1286 1287 void KoShape::setBorder(KoBorder *border) 1288 { 1289 Q_D(KoShape); 1290 if (d->border) { 1291 // The shape owns the border. 1292 delete d->border; 1293 } 1294 d->border = border; 1295 d->shapeChanged(BorderChanged); 1296 notifyChanged(); 1297 } 1298 1299 KoBorder *KoShape::border() const 1300 { 1301 Q_D(const KoShape); 1302 return d->border; 1303 } 1304 1305 void KoShape::setClipPath(KoClipPath *clipPath) 1306 { 1307 Q_D(KoShape); 1308 d->clipPath = clipPath; 1309 d->shapeChanged(ClipPathChanged); 1310 notifyChanged(); 1311 } 1312 1313 KoClipPath * KoShape::clipPath() const 1314 { 1315 Q_D(const KoShape); 1316 return d->clipPath; 1317 } 1318 1319 QTransform KoShape::transform() const 1320 { 1321 Q_D(const KoShape); 1322 return d->localMatrix; 1323 } 1324 1325 QString KoShape::name() const 1326 { 1327 Q_D(const KoShape); 1328 return d->name; 1329 } 1330 1331 void KoShape::setName(const QString &name) 1332 { 1333 Q_D(KoShape); 1334 d->name = name; 1335 } 1336 1337 void KoShape::waitUntilReady(const KoViewConverter &converter, bool asynchronous) const 1338 { 1339 Q_UNUSED(converter); 1340 Q_UNUSED(asynchronous); 1341 } 1342 1343 bool KoShape::isEditable() const 1344 { 1345 Q_D(const KoShape); 1346 if (!d->visible || isGeometryProtected()) 1347 return false; 1348 1349 if (d->parent && d->parent->isChildLocked(this)) 1350 return false; 1351 1352 return true; 1353 } 1354 1355 // painting 1356 void KoShape::paintBorder(QPainter &painter, const KoViewConverter &converter) 1357 { 1358 Q_UNUSED(converter); 1359 KoBorder *bd = border(); 1360 if (!bd) { 1361 return; 1362 } 1363 1364 QRectF borderRect = QRectF(QPointF(0, 0), size()); 1365 // Paint the border. 1366 bd->paint(painter, borderRect, KoBorder::PaintInsideLine); 1367 } 1368 1369 1370 // loading & saving methods 1371 QString KoShape::saveStyle(KoGenStyle &style, KoShapeSavingContext &context) const 1372 { 1373 Q_D(const KoShape); 1374 // and fill the style 1375 KoShapeStrokeModel *sm = stroke(); 1376 if (sm) { 1377 sm->fillStyle(style, context); 1378 } 1379 else { 1380 style.addProperty("draw:stroke", "none", KoGenStyle::GraphicType); 1381 } 1382 KoShapeShadow *s = shadow(); 1383 if (s) 1384 s->fillStyle(style, context); 1385 1386 QSharedPointer<KoShapeBackground> bg = background(); 1387 if (bg) { 1388 bg->fillStyle(style, context); 1389 } 1390 else { 1391 style.addProperty("draw:fill", "none", KoGenStyle::GraphicType); 1392 } 1393 1394 KoBorder *b = border(); 1395 if (b) { 1396 b->saveOdf(style); 1397 } 1398 1399 if (context.isSet(KoShapeSavingContext::AutoStyleInStyleXml)) { 1400 style.setAutoStyleInStylesDotXml(true); 1401 } 1402 1403 QString value; 1404 if (!d->allowedInteractions.testFlag(MoveAllowed)) { 1405 value = "position"; 1406 } 1407 if (!d->allowedInteractions.testFlag(ResizeAllowed)) { 1408 if (! value.isEmpty()) 1409 value += ' '; 1410 value += "size"; 1411 } 1412 if (isContentProtected()) { 1413 if (! value.isEmpty()) 1414 value += ' '; 1415 value += "content"; 1416 } 1417 if (!value.isEmpty()) { 1418 style.addProperty("style:protect", value, KoGenStyle::GraphicType); 1419 } 1420 1421 QMap<QByteArray, QString>::const_iterator it(d->additionalStyleAttributes.constBegin()); 1422 for (; it != d->additionalStyleAttributes.constEnd(); ++it) { 1423 style.addProperty(it.key(), it.value()); 1424 } 1425 1426 if (parent() && parent()->isClipped(this)) { 1427 /* 1428 * In Calligra clipping is done using a parent shape which can be rotated, sheared etc 1429 * and even non-square. So the ODF interoperability version we write here is really 1430 * just a very simple version of that... 1431 */ 1432 qreal top = -position().y(); 1433 qreal left = -position().x(); 1434 qreal right = parent()->size().width() - size().width() - left; 1435 qreal bottom = parent()->size().height() - size().height() - top; 1436 1437 style.addProperty("fo:clip", QString("rect(%1pt, %2pt, %3pt, %4pt)") 1438 .arg(top, 10, 'f').arg(right, 10, 'f') 1439 .arg(bottom, 10, 'f').arg(left, 10, 'f'), KoGenStyle::GraphicType); 1440 1441 } 1442 1443 QString wrap; 1444 switch (textRunAroundSide()) { 1445 case BiggestRunAroundSide: 1446 wrap = "biggest"; 1447 break; 1448 case LeftRunAroundSide: 1449 wrap = "left"; 1450 break; 1451 case RightRunAroundSide: 1452 wrap = "right"; 1453 break; 1454 case EnoughRunAroundSide: 1455 wrap = "dynamic"; 1456 break; 1457 case BothRunAroundSide: 1458 wrap = "parallel"; 1459 break; 1460 case NoRunAround: 1461 wrap = "none"; 1462 break; 1463 case RunThrough: 1464 wrap = "run-through"; 1465 break; 1466 } 1467 style.addProperty("style:wrap", wrap, KoGenStyle::GraphicType); 1468 switch (textRunAroundContour()) { 1469 case ContourBox: 1470 style.addProperty("style:wrap-contour", "false", KoGenStyle::GraphicType); 1471 break; 1472 case ContourFull: 1473 style.addProperty("style:wrap-contour", "true", KoGenStyle::GraphicType); 1474 style.addProperty("style:wrap-contour-mode", "full", KoGenStyle::GraphicType); 1475 break; 1476 case ContourOutside: 1477 style.addProperty("style:wrap-contour", "true", KoGenStyle::GraphicType); 1478 style.addProperty("style:wrap-contour-mode", "outside", KoGenStyle::GraphicType); 1479 break; 1480 } 1481 style.addPropertyPt("style:wrap-dynamic-threshold", textRunAroundThreshold(), KoGenStyle::GraphicType); 1482 if ((textRunAroundDistanceLeft() == textRunAroundDistanceRight()) 1483 && (textRunAroundDistanceTop() == textRunAroundDistanceBottom()) 1484 && (textRunAroundDistanceLeft() == textRunAroundDistanceTop())) { 1485 style.addPropertyPt("fo:margin", textRunAroundDistanceLeft(), KoGenStyle::GraphicType); 1486 } else { 1487 style.addPropertyPt("fo:margin-left", textRunAroundDistanceLeft(), KoGenStyle::GraphicType); 1488 style.addPropertyPt("fo:margin-top", textRunAroundDistanceTop(), KoGenStyle::GraphicType); 1489 style.addPropertyPt("fo:margin-right", textRunAroundDistanceRight(), KoGenStyle::GraphicType); 1490 style.addPropertyPt("fo:margin-bottom", textRunAroundDistanceBottom(), KoGenStyle::GraphicType); 1491 } 1492 1493 return context.mainStyles().insert(style, context.styleFamily()); 1494 } 1495 1496 void KoShape::loadStyle(const KoXmlElement &element, KoShapeLoadingContext &context) 1497 { 1498 Q_D(KoShape); 1499 1500 KoStyleStack &styleStack = context.odfLoadingContext().styleStack(); 1501 styleStack.setTypeProperties("graphic"); 1502 1503 d->fill.clear(); 1504 if (d->stroke && !d->stroke->deref()) { 1505 delete d->stroke; 1506 d->stroke = 0; 1507 } 1508 if (d->shadow && !d->shadow->deref()) { 1509 delete d->shadow; 1510 d->shadow = 0; 1511 } 1512 setBackground(loadOdfFill(context)); 1513 setStroke(loadOdfStroke(element, context)); 1514 setShadow(d->loadOdfShadow(context)); 1515 setBorder(d->loadOdfBorder(context)); 1516 1517 QString protect(styleStack.property(KoXmlNS::style, "protect")); 1518 #if QT_VERSION >= 0x050700 1519 d->allowedInteractions.setFlag(MoveAllowed, !protect.contains("position")); 1520 d->allowedInteractions.setFlag(ResizeAllowed, !protect.contains("size")); 1521 #else 1522 (!protect.contains("position")) ? (d->allowedInteractions |= MoveAllowed) : (d->allowedInteractions &= ~MoveAllowed); 1523 (!protect.contains("size")) ? (d->allowedInteractions |= ResizeAllowed) : (d->allowedInteractions &= ~ResizeAllowed); 1524 #endif 1525 setContentProtected(protect.contains("content")); 1526 1527 QString margin = styleStack.property(KoXmlNS::fo, "margin"); 1528 if (!margin.isEmpty()) { 1529 setTextRunAroundDistanceLeft(KoUnit::parseValue(margin)); 1530 setTextRunAroundDistanceTop(KoUnit::parseValue(margin)); 1531 setTextRunAroundDistanceRight(KoUnit::parseValue(margin)); 1532 setTextRunAroundDistanceBottom(KoUnit::parseValue(margin)); 1533 } 1534 margin = styleStack.property(KoXmlNS::fo, "margin-left"); 1535 if (!margin.isEmpty()) { 1536 setTextRunAroundDistanceLeft(KoUnit::parseValue(margin)); 1537 } 1538 1539 margin = styleStack.property(KoXmlNS::fo, "margin-top"); 1540 if (!margin.isEmpty()) { 1541 setTextRunAroundDistanceTop(KoUnit::parseValue(margin)); 1542 } 1543 margin = styleStack.property(KoXmlNS::fo, "margin-right"); 1544 if (!margin.isEmpty()) { 1545 setTextRunAroundDistanceRight(KoUnit::parseValue(margin)); 1546 } 1547 margin = styleStack.property(KoXmlNS::fo, "margin-bottom"); 1548 if (!margin.isEmpty()) { 1549 setTextRunAroundDistanceBottom(KoUnit::parseValue(margin)); 1550 } 1551 1552 QString wrap; 1553 if (styleStack.hasProperty(KoXmlNS::style, "wrap")) { 1554 wrap = styleStack.property(KoXmlNS::style, "wrap"); 1555 } else { 1556 // no value given in the file, but guess biggest 1557 wrap = "biggest"; 1558 } 1559 if (wrap == "none") { 1560 setTextRunAroundSide(KoShape::NoRunAround); 1561 } else if (wrap == "run-through") { 1562 QString runTrought = styleStack.property(KoXmlNS::style, "run-through", "background"); 1563 if (runTrought == "background") { 1564 setTextRunAroundSide(KoShape::RunThrough, KoShape::Background); 1565 } else { 1566 setTextRunAroundSide(KoShape::RunThrough, KoShape::Foreground); 1567 } 1568 } else { 1569 if (wrap == "biggest") 1570 setTextRunAroundSide(KoShape::BiggestRunAroundSide); 1571 else if (wrap == "left") 1572 setTextRunAroundSide(KoShape::LeftRunAroundSide); 1573 else if (wrap == "right") 1574 setTextRunAroundSide(KoShape::RightRunAroundSide); 1575 else if (wrap == "dynamic") 1576 setTextRunAroundSide(KoShape::EnoughRunAroundSide); 1577 else if (wrap == "parallel") 1578 setTextRunAroundSide(KoShape::BothRunAroundSide); 1579 } 1580 1581 if (styleStack.hasProperty(KoXmlNS::style, "wrap-dynamic-threshold")) { 1582 QString wrapThreshold = styleStack.property(KoXmlNS::style, "wrap-dynamic-threshold"); 1583 if (!wrapThreshold.isEmpty()) { 1584 setTextRunAroundThreshold(KoUnit::parseValue(wrapThreshold)); 1585 } 1586 } 1587 if (styleStack.property(KoXmlNS::style, "wrap-contour", "false") == "true") { 1588 if (styleStack.property(KoXmlNS::style, "wrap-contour-mode", "full") == "full") { 1589 setTextRunAroundContour(KoShape::ContourFull); 1590 } else { 1591 setTextRunAroundContour(KoShape::ContourOutside); 1592 } 1593 } else { 1594 setTextRunAroundContour(KoShape::ContourBox); 1595 } 1596 } 1597 1598 bool KoShape::loadOdfAttributes(const KoXmlElement &element, KoShapeLoadingContext &context, int attributes) 1599 { 1600 Q_D(KoShape); 1601 if (attributes & OdfPosition) { 1602 QPointF pos(position()); 1603 if (element.hasAttributeNS(KoXmlNS::svg, "x")) 1604 pos.setX(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "x", QString()))); 1605 if (element.hasAttributeNS(KoXmlNS::svg, "y")) 1606 pos.setY(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "y", QString()))); 1607 setPosition(pos); 1608 } 1609 1610 if (attributes & OdfSize) { 1611 QSizeF s(size()); 1612 if (element.hasAttributeNS(KoXmlNS::svg, "width")) 1613 s.setWidth(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "width", QString()))); 1614 if (element.hasAttributeNS(KoXmlNS::svg, "height")) 1615 s.setHeight(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "height", QString()))); 1616 setSize(s); 1617 } 1618 1619 if (attributes & OdfLayer) { 1620 if (element.hasAttributeNS(KoXmlNS::draw, "layer")) { 1621 KoShapeLayer *layer = context.layer(element.attributeNS(KoXmlNS::draw, "layer")); 1622 if (layer) { 1623 setParent(layer); 1624 } 1625 } 1626 } 1627 1628 if (attributes & OdfId) { 1629 KoElementReference ref; 1630 ref.loadOdf(element); 1631 if (ref.isValid()) { 1632 context.addShapeId(this, ref.toString()); 1633 } 1634 } 1635 1636 if (attributes & OdfZIndex) { 1637 if (element.hasAttributeNS(KoXmlNS::draw, "z-index")) { 1638 setZIndex(element.attributeNS(KoXmlNS::draw, "z-index").toInt()); 1639 } else { 1640 setZIndex(context.zIndex()); 1641 } 1642 } 1643 1644 if (attributes & OdfName) { 1645 if (element.hasAttributeNS(KoXmlNS::draw, "name")) { 1646 setName(element.attributeNS(KoXmlNS::draw, "name")); 1647 } 1648 } 1649 1650 if (attributes & OdfStyle) { 1651 KoStyleStack &styleStack = context.odfLoadingContext().styleStack(); 1652 styleStack.save(); 1653 if (element.hasAttributeNS(KoXmlNS::draw, "style-name")) { 1654 context.odfLoadingContext().fillStyleStack(element, KoXmlNS::draw, "style-name", "graphic"); 1655 } 1656 if (element.hasAttributeNS(KoXmlNS::presentation, "style-name")) { 1657 context.odfLoadingContext().fillStyleStack(element, KoXmlNS::presentation, "style-name", "presentation"); 1658 } 1659 loadStyle(element, context); 1660 1661 styleStack.restore(); 1662 } 1663 1664 if (attributes & OdfTransformation) { 1665 QString transform = element.attributeNS(KoXmlNS::draw, "transform", QString()); 1666 if (! transform.isEmpty()) 1667 applyAbsoluteTransformation(parseOdfTransform(transform, context)); 1668 } 1669 1670 if (attributes & OdfAdditionalAttributes) { 1671 QSet<KoShapeLoadingContext::AdditionalAttributeData> additionalAttributeData = KoShapeLoadingContext::additionalAttributeData(); 1672 foreach(const KoShapeLoadingContext::AdditionalAttributeData &attributeData, additionalAttributeData) { 1673 if (element.hasAttributeNS(attributeData.ns, attributeData.tag)) { 1674 QString value = element.attributeNS(attributeData.ns, attributeData.tag); 1675 //debugFlake << "load additional attribute" << attributeData.tag << value; 1676 setAdditionalAttribute(attributeData.name, value); 1677 } 1678 } 1679 } 1680 1681 if (attributes & OdfCommonChildElements) { 1682 const KoXmlElement eventActionsElement(KoXml::namedItemNS(element, KoXmlNS::office, "event-listeners")); 1683 if (!eventActionsElement.isNull()) { 1684 d->eventActions = KoEventActionRegistry::instance()->createEventActionsFromOdf(eventActionsElement, context); 1685 } 1686 // load glue points (connection points) 1687 loadOdfGluePoints(element, context); 1688 } 1689 1690 return true; 1691 } 1692 1693 QSharedPointer<KoShapeBackground> KoShape::loadOdfFill(KoShapeLoadingContext &context) const 1694 { 1695 QString fill = KoShapePrivate::getStyleProperty("fill", context); 1696 QSharedPointer<KoShapeBackground> bg; 1697 if (fill == "solid") { 1698 bg = QSharedPointer<KoShapeBackground>(new KoColorBackground()); 1699 } 1700 else if (fill == "hatch") { 1701 bg = QSharedPointer<KoShapeBackground>(new KoHatchBackground()); 1702 } 1703 else if (fill == "gradient") { 1704 QString styleName = KoShapePrivate::getStyleProperty("fill-gradient-name", context); 1705 KoXmlElement *e = context.odfLoadingContext().stylesReader().drawStyles("gradient").value(styleName); 1706 QString style; 1707 if (e) { 1708 style = e->attributeNS(KoXmlNS::draw, "style", QString()); 1709 } 1710 if ((style == "rectangular") || (style == "square")) { 1711 bg = QSharedPointer<KoShapeBackground>(new KoOdfGradientBackground()); 1712 } else { 1713 QGradient *gradient = new QLinearGradient(); 1714 gradient->setCoordinateMode(QGradient::ObjectBoundingMode); 1715 bg = QSharedPointer<KoShapeBackground>(new KoGradientBackground(gradient)); 1716 } 1717 } else if (fill == "bitmap") { 1718 bg = QSharedPointer<KoShapeBackground>(new KoPatternBackground(context.imageCollection())); 1719 #ifndef NWORKAROUND_ODF_BUGS 1720 } else if (fill.isEmpty()) { 1721 bg = QSharedPointer<KoShapeBackground>(KoOdfWorkaround::fixBackgroundColor(this, context)); 1722 return bg; 1723 #endif 1724 } else { 1725 return QSharedPointer<KoShapeBackground>(0); 1726 } 1727 1728 if (!bg->loadStyle(context.odfLoadingContext(), size())) { 1729 return QSharedPointer<KoShapeBackground>(0); 1730 } 1731 1732 return bg; 1733 } 1734 1735 KoShapeStrokeModel *KoShape::loadOdfStroke(const KoXmlElement &element, KoShapeLoadingContext &context) const 1736 { 1737 KoStyleStack &styleStack = context.odfLoadingContext().styleStack(); 1738 KoOdfStylesReader &stylesReader = context.odfLoadingContext().stylesReader(); 1739 1740 QString stroke = KoShapePrivate::getStyleProperty("stroke", context); 1741 if (stroke == "solid" || stroke == "dash") { 1742 QPen pen = KoOdfGraphicStyles::loadOdfStrokeStyle(styleStack, stroke, stylesReader); 1743 1744 KoShapeStroke *stroke = new KoShapeStroke(); 1745 1746 if (styleStack.hasProperty(KoXmlNS::calligra, "stroke-gradient")) { 1747 QString gradientName = styleStack.property(KoXmlNS::calligra, "stroke-gradient"); 1748 QBrush brush = KoOdfGraphicStyles::loadOdfGradientStyleByName(stylesReader, gradientName, size()); 1749 stroke->setLineBrush(brush); 1750 } else { 1751 stroke->setColor(pen.color()); 1752 } 1753 1754 #ifndef NWORKAROUND_ODF_BUGS 1755 KoOdfWorkaround::fixPenWidth(pen, context); 1756 #endif 1757 stroke->setLineWidth(pen.widthF()); 1758 stroke->setJoinStyle(pen.joinStyle()); 1759 stroke->setLineStyle(pen.style(), pen.dashPattern()); 1760 stroke->setCapStyle(pen.capStyle()); 1761 1762 return stroke; 1763 #ifndef NWORKAROUND_ODF_BUGS 1764 } else if (stroke.isEmpty()) { 1765 QPen pen = KoOdfGraphicStyles::loadOdfStrokeStyle(styleStack, "solid", stylesReader); 1766 if (KoOdfWorkaround::fixMissingStroke(pen, element, context, this)) { 1767 KoShapeStroke *stroke = new KoShapeStroke(); 1768 1769 #ifndef NWORKAROUND_ODF_BUGS 1770 KoOdfWorkaround::fixPenWidth(pen, context); 1771 #endif 1772 stroke->setLineWidth(pen.widthF()); 1773 stroke->setJoinStyle(pen.joinStyle()); 1774 stroke->setLineStyle(pen.style(), pen.dashPattern()); 1775 stroke->setCapStyle(pen.capStyle()); 1776 stroke->setColor(pen.color()); 1777 1778 return stroke; 1779 } 1780 #endif 1781 } 1782 1783 return 0; 1784 } 1785 1786 KoShapeShadow *KoShapePrivate::loadOdfShadow(KoShapeLoadingContext &context) const 1787 { 1788 KoStyleStack &styleStack = context.odfLoadingContext().styleStack(); 1789 QString shadowStyle = KoShapePrivate::getStyleProperty("shadow", context); 1790 if (shadowStyle == "visible" || shadowStyle == "hidden") { 1791 KoShapeShadow *shadow = new KoShapeShadow(); 1792 QColor shadowColor(styleStack.property(KoXmlNS::draw, "shadow-color")); 1793 qreal offsetX = KoUnit::parseValue(styleStack.property(KoXmlNS::draw, "shadow-offset-x")); 1794 qreal offsetY = KoUnit::parseValue(styleStack.property(KoXmlNS::draw, "shadow-offset-y")); 1795 shadow->setOffset(QPointF(offsetX, offsetY)); 1796 qreal blur = KoUnit::parseValue(styleStack.property(KoXmlNS::calligra, "shadow-blur-radius")); 1797 shadow->setBlur(blur); 1798 1799 QString opacity = styleStack.property(KoXmlNS::draw, "shadow-opacity"); 1800 if (! opacity.isEmpty() && opacity.right(1) == "%") 1801 shadowColor.setAlphaF(opacity.leftRef(opacity.length() - 1).toFloat() / 100.0); 1802 shadow->setColor(shadowColor); 1803 shadow->setVisible(shadowStyle == "visible"); 1804 1805 return shadow; 1806 } 1807 return 0; 1808 } 1809 1810 KoBorder *KoShapePrivate::loadOdfBorder(KoShapeLoadingContext &context) const 1811 { 1812 KoStyleStack &styleStack = context.odfLoadingContext().styleStack(); 1813 1814 KoBorder *border = new KoBorder(); 1815 if (border->loadOdf(styleStack)) { 1816 return border; 1817 } 1818 delete border; 1819 return 0; 1820 } 1821 1822 1823 void KoShape::loadOdfGluePoints(const KoXmlElement &element, KoShapeLoadingContext &context) 1824 { 1825 Q_D(KoShape); 1826 1827 KoXmlElement child; 1828 bool hasCenterGluePoint = false; 1829 forEachElement(child, element) { 1830 if (child.namespaceURI() != KoXmlNS::draw) 1831 continue; 1832 if (child.localName() != "glue-point") 1833 continue; 1834 1835 // NOTE: this uses draw:id, but apparently while ODF 1.2 has deprecated 1836 // all use of draw:id for xml:id, it didn't specify that here, so it 1837 // doesn't support xml:id (and so, maybe, shouldn't use KoElementReference. 1838 const QString id = child.attributeNS(KoXmlNS::draw, "id", QString()); 1839 const int index = id.toInt(); 1840 // connection point in center should be default but odf doesn't support, 1841 // in new shape, first custom point is in center, it's okay to replace that point 1842 // with point from xml now, we'll add it back later 1843 if(id.isEmpty() || index < KoConnectionPoint::FirstCustomConnectionPoint || 1844 (index != KoConnectionPoint::FirstCustomConnectionPoint && d->connectors.contains(index))) { 1845 warnFlake << "glue-point with no or invalid id"; 1846 continue; 1847 } 1848 QString xStr = child.attributeNS(KoXmlNS::svg, "x", QString()).simplified(); 1849 QString yStr = child.attributeNS(KoXmlNS::svg, "y", QString()).simplified(); 1850 if(xStr.isEmpty() || yStr.isEmpty()) { 1851 warnFlake << "glue-point with invald position"; 1852 continue; 1853 } 1854 1855 KoConnectionPoint connector; 1856 1857 const QString align = child.attributeNS(KoXmlNS::draw, "align", QString()); 1858 if (align.isEmpty()) { 1859 #ifndef NWORKAROUND_ODF_BUGS 1860 KoOdfWorkaround::fixGluePointPosition(xStr, context); 1861 KoOdfWorkaround::fixGluePointPosition(yStr, context); 1862 #endif 1863 if(!xStr.endsWith('%') || !yStr.endsWith('%')) { 1864 warnFlake << "glue-point with invald position"; 1865 continue; 1866 } 1867 // x and y are relative to drawing object center 1868 connector.position.setX(xStr.remove('%').toDouble()/100.0); 1869 connector.position.setY(yStr.remove('%').toDouble()/100.0); 1870 // convert position to be relative to top-left corner 1871 connector.position += QPointF(0.5, 0.5); 1872 connector.position.rx() = qBound<qreal>(0.0, connector.position.x(), 1.0); 1873 connector.position.ry() = qBound<qreal>(0.0, connector.position.y(), 1.0); 1874 } else { 1875 // absolute distances to the edge specified by align 1876 connector.position.setX(KoUnit::parseValue(xStr)); 1877 connector.position.setY(KoUnit::parseValue(yStr)); 1878 if (align == "top-left") { 1879 connector.alignment = KoConnectionPoint::AlignTopLeft; 1880 } else if (align == "top") { 1881 connector.alignment = KoConnectionPoint::AlignTop; 1882 } else if (align == "top-right") { 1883 connector.alignment = KoConnectionPoint::AlignTopRight; 1884 } else if (align == "left") { 1885 connector.alignment = KoConnectionPoint::AlignLeft; 1886 } else if (align == "center") { 1887 connector.alignment = KoConnectionPoint::AlignCenter; 1888 } else if (align == "right") { 1889 connector.alignment = KoConnectionPoint::AlignRight; 1890 } else if (align == "bottom-left") { 1891 connector.alignment = KoConnectionPoint::AlignBottomLeft; 1892 } else if (align == "bottom") { 1893 connector.alignment = KoConnectionPoint::AlignBottom; 1894 } else if (align == "bottom-right") { 1895 connector.alignment = KoConnectionPoint::AlignBottomRight; 1896 } 1897 debugFlake << "using alignment" << align; 1898 } 1899 const QString escape = child.attributeNS(KoXmlNS::draw, "escape-direction", QString()); 1900 if (!escape.isEmpty()) { 1901 if (escape == "horizontal") { 1902 connector.escapeDirection = KoConnectionPoint::HorizontalDirections; 1903 } else if (escape == "vertical") { 1904 connector.escapeDirection = KoConnectionPoint::VerticalDirections; 1905 } else if (escape == "left") { 1906 connector.escapeDirection = KoConnectionPoint::LeftDirection; 1907 } else if (escape == "right") { 1908 connector.escapeDirection = KoConnectionPoint::RightDirection; 1909 } else if (escape == "up") { 1910 connector.escapeDirection = KoConnectionPoint::UpDirection; 1911 } else if (escape == "down") { 1912 connector.escapeDirection = KoConnectionPoint::DownDirection; 1913 } 1914 debugFlake << "using escape direction" << escape; 1915 } 1916 d->connectors[index] = connector; 1917 debugFlake << "loaded glue-point" << index << "at position" << connector.position; 1918 if (d->connectors[index].position == QPointF(0.5, 0.5)) { 1919 hasCenterGluePoint = true; 1920 debugFlake << "center glue-point found at id " << index; 1921 } 1922 } 1923 if (!hasCenterGluePoint) { 1924 d->connectors[d->connectors.count()] = KoConnectionPoint(QPointF(0.5, 0.5), 1925 KoConnectionPoint::AllDirections, KoConnectionPoint::AlignCenter); 1926 } 1927 debugFlake << "shape has now" << d->connectors.count() << "glue-points"; 1928 } 1929 1930 void KoShape::loadOdfClipContour(const KoXmlElement &element, KoShapeLoadingContext &context, const QSizeF &scaleFactor) 1931 { 1932 Q_D(KoShape); 1933 1934 KoXmlElement child; 1935 forEachElement(child, element) { 1936 if (child.namespaceURI() != KoXmlNS::draw) 1937 continue; 1938 if (child.localName() != "contour-polygon") 1939 continue; 1940 1941 debugFlake << "shape loads contour-polygon"; 1942 KoPathShape *ps = new KoPathShape(); 1943 ps->loadContourOdf(child, context, scaleFactor); 1944 ps->setTransformation(transformation()); 1945 1946 KoClipData *cd = new KoClipData(ps); 1947 KoClipPath *clipPath = new KoClipPath(this, cd); 1948 d->clipPath = clipPath; 1949 } 1950 } 1951 1952 QTransform KoShape::parseOdfTransform(const QString &transform, KoShapeLoadingContext &context) 1953 { 1954 QTransform matrix; 1955 1956 // Split string for handling 1 transform statement at a time 1957 QStringList subtransforms = transform.split(')', QString::SkipEmptyParts); 1958 QStringList::ConstIterator it = subtransforms.constBegin(); 1959 QStringList::ConstIterator end = subtransforms.constEnd(); 1960 for (; it != end; ++it) { 1961 QStringList subtransform = (*it).split('(', QString::SkipEmptyParts); 1962 1963 subtransform[0] = subtransform[0].trimmed().toLower(); 1964 subtransform[1] = subtransform[1].simplified(); 1965 QRegExp reg("[,( ]"); 1966 QStringList params = subtransform[1].split(reg, QString::SkipEmptyParts); 1967 1968 if (subtransform[0].startsWith(';') || subtransform[0].startsWith(',')) 1969 subtransform[0] = subtransform[0].right(subtransform[0].length() - 1); 1970 1971 QString cmd = subtransform[0].toLower(); 1972 1973 if (cmd == "rotate") { 1974 QTransform rotMatrix; 1975 #ifndef NWORKAROUND_ODF_BUGS 1976 KoOdfWorkaround::fixRotate(params, context); 1977 #endif 1978 if (params.count() == 3) { 1979 qreal x = KoUnit::parseValue(params[1]); 1980 qreal y = KoUnit::parseValue(params[2]); 1981 1982 rotMatrix.translate(x, y); 1983 rotMatrix.rotate(KoUnit::parseAngle(params[0], 0.0)); 1984 rotMatrix.translate(x, y); 1985 } else { 1986 rotMatrix.rotate(KoUnit::parseAngle(params[0], 0.0)); 1987 } 1988 matrix = matrix * rotMatrix; 1989 } else if (cmd == "translate") { 1990 QTransform moveMatrix; 1991 if (params.count() == 2) { 1992 qreal x = KoUnit::parseValue(params[0]); 1993 qreal y = KoUnit::parseValue(params[1]); 1994 moveMatrix.translate(x, y); 1995 } else // Spec : if only one param given, assume 2nd param to be 0 1996 moveMatrix.translate(KoUnit::parseValue(params[0]) , 0); 1997 matrix = matrix * moveMatrix; 1998 } else if (cmd == "scale") { 1999 QTransform scaleMatrix; 2000 if (params.count() == 2) 2001 scaleMatrix.scale(params[0].toDouble(), params[1].toDouble()); 2002 else // Spec : if only one param given, assume uniform scaling 2003 scaleMatrix.scale(params[0].toDouble(), params[0].toDouble()); 2004 matrix = matrix * scaleMatrix; 2005 } else if (cmd == "skewx") { 2006 #ifndef NWORKAROUND_ODF_BUGS 2007 KoOdfWorkaround::fixSkew(params, context); 2008 #endif 2009 QPointF p = absolutePosition(KoFlake::TopLeftCorner); 2010 QTransform shearMatrix; 2011 shearMatrix.translate(p.x(), p.y()); 2012 shearMatrix.shear(tan(KoUnit::parseAngle(params[0], 0.0F) * M_PI / 180), 0.0F); 2013 shearMatrix.translate(-p.x(), -p.y()); 2014 matrix = matrix * shearMatrix; 2015 } else if (cmd == "skewy") { 2016 #ifndef NWORKAROUND_ODF_BUGS 2017 KoOdfWorkaround::fixSkew(params, context); 2018 #endif 2019 QPointF p = absolutePosition(KoFlake::TopLeftCorner); 2020 QTransform shearMatrix; 2021 shearMatrix.translate(p.x(), p.y()); 2022 shearMatrix.shear(0.0F, tan(KoUnit::parseAngle(params[0], 0.0F) * M_PI / 180)); 2023 shearMatrix.translate(-p.x(), -p.y()); 2024 matrix = matrix * shearMatrix; 2025 } else if (cmd == "matrix") { 2026 QTransform m; 2027 if (params.count() >= 6) { 2028 m.setMatrix(params[0].toDouble(), params[1].toDouble(), 0, 2029 params[2].toDouble(), params[3].toDouble(), 0, 2030 KoUnit::parseValue(params[4]), KoUnit::parseValue(params[5]), 1); 2031 } 2032 matrix = matrix * m; 2033 } 2034 } 2035 2036 return matrix; 2037 } 2038 2039 void KoShape::saveOdfAttributes(KoShapeSavingContext &context, int attributes) const 2040 { 2041 Q_D(const KoShape); 2042 if (attributes & OdfStyle) { 2043 KoGenStyle style; 2044 // all items that should be written to 'draw:frame' and any other 'draw:' object that inherits this shape 2045 if (context.isSet(KoShapeSavingContext::PresentationShape)) { 2046 style = KoGenStyle(KoGenStyle::PresentationAutoStyle, "presentation"); 2047 context.xmlWriter().addAttribute("presentation:style-name", saveStyle(style, context)); 2048 } else { 2049 style = KoGenStyle(KoGenStyle::GraphicAutoStyle, "graphic"); 2050 context.xmlWriter().addAttribute("draw:style-name", saveStyle(style, context)); 2051 } 2052 } 2053 2054 if (attributes & OdfId) { 2055 if (context.isSet(KoShapeSavingContext::DrawId)) { 2056 KoElementReference ref = context.xmlid(this, "shape", KoElementReference::Counter); 2057 ref.saveOdf(&context.xmlWriter(), KoElementReference::DrawId); 2058 } 2059 } 2060 2061 if (attributes & OdfName) { 2062 if (! name().isEmpty()) 2063 context.xmlWriter().addAttribute("draw:name", name()); 2064 } 2065 2066 if (attributes & OdfLayer) { 2067 KoShape *parent = d->parent; 2068 while (parent) { 2069 if (dynamic_cast<KoShapeLayer*>(parent)) { 2070 context.xmlWriter().addAttribute("draw:layer", parent->name()); 2071 break; 2072 } 2073 parent = parent->parent(); 2074 } 2075 } 2076 2077 if (attributes & OdfZIndex && context.isSet(KoShapeSavingContext::ZIndex)) { 2078 context.xmlWriter().addAttribute("draw:z-index", zIndex()); 2079 } 2080 2081 if (attributes & OdfSize) { 2082 QSizeF s(size()); 2083 if (parent() && parent()->isClipped(this)) { // being clipped shrinks our visible size 2084 // clipping in ODF is done using a combination of visual size and content cliprect. 2085 // A picture of 10cm x 10cm displayed in a box of 2cm x 4cm will be scaled (out 2086 // of proportion in this case). If we then add a fo:clip like; 2087 // fo:clip="rect(2cm, 3cm, 4cm, 5cm)" (top, right, bottom, left) 2088 // our original 10x10 is clipped to 2cm x 4cm and *then* fitted in that box. 2089 2090 // TODO do this properly by subtracting rects 2091 s = parent()->size(); 2092 } 2093 context.xmlWriter().addAttributePt("svg:width", s.width()); 2094 context.xmlWriter().addAttributePt("svg:height", s.height()); 2095 } 2096 2097 // The position is implicitly stored in the transformation matrix 2098 // if the transformation is saved as well 2099 if ((attributes & OdfPosition) && !(attributes & OdfTransformation)) { 2100 const QPointF p(position() * context.shapeOffset(this)); 2101 context.xmlWriter().addAttributePt("svg:x", p.x()); 2102 context.xmlWriter().addAttributePt("svg:y", p.y()); 2103 } 2104 2105 if (attributes & OdfTransformation) { 2106 QTransform matrix = absoluteTransformation(0) * context.shapeOffset(this); 2107 if (! matrix.isIdentity()) { 2108 if (qAbs(matrix.m11() - 1) < 1E-5 // 1 2109 && qAbs(matrix.m12()) < 1E-5 // 0 2110 && qAbs(matrix.m21()) < 1E-5 // 0 2111 && qAbs(matrix.m22() - 1) < 1E-5) { // 1 2112 context.xmlWriter().addAttributePt("svg:x", matrix.dx()); 2113 context.xmlWriter().addAttributePt("svg:y", matrix.dy()); 2114 } else { 2115 QString m = QString("matrix(%1 %2 %3 %4 %5pt %6pt)") 2116 .arg(matrix.m11(), 0, 'f', 11) 2117 .arg(matrix.m12(), 0, 'f', 11) 2118 .arg(matrix.m21(), 0, 'f', 11) 2119 .arg(matrix.m22(), 0, 'f', 11) 2120 .arg(matrix.dx(), 0, 'f', 11) 2121 .arg(matrix.dy(), 0, 'f', 11); 2122 context.xmlWriter().addAttribute("draw:transform", m); 2123 } 2124 } 2125 } 2126 2127 if (attributes & OdfViewbox) { 2128 const QSizeF s(size()); 2129 QString viewBox = QString("0 0 %1 %2").arg(qRound(s.width())).arg(qRound(s.height())); 2130 context.xmlWriter().addAttribute("svg:viewBox", viewBox); 2131 } 2132 2133 if (attributes & OdfAdditionalAttributes) { 2134 QMap<QString, QString>::const_iterator it(d->additionalAttributes.constBegin()); 2135 for (; it != d->additionalAttributes.constEnd(); ++it) { 2136 context.xmlWriter().addAttribute(it.key().toUtf8(), it.value()); 2137 } 2138 } 2139 } 2140 2141 void KoShape::saveOdfCommonChildElements(KoShapeSavingContext &context) const 2142 { 2143 Q_D(const KoShape); 2144 // save event listeners see ODF 9.2.21 Event Listeners 2145 if (d->eventActions.size() > 0) { 2146 context.xmlWriter().startElement("office:event-listeners"); 2147 foreach(KoEventAction * action, d->eventActions) { 2148 action->saveOdf(context); 2149 } 2150 context.xmlWriter().endElement(); 2151 } 2152 2153 // save glue points see ODF 9.2.19 Glue Points 2154 if(d->connectors.count()) { 2155 KoConnectionPoints::const_iterator cp = d->connectors.constBegin(); 2156 KoConnectionPoints::const_iterator lastCp = d->connectors.constEnd(); 2157 for(; cp != lastCp; ++cp) { 2158 // do not save default glue points 2159 if(cp.key() < 4) 2160 continue; 2161 context.xmlWriter().startElement("draw:glue-point"); 2162 context.xmlWriter().addAttribute("draw:id", QString("%1").arg(cp.key())); 2163 if (cp.value().alignment == KoConnectionPoint::AlignNone) { 2164 // convert to percent from center 2165 const qreal x = cp.value().position.x() * 100.0 - 50.0; 2166 const qreal y = cp.value().position.y() * 100.0 - 50.0; 2167 context.xmlWriter().addAttribute("svg:x", QString("%1%").arg(x)); 2168 context.xmlWriter().addAttribute("svg:y", QString("%1%").arg(y)); 2169 } else { 2170 context.xmlWriter().addAttributePt("svg:x", cp.value().position.x()); 2171 context.xmlWriter().addAttributePt("svg:y", cp.value().position.y()); 2172 } 2173 QString escapeDirection; 2174 switch(cp.value().escapeDirection) { 2175 case KoConnectionPoint::HorizontalDirections: 2176 escapeDirection = "horizontal"; 2177 break; 2178 case KoConnectionPoint::VerticalDirections: 2179 escapeDirection = "vertical"; 2180 break; 2181 case KoConnectionPoint::LeftDirection: 2182 escapeDirection = "left"; 2183 break; 2184 case KoConnectionPoint::RightDirection: 2185 escapeDirection = "right"; 2186 break; 2187 case KoConnectionPoint::UpDirection: 2188 escapeDirection = "up"; 2189 break; 2190 case KoConnectionPoint::DownDirection: 2191 escapeDirection = "down"; 2192 break; 2193 default: 2194 // fall through 2195 break; 2196 } 2197 if(!escapeDirection.isEmpty()) { 2198 context.xmlWriter().addAttribute("draw:escape-direction", escapeDirection); 2199 } 2200 QString alignment; 2201 switch(cp.value().alignment) { 2202 case KoConnectionPoint::AlignTopLeft: 2203 alignment = "top-left"; 2204 break; 2205 case KoConnectionPoint::AlignTop: 2206 alignment = "top"; 2207 break; 2208 case KoConnectionPoint::AlignTopRight: 2209 alignment = "top-right"; 2210 break; 2211 case KoConnectionPoint::AlignLeft: 2212 alignment = "left"; 2213 break; 2214 case KoConnectionPoint::AlignCenter: 2215 alignment = "center"; 2216 break; 2217 case KoConnectionPoint::AlignRight: 2218 alignment = "right"; 2219 break; 2220 case KoConnectionPoint::AlignBottomLeft: 2221 alignment = "bottom-left"; 2222 break; 2223 case KoConnectionPoint::AlignBottom: 2224 alignment = "bottom"; 2225 break; 2226 case KoConnectionPoint::AlignBottomRight: 2227 alignment = "bottom-right"; 2228 break; 2229 default: 2230 // fall through 2231 break; 2232 } 2233 if(!alignment.isEmpty()) { 2234 context.xmlWriter().addAttribute("draw:align", alignment); 2235 } 2236 context.xmlWriter().endElement(); 2237 } 2238 } 2239 } 2240 2241 void KoShape::saveOdfClipContour(KoShapeSavingContext &context, const QSizeF &originalSize) const 2242 { 2243 Q_D(const KoShape); 2244 2245 debugFlake << "shape saves contour-polygon"; 2246 if (d->clipPath && !d->clipPath->clipPathShapes().isEmpty()) { 2247 // This will loose data as odf can only save one set of contour wheras 2248 // svg loading and at least karbon editing can produce more than one 2249 // TODO, FIXME see if we can save more than one clipshape to odf 2250 d->clipPath->clipPathShapes().constFirst()->saveContourOdf(context, originalSize); 2251 } 2252 } 2253 2254 // end loading & saving methods 2255 2256 // static 2257 void KoShape::applyConversion(QPainter &painter, const KoViewConverter &converter) 2258 { 2259 qreal zoomX, zoomY; 2260 converter.zoom(&zoomX, &zoomY); 2261 painter.scale(zoomX, zoomY); 2262 } 2263 2264 QPointF KoShape::shapeToDocument(const QPointF &point) const 2265 { 2266 return absoluteTransformation(0).map(point); 2267 } 2268 2269 QRectF KoShape::shapeToDocument(const QRectF &rect) const 2270 { 2271 return absoluteTransformation(0).mapRect(rect); 2272 } 2273 2274 QPointF KoShape::documentToShape(const QPointF &point) const 2275 { 2276 return absoluteTransformation(0).inverted().map(point); 2277 } 2278 2279 QRectF KoShape::documentToShape(const QRectF &rect) const 2280 { 2281 return absoluteTransformation(0).inverted().mapRect(rect); 2282 } 2283 2284 bool KoShape::addDependee(KoShape *shape) 2285 { 2286 Q_D(KoShape); 2287 if (! shape) 2288 return false; 2289 2290 // refuse to establish a circular dependency 2291 if (shape->hasDependee(this)) 2292 return false; 2293 2294 if (! d->dependees.contains(shape)) 2295 d->dependees.append(shape); 2296 2297 return true; 2298 } 2299 2300 void KoShape::removeDependee(KoShape *shape) 2301 { 2302 Q_D(KoShape); 2303 int index = d->dependees.indexOf(shape); 2304 if (index >= 0) 2305 d->dependees.removeAt(index); 2306 } 2307 2308 bool KoShape::hasDependee(KoShape *shape) const 2309 { 2310 Q_D(const KoShape); 2311 return d->dependees.contains(shape); 2312 } 2313 2314 QList<KoShape*> KoShape::dependees() const 2315 { 2316 Q_D(const KoShape); 2317 return d->dependees; 2318 } 2319 2320 void KoShape::shapeChanged(ChangeType type, KoShape *shape) 2321 { 2322 Q_UNUSED(type); 2323 Q_UNUSED(shape); 2324 } 2325 2326 KoSnapData KoShape::snapData() const 2327 { 2328 return KoSnapData(); 2329 } 2330 2331 void KoShape::setAdditionalAttribute(const QString &name, const QString &value) 2332 { 2333 Q_D(KoShape); 2334 d->additionalAttributes.insert(name, value); 2335 } 2336 2337 void KoShape::removeAdditionalAttribute(const QString &name) 2338 { 2339 Q_D(KoShape); 2340 d->additionalAttributes.remove(name); 2341 } 2342 2343 bool KoShape::hasAdditionalAttribute(const QString &name) const 2344 { 2345 Q_D(const KoShape); 2346 return d->additionalAttributes.contains(name); 2347 } 2348 2349 QString KoShape::additionalAttribute(const QString &name) const 2350 { 2351 Q_D(const KoShape); 2352 return d->additionalAttributes.value(name); 2353 } 2354 2355 void KoShape::setAdditionalStyleAttribute(const char *name, const QString &value) 2356 { 2357 Q_D(KoShape); 2358 d->additionalStyleAttributes.insert(name, value); 2359 } 2360 2361 void KoShape::removeAdditionalStyleAttribute(const char *name) 2362 { 2363 Q_D(KoShape); 2364 d->additionalStyleAttributes.remove(name); 2365 } 2366 2367 QString KoShape::additionalStyleAttribute(const QByteArray &name) const 2368 { 2369 Q_D(const KoShape); 2370 return d->additionalStyleAttributes.value(name); 2371 } 2372 2373 QMap<QByteArray, QString> KoShape::additionalStyleAttributes() const 2374 { 2375 Q_D(const KoShape); 2376 return d->additionalStyleAttributes; 2377 } 2378 2379 KoFilterEffectStack *KoShape::filterEffectStack() const 2380 { 2381 Q_D(const KoShape); 2382 return d->filterEffectStack; 2383 } 2384 2385 void KoShape::setFilterEffectStack(KoFilterEffectStack *filterEffectStack) 2386 { 2387 Q_D(KoShape); 2388 if (d->filterEffectStack) 2389 d->filterEffectStack->deref(); 2390 d->filterEffectStack = filterEffectStack; 2391 if (d->filterEffectStack) { 2392 d->filterEffectStack->ref(); 2393 } 2394 notifyChanged(); 2395 } 2396 2397 QSet<KoShape*> KoShape::toolDelegates() const 2398 { 2399 Q_D(const KoShape); 2400 return d->toolDelegates; 2401 } 2402 2403 void KoShape::setToolDelegates(const QSet<KoShape*> &delegates) 2404 { 2405 Q_D(KoShape); 2406 d->toolDelegates = delegates; 2407 } 2408 2409 QString KoShape::hyperLink () const 2410 { 2411 Q_D(const KoShape); 2412 return d->hyperLink; 2413 } 2414 2415 void KoShape::setHyperLink(const QString &hyperLink) 2416 { 2417 Q_D(KoShape); 2418 d->hyperLink = hyperLink; 2419 } 2420 2421 KoShapePrivate *KoShape::priv() 2422 { 2423 Q_D(KoShape); 2424 return d; 2425 }