File indexing completed on 2024-05-19 04:24:55
0001 /* This file is part of the KDE project 0002 SPDX-FileCopyrightText: 2006 C. Boemann Rasmussen <cbo@boemann.dk> 0003 SPDX-FileCopyrightText: 2006-2010 Thomas Zander <zander@kde.org> 0004 SPDX-FileCopyrightText: 2006-2010 Thorsten Zachmann <zachmann@kde.org> 0005 SPDX-FileCopyrightText: 2007-2009, 2011 Jan Hambrecht <jaham@gmx.net> 0006 SPDX-FileCopyrightText: 2010 Boudewijn Rempt <boud@valdyas.org> 0007 0008 SPDX-License-Identifier: LGPL-2.0-or-later 0009 */ 0010 0011 #include <limits> 0012 0013 #include "KoShape.h" 0014 #include "KoShape_p.h" 0015 #include "KoShapeContainer.h" 0016 #include "KoShapeLayer.h" 0017 #include "KoShapeContainerModel.h" 0018 #include "KoSelection.h" 0019 #include "KoPointerEvent.h" 0020 #include "KoInsets.h" 0021 #include "KoShapeStrokeModel.h" 0022 #include "KoShapeBackground.h" 0023 #include "KoColorBackground.h" 0024 #include "KoHatchBackground.h" 0025 #include "KoGradientBackground.h" 0026 #include "KoPatternBackground.h" 0027 #include "KoShapeManager.h" 0028 #include "KoShapeUserData.h" 0029 #include "KoShapeApplicationData.h" 0030 #include "KoShapeSavingContext.h" 0031 #include "KoShapeLoadingContext.h" 0032 #include "KoViewConverter.h" 0033 #include "KoShapeStroke.h" 0034 #include "KoShapeShadow.h" 0035 #include "KoClipPath.h" 0036 #include "KoPathShape.h" 0037 #include "KoFilterEffectStack.h" 0038 #include <KoSnapData.h> 0039 0040 #include <KoXmlWriter.h> 0041 #include <KoXmlNS.h> 0042 #include <KoUnit.h> 0043 0044 #include <QPainter> 0045 #include <QVariant> 0046 #include <QPainterPath> 0047 #include <QList> 0048 #include <QMap> 0049 #include <QByteArray> 0050 #include <FlakeDebug.h> 0051 0052 #include "kis_assert.h" 0053 0054 #include <KisHandlePainterHelper.h> 0055 0056 // KoShape::Private 0057 0058 KoShape::SharedData::SharedData() 0059 : QSharedData() 0060 , size(50, 50) 0061 , shadow(0) 0062 , filterEffectStack(0) 0063 , transparency(0.0) 0064 , zIndex(0) 0065 , runThrough(0) 0066 , visible(true) 0067 , printable(true) 0068 , geometryProtected(false) 0069 , keepAspect(false) 0070 , selectable(true) 0071 , protectContent(false) 0072 , textRunAroundSide(KoShape::BiggestRunAroundSide) 0073 , textRunAroundDistanceLeft(0.0) 0074 , textRunAroundDistanceTop(0.0) 0075 , textRunAroundDistanceRight(0.0) 0076 , textRunAroundDistanceBottom(0.0) 0077 , textRunAroundThreshold(0.0) 0078 , textRunAroundContour(KoShape::ContourFull) 0079 , paintOrder(QVector<PaintOrder>({Fill, Stroke, Markers})) 0080 , inheritPaintOrder(true) 0081 { } 0082 0083 KoShape::SharedData::SharedData(const SharedData &rhs) 0084 : QSharedData() 0085 , size(rhs.size) 0086 , shapeId(rhs.shapeId) 0087 , name(rhs.name) 0088 , localMatrix(rhs.localMatrix) 0089 , userData(rhs.userData ? rhs.userData->clone() : 0) 0090 , stroke(rhs.stroke) 0091 , fill(rhs.fill) 0092 , inheritBackground(rhs.inheritBackground) 0093 , inheritStroke(rhs.inheritStroke) 0094 , shadow(0) // WARNING: not implemented in Krita 0095 , clipPath(rhs.clipPath ? rhs.clipPath->clone() : 0) 0096 , clipMask(rhs.clipMask ? rhs.clipMask->clone() : 0) 0097 , additionalAttributes(rhs.additionalAttributes) 0098 , additionalStyleAttributes(rhs.additionalStyleAttributes) 0099 , filterEffectStack(0) // WARNING: not implemented in Krita 0100 , transparency(rhs.transparency) 0101 , hyperLink(rhs.hyperLink) 0102 0103 , zIndex(rhs.zIndex) 0104 , runThrough(rhs.runThrough) 0105 , visible(rhs.visible) 0106 , printable(rhs.visible) 0107 , geometryProtected(rhs.geometryProtected) 0108 , keepAspect(rhs.keepAspect) 0109 , selectable(rhs.selectable) 0110 , protectContent(rhs.protectContent) 0111 0112 , textRunAroundSide(rhs.textRunAroundSide) 0113 , textRunAroundDistanceLeft(rhs.textRunAroundDistanceLeft) 0114 , textRunAroundDistanceTop(rhs.textRunAroundDistanceTop) 0115 , textRunAroundDistanceRight(rhs.textRunAroundDistanceRight) 0116 , textRunAroundDistanceBottom(rhs.textRunAroundDistanceBottom) 0117 , textRunAroundThreshold(rhs.textRunAroundThreshold) 0118 , textRunAroundContour(rhs.textRunAroundContour) 0119 0120 , paintOrder(rhs.paintOrder) 0121 , inheritPaintOrder(rhs.inheritPaintOrder) 0122 { 0123 } 0124 0125 KoShape::SharedData::~SharedData() 0126 { 0127 if (shadow && !shadow->deref()) 0128 delete shadow; 0129 if (filterEffectStack && !filterEffectStack->deref()) 0130 delete filterEffectStack; 0131 } 0132 0133 void KoShape::shapeChangedPriv(KoShape::ChangeType type) 0134 { 0135 if (d->parent) 0136 d->parent->model()->childChanged(this, type); 0137 0138 this->shapeChanged(type); 0139 0140 Q_FOREACH (KoShape * shape, d->dependees) { 0141 shape->shapeChanged(type, this); 0142 } 0143 0144 Q_FOREACH (KoShape::ShapeChangeListener *listener, d->listeners) { 0145 listener->notifyShapeChangedImpl(type, this); 0146 } 0147 } 0148 0149 void KoShape::addShapeManager(KoShapeManager *manager) 0150 { 0151 d->shapeManagers.insert(manager); 0152 } 0153 0154 void KoShape::removeShapeManager(KoShapeManager *manager) 0155 { 0156 d->shapeManagers.remove(manager); 0157 } 0158 0159 // ======== KoShape 0160 0161 const qint16 KoShape::maxZIndex = std::numeric_limits<qint16>::max(); 0162 const qint16 KoShape::minZIndex = std::numeric_limits<qint16>::min(); 0163 0164 KoShape::KoShape() 0165 : d(new Private()), 0166 s(new SharedData) 0167 { 0168 notifyChanged(); 0169 } 0170 0171 KoShape::KoShape(const KoShape &rhs) 0172 : d(new Private()), 0173 s(rhs.s) 0174 { 0175 } 0176 0177 KoShape::~KoShape() 0178 { 0179 shapeChangedPriv(Deleted); 0180 d->listeners.clear(); 0181 /** 0182 * The shape must have already been detached from all the parents and 0183 * shape managers. Otherwise we might accidentally request some RTTI 0184 * information, which is not available anymore (we are in d-tor). 0185 * 0186 * TL;DR: fix the code that caused this destruction without unparenting 0187 * instead of trying to remove these assert! 0188 */ 0189 KIS_SAFE_ASSERT_RECOVER (!d->parent) { 0190 d->parent->removeShape(this); 0191 } 0192 0193 KIS_SAFE_ASSERT_RECOVER (d->shapeManagers.isEmpty()) { 0194 Q_FOREACH (KoShapeManager *manager, d->shapeManagers) { 0195 manager->shapeInterface()->notifyShapeDestructed(this); 0196 } 0197 d->shapeManagers.clear(); 0198 } 0199 } 0200 0201 KoShape *KoShape::cloneShape() const 0202 { 0203 KIS_SAFE_ASSERT_RECOVER_NOOP(0 && "not implemented!"); 0204 qWarning() << shapeId() << "cannot be cloned"; 0205 return 0; 0206 } 0207 0208 KoShape *KoShape::cloneShapeAndBakeAbsoluteTransform() const 0209 { 0210 KoShape *clonedShape = this->cloneShape(); 0211 0212 /** 0213 * The shape is cloned without its parent's transformation, so we should 0214 * adjust it manually. 0215 */ 0216 KoShape *oldParentShape = this->parent(); 0217 if (oldParentShape && !oldParentShape->absoluteTransformation().isIdentity()) { 0218 clonedShape->applyAbsoluteTransformation(oldParentShape->absoluteTransformation()); 0219 } 0220 0221 return clonedShape; 0222 } 0223 0224 void KoShape::paintStroke(QPainter &painter) const 0225 { 0226 if (stroke()) { 0227 stroke()->paint(this, painter); 0228 } 0229 } 0230 0231 void KoShape::paintMarkers(QPainter &painter) const 0232 { 0233 if (stroke()) { 0234 stroke()->paintMarkers(this, painter); 0235 } 0236 } 0237 0238 void KoShape::scale(qreal sx, qreal sy) 0239 { 0240 QPointF pos = position(); 0241 QTransform scaleMatrix; 0242 scaleMatrix.translate(pos.x(), pos.y()); 0243 scaleMatrix.scale(sx, sy); 0244 scaleMatrix.translate(-pos.x(), -pos.y()); 0245 s->localMatrix = s->localMatrix * scaleMatrix; 0246 0247 notifyChanged(); 0248 shapeChangedPriv(ScaleChanged); 0249 } 0250 0251 void KoShape::rotate(qreal angle) 0252 { 0253 QPointF center = s->localMatrix.map(QPointF(0.5 * size().width(), 0.5 * size().height())); 0254 QTransform rotateMatrix; 0255 rotateMatrix.translate(center.x(), center.y()); 0256 rotateMatrix.rotate(angle); 0257 rotateMatrix.translate(-center.x(), -center.y()); 0258 s->localMatrix = s->localMatrix * rotateMatrix; 0259 0260 notifyChanged(); 0261 shapeChangedPriv(RotationChanged); 0262 } 0263 0264 void KoShape::shear(qreal sx, qreal sy) 0265 { 0266 QPointF pos = position(); 0267 QTransform shearMatrix; 0268 shearMatrix.translate(pos.x(), pos.y()); 0269 shearMatrix.shear(sx, sy); 0270 shearMatrix.translate(-pos.x(), -pos.y()); 0271 s->localMatrix = s->localMatrix * shearMatrix; 0272 0273 notifyChanged(); 0274 shapeChangedPriv(ShearChanged); 0275 } 0276 0277 void KoShape::setSize(const QSizeF &newSize) 0278 { 0279 QSizeF oldSize(size()); 0280 0281 // always set size, as d->size and size() may vary 0282 setSizeImpl(newSize); 0283 0284 if (oldSize == newSize) 0285 return; 0286 0287 notifyChanged(); 0288 shapeChangedPriv(SizeChanged); 0289 } 0290 0291 void KoShape::setSizeImpl(const QSizeF &size) const 0292 { 0293 s->size = size; 0294 } 0295 0296 void KoShape::setPosition(const QPointF &newPosition) 0297 { 0298 QPointF currentPos = position(); 0299 if (newPosition == currentPos) 0300 return; 0301 QTransform translateMatrix; 0302 translateMatrix.translate(newPosition.x() - currentPos.x(), newPosition.y() - currentPos.y()); 0303 s->localMatrix = s->localMatrix * translateMatrix; 0304 0305 notifyChanged(); 0306 shapeChangedPriv(PositionChanged); 0307 } 0308 0309 bool KoShape::hitTest(const QPointF &position) const 0310 { 0311 if (d->parent && d->parent->isClipped(this) && !d->parent->hitTest(position)) 0312 return false; 0313 0314 QPointF point = absoluteTransformation().inverted().map(position); 0315 QRectF bb = outlineRect(); 0316 0317 if (s->stroke) { 0318 KoInsets insets; 0319 s->stroke->strokeInsets(this, insets); 0320 bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom); 0321 } 0322 if (bb.contains(point)) 0323 return true; 0324 0325 // if there is no shadow we can as well just leave 0326 if (! s->shadow) 0327 return false; 0328 0329 // the shadow has an offset to the shape, so we simply 0330 // check if the position minus the shadow offset hits the shape 0331 point = absoluteTransformation().inverted().map(position - s->shadow->offset()); 0332 0333 return bb.contains(point); 0334 } 0335 0336 QRectF KoShape::boundingRect() const 0337 { 0338 0339 QTransform transform = absoluteTransformation(); 0340 QRectF bb = outlineRect(); 0341 if (s->stroke) { 0342 KoInsets insets; 0343 s->stroke->strokeInsets(this, insets); 0344 bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom); 0345 } 0346 bb = transform.mapRect(bb); 0347 if (s->shadow) { 0348 KoInsets insets; 0349 s->shadow->insets(insets); 0350 bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom); 0351 } 0352 if (s->filterEffectStack) { 0353 QRectF clipRect = s->filterEffectStack->clipRectForBoundingRect(outlineRect()); 0354 bb |= transform.mapRect(clipRect); 0355 } 0356 0357 return bb; 0358 } 0359 0360 QRectF KoShape::boundingRect(const QList<KoShape *> &shapes) 0361 { 0362 QRectF boundingRect; 0363 Q_FOREACH (KoShape *shape, shapes) { 0364 boundingRect |= shape->boundingRect(); 0365 } 0366 return boundingRect; 0367 } 0368 0369 QRectF KoShape::absoluteOutlineRect() const 0370 { 0371 return absoluteTransformation().map(outline()).boundingRect(); 0372 } 0373 0374 QRectF KoShape::absoluteOutlineRect(const QList<KoShape *> &shapes) 0375 { 0376 QRectF absoluteOutlineRect; 0377 Q_FOREACH (KoShape *shape, shapes) { 0378 absoluteOutlineRect |= shape->absoluteOutlineRect(); 0379 } 0380 return absoluteOutlineRect; 0381 } 0382 0383 QTransform KoShape::absoluteTransformation() const 0384 { 0385 QTransform matrix; 0386 // apply parents matrix to inherit any transformations done there. 0387 KoShapeContainer * container = d->parent; 0388 if (container) { 0389 if (container->inheritsTransform(this)) { 0390 matrix = container->absoluteTransformation(); 0391 } else { 0392 QSizeF containerSize = container->size(); 0393 QPointF containerPos = container->absolutePosition() - QPointF(0.5 * containerSize.width(), 0.5 * containerSize.height()); 0394 matrix.translate(containerPos.x(), containerPos.y()); 0395 } 0396 } 0397 0398 return s->localMatrix * matrix; 0399 } 0400 0401 void KoShape::applyAbsoluteTransformation(const QTransform &matrix) 0402 { 0403 QTransform globalMatrix = absoluteTransformation(); 0404 // the transformation is relative to the global coordinate system 0405 // but we want to change the local matrix, so convert the matrix 0406 // to be relative to the local coordinate system 0407 QTransform transformMatrix = globalMatrix * matrix * globalMatrix.inverted(); 0408 applyTransformation(transformMatrix); 0409 } 0410 0411 void KoShape::applyTransformation(const QTransform &matrix) 0412 { 0413 s->localMatrix = matrix * s->localMatrix; 0414 notifyChanged(); 0415 shapeChangedPriv(GenericMatrixChange); 0416 } 0417 0418 void KoShape::setTransformation(const QTransform &matrix) 0419 { 0420 s->localMatrix = matrix; 0421 notifyChanged(); 0422 shapeChangedPriv(GenericMatrixChange); 0423 } 0424 0425 QTransform KoShape::transformation() const 0426 { 0427 return s->localMatrix; 0428 } 0429 0430 KoShape::ChildZOrderPolicy KoShape::childZOrderPolicy() 0431 { 0432 return ChildZDefault; 0433 } 0434 0435 bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2) 0436 { 0437 /** 0438 * WARNING: Our definition of zIndex is not yet compatible with SVG2's 0439 * definition. In SVG stacking context of groups with the same 0440 * zIndex are **merged**, while in Krita the contents of groups 0441 * is never merged. One group will always below than the other. 0442 * Therefore, when zIndex of two groups inside the same parent 0443 * coincide, the resulting painting order in Krita is 0444 * **UNDEFINED**. 0445 * 0446 * To avoid this trouble we use KoShapeReorderCommand::mergeInShape() 0447 * inside KoShapeCreateCommand. 0448 */ 0449 0450 /** 0451 * The algorithm below doesn't correctly handle the case when the two pointers actually 0452 * point to the same shape. So just check it in advance to guarantee strict weak ordering 0453 * relation requirement 0454 */ 0455 if (s1 == s2) return false; 0456 0457 0458 // First sort according to runThrough which is sort of a master level 0459 KoShape *parentShapeS1 = s1->parent(); 0460 KoShape *parentShapeS2 = s2->parent(); 0461 int runThrough1 = s1->runThrough(); 0462 int runThrough2 = s2->runThrough(); 0463 while (parentShapeS1) { 0464 if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) { 0465 runThrough1 = parentShapeS1->runThrough(); 0466 } else { 0467 runThrough1 = runThrough1 + parentShapeS1->runThrough(); 0468 } 0469 parentShapeS1 = parentShapeS1->parent(); 0470 } 0471 0472 while (parentShapeS2) { 0473 if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) { 0474 runThrough2 = parentShapeS2->runThrough(); 0475 } else { 0476 runThrough2 = runThrough2 + parentShapeS2->runThrough(); 0477 } 0478 parentShapeS2 = parentShapeS2->parent(); 0479 } 0480 0481 if (runThrough1 > runThrough2) { 0482 return false; 0483 } 0484 if (runThrough1 < runThrough2) { 0485 return true; 0486 } 0487 0488 // If on the same runThrough level then the zIndex is all that matters. 0489 // 0490 // We basically walk up through the parents until we find a common base parent 0491 // To do that we need two loops where the inner loop walks up through the parents 0492 // of s2 every time we step up one parent level on s1 0493 // 0494 // We don't update the index value until after we have seen that it's not a common base 0495 // That way we ensure that two children of a common base are sorted according to their respective 0496 // z value 0497 bool foundCommonParent = false; 0498 int index1 = s1->zIndex(); 0499 int index2 = s2->zIndex(); 0500 parentShapeS1 = s1; 0501 parentShapeS2 = s2; 0502 while (parentShapeS1 && !foundCommonParent) { 0503 parentShapeS2 = s2; 0504 index2 = parentShapeS2->zIndex(); 0505 while (parentShapeS2) { 0506 if (parentShapeS2 == parentShapeS1) { 0507 foundCommonParent = true; 0508 break; 0509 } 0510 if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) { 0511 index2 = parentShapeS2->zIndex(); 0512 } 0513 parentShapeS2 = parentShapeS2->parent(); 0514 } 0515 0516 if (!foundCommonParent) { 0517 if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) { 0518 index1 = parentShapeS1->zIndex(); 0519 } 0520 parentShapeS1 = parentShapeS1->parent(); 0521 } 0522 } 0523 0524 // If the one shape is a parent/child of the other then sort so. 0525 if (s1 == parentShapeS2) { 0526 return true; 0527 } 0528 if (s2 == parentShapeS1) { 0529 return false; 0530 } 0531 0532 // If we went that far then the z-Index is used for sorting. 0533 return index1 < index2; 0534 } 0535 0536 void KoShape::setParent(KoShapeContainer *parent) 0537 { 0538 0539 if (d->parent == parent) { 0540 return; 0541 } 0542 0543 if (d->parent) { 0544 d->parent->shapeInterface()->removeShape(this); 0545 d->parent = 0; 0546 } 0547 0548 0549 KIS_SAFE_ASSERT_RECOVER_NOOP(parent != this); 0550 0551 if (parent && parent != this) { 0552 d->parent = parent; 0553 parent->shapeInterface()->addShape(this); 0554 } 0555 0556 notifyChanged(); 0557 shapeChangedPriv(ParentChanged); 0558 } 0559 0560 bool KoShape::inheritsTransformFromAny(const QList<KoShape *> ancestorsInQuestion) const 0561 { 0562 bool result = false; 0563 0564 KoShape *shape = const_cast<KoShape*>(this); 0565 while (shape) { 0566 KoShapeContainer *parent = shape->parent(); 0567 if (parent && !parent->inheritsTransform(shape)) { 0568 break; 0569 } 0570 0571 if (ancestorsInQuestion.contains(shape)) { 0572 result = true; 0573 break; 0574 } 0575 0576 shape = parent; 0577 } 0578 0579 return result; 0580 } 0581 0582 bool KoShape::hasCommonParent(const KoShape *shape) const 0583 { 0584 const KoShape *thisShape = this; 0585 while (thisShape) { 0586 0587 const KoShape *otherShape = shape; 0588 while (otherShape) { 0589 if (thisShape == otherShape) { 0590 return true; 0591 } 0592 otherShape = otherShape->parent(); 0593 } 0594 0595 thisShape = thisShape->parent(); 0596 } 0597 0598 return false; 0599 } 0600 0601 qint16 KoShape::zIndex() const 0602 { 0603 return s->zIndex; 0604 } 0605 0606 void KoShape::update() const 0607 { 0608 0609 if (!d->shapeManagers.empty()) { 0610 const QRectF rect(boundingRect()); 0611 Q_FOREACH (KoShapeManager * manager, d->shapeManagers) { 0612 manager->update(rect, this, true); 0613 } 0614 } 0615 } 0616 0617 void KoShape::updateAbsolute(const QRectF &rect) const 0618 { 0619 if (rect.isEmpty() && !rect.isNull()) { 0620 return; 0621 } 0622 0623 0624 if (!d->shapeManagers.empty() && isVisible()) { 0625 Q_FOREACH (KoShapeManager *manager, d->shapeManagers) { 0626 manager->update(rect); 0627 } 0628 } 0629 } 0630 0631 QPainterPath KoShape::outline() const 0632 { 0633 QPainterPath path; 0634 path.addRect(outlineRect()); 0635 return path; 0636 } 0637 0638 QRectF KoShape::outlineRect() const 0639 { 0640 const QSizeF s = size(); 0641 return QRectF(QPointF(0, 0), QSizeF(qMax(s.width(), qreal(0.0001)), 0642 qMax(s.height(), qreal(0.0001)))); 0643 } 0644 0645 QPainterPath KoShape::shadowOutline() const 0646 { 0647 if (background()) { 0648 return outline(); 0649 } 0650 0651 return QPainterPath(); 0652 } 0653 0654 QPointF KoShape::absolutePosition(KoFlake::AnchorPosition anchor) const 0655 { 0656 const QRectF rc = outlineRect(); 0657 0658 QPointF point = rc.topLeft(); 0659 0660 bool valid = false; 0661 QPointF anchoredPoint = KoFlake::anchorToPoint(anchor, rc, &valid); 0662 if (valid) { 0663 point = anchoredPoint; 0664 } 0665 0666 return absoluteTransformation().map(point); 0667 } 0668 0669 void KoShape::setAbsolutePosition(const QPointF &newPosition, KoFlake::AnchorPosition anchor) 0670 { 0671 QPointF currentAbsPosition = absolutePosition(anchor); 0672 QPointF translate = newPosition - currentAbsPosition; 0673 QTransform translateMatrix; 0674 translateMatrix.translate(translate.x(), translate.y()); 0675 applyAbsoluteTransformation(translateMatrix); 0676 notifyChanged(); 0677 shapeChangedPriv(PositionChanged); 0678 } 0679 0680 void KoShape::copySettings(const KoShape *shape) 0681 { 0682 s->size = shape->size(); 0683 s->zIndex = shape->zIndex(); 0684 s->visible = shape->isVisible(false); 0685 0686 // Ensure printable is true by default 0687 if (!s->visible) 0688 s->printable = true; 0689 else 0690 s->printable = shape->isPrintable(); 0691 0692 s->geometryProtected = shape->isGeometryProtected(); 0693 s->protectContent = shape->isContentProtected(); 0694 s->selectable = shape->isSelectable(); 0695 s->keepAspect = shape->keepAspectRatio(); 0696 s->localMatrix = shape->s->localMatrix; 0697 } 0698 0699 void KoShape::notifyChanged() 0700 { 0701 Q_FOREACH (KoShapeManager * manager, d->shapeManagers) { 0702 manager->notifyShapeChanged(this); 0703 } 0704 } 0705 0706 void KoShape::setUserData(KoShapeUserData *userData) 0707 { 0708 s->userData.reset(userData); 0709 } 0710 0711 KoShapeUserData *KoShape::userData() const 0712 { 0713 return s->userData.data(); 0714 } 0715 0716 bool KoShape::hasTransparency() const 0717 { 0718 QSharedPointer<KoShapeBackground> bg = background(); 0719 0720 return !bg || bg->hasTransparency() || s->transparency > 0.0; 0721 } 0722 0723 void KoShape::setTransparency(qreal transparency) 0724 { 0725 s->transparency = qBound<qreal>(0.0, transparency, 1.0); 0726 0727 shapeChangedPriv(TransparencyChanged); 0728 notifyChanged(); 0729 } 0730 0731 qreal KoShape::transparency(bool recursive) const 0732 { 0733 if (!recursive || !parent()) { 0734 return s->transparency; 0735 } else { 0736 const qreal parentOpacity = 1.0-parent()->transparency(recursive); 0737 const qreal childOpacity = 1.0-s->transparency; 0738 return 1.0-(parentOpacity*childOpacity); 0739 } 0740 } 0741 0742 KoInsets KoShape::strokeInsets() const 0743 { 0744 KoInsets answer; 0745 if (s->stroke) 0746 s->stroke->strokeInsets(this, answer); 0747 return answer; 0748 } 0749 0750 void KoShape::setPaintOrder(KoShape::PaintOrder first, KoShape::PaintOrder second) 0751 { 0752 KIS_SAFE_ASSERT_RECOVER_RETURN(first != second); 0753 QVector<PaintOrder> order = {Fill, Stroke, Markers}; 0754 0755 if (first != Fill) { 0756 if (order.at(1) == first) { 0757 order[1] = order[0]; 0758 order[0] = first; 0759 } else if (order.at(2) == first) { 0760 order[2] = order[0]; 0761 order[0] = first; 0762 } 0763 } 0764 if (second != first && second != Stroke) { 0765 if (order.at(2) == second) { 0766 order[2] = order[1]; 0767 order[1] = second; 0768 } 0769 } 0770 s->inheritPaintOrder = false; 0771 s->paintOrder = order; 0772 } 0773 0774 QVector<KoShape::PaintOrder> KoShape::paintOrder() const 0775 { 0776 QVector<PaintOrder> order = {Fill, Stroke, Markers}; 0777 if (!s->inheritPaintOrder) { 0778 order = s->paintOrder; 0779 } else if (parent()) { 0780 order = parent()->paintOrder(); 0781 } 0782 return order; 0783 } 0784 0785 void KoShape::setInheritPaintOrder(bool value) 0786 { 0787 s->inheritPaintOrder = value; 0788 } 0789 0790 bool KoShape::inheritPaintOrder() const 0791 { 0792 return s->inheritPaintOrder; 0793 } 0794 0795 qreal KoShape::rotation() const 0796 { 0797 // try to extract the rotation angle out of the local matrix 0798 // if it is a pure rotation matrix 0799 0800 // check if the matrix has shearing mixed in 0801 if (fabs(fabs(s->localMatrix.m12()) - fabs(s->localMatrix.m21())) > 1e-10) 0802 return std::numeric_limits<qreal>::quiet_NaN(); 0803 // check if the matrix has scaling mixed in 0804 if (fabs(s->localMatrix.m11() - s->localMatrix.m22()) > 1e-10) 0805 return std::numeric_limits<qreal>::quiet_NaN(); 0806 0807 // calculate the angle from the matrix elements 0808 qreal angle = atan2(-s->localMatrix.m21(), s->localMatrix.m11()) * 180.0 / M_PI; 0809 if (angle < 0.0) 0810 angle += 360.0; 0811 0812 return angle; 0813 } 0814 0815 QSizeF KoShape::size() const 0816 { 0817 return s->size; 0818 } 0819 0820 QPointF KoShape::position() const 0821 { 0822 QPointF center = outlineRect().center(); 0823 return s->localMatrix.map(center) - center; 0824 } 0825 0826 KoShape::TextRunAroundSide KoShape::textRunAroundSide() const 0827 { 0828 return s->textRunAroundSide; 0829 } 0830 0831 void KoShape::setTextRunAroundSide(TextRunAroundSide side, RunThroughLevel runThrough) 0832 { 0833 0834 if (side == RunThrough) { 0835 if (runThrough == Background) { 0836 setRunThrough(-1); 0837 } else { 0838 setRunThrough(1); 0839 } 0840 } else { 0841 setRunThrough(0); 0842 } 0843 0844 if ( s->textRunAroundSide == side) { 0845 return; 0846 } 0847 0848 s->textRunAroundSide = side; 0849 notifyChanged(); 0850 shapeChangedPriv(TextRunAroundChanged); 0851 } 0852 0853 qreal KoShape::textRunAroundDistanceTop() const 0854 { 0855 return s->textRunAroundDistanceTop; 0856 } 0857 0858 void KoShape::setTextRunAroundDistanceTop(qreal distance) 0859 { 0860 s->textRunAroundDistanceTop = distance; 0861 } 0862 0863 qreal KoShape::textRunAroundDistanceLeft() const 0864 { 0865 return s->textRunAroundDistanceLeft; 0866 } 0867 0868 void KoShape::setTextRunAroundDistanceLeft(qreal distance) 0869 { 0870 s->textRunAroundDistanceLeft = distance; 0871 } 0872 0873 qreal KoShape::textRunAroundDistanceRight() const 0874 { 0875 return s->textRunAroundDistanceRight; 0876 } 0877 0878 void KoShape::setTextRunAroundDistanceRight(qreal distance) 0879 { 0880 s->textRunAroundDistanceRight = distance; 0881 } 0882 0883 qreal KoShape::textRunAroundDistanceBottom() const 0884 { 0885 return s->textRunAroundDistanceBottom; 0886 } 0887 0888 void KoShape::setTextRunAroundDistanceBottom(qreal distance) 0889 { 0890 s->textRunAroundDistanceBottom = distance; 0891 } 0892 0893 qreal KoShape::textRunAroundThreshold() const 0894 { 0895 return s->textRunAroundThreshold; 0896 } 0897 0898 void KoShape::setTextRunAroundThreshold(qreal threshold) 0899 { 0900 s->textRunAroundThreshold = threshold; 0901 } 0902 0903 KoShape::TextRunAroundContour KoShape::textRunAroundContour() const 0904 { 0905 return s->textRunAroundContour; 0906 } 0907 0908 void KoShape::setTextRunAroundContour(KoShape::TextRunAroundContour contour) 0909 { 0910 s->textRunAroundContour = contour; 0911 } 0912 0913 void KoShape::setBackground(QSharedPointer<KoShapeBackground> fill) 0914 { 0915 s->inheritBackground = false; 0916 s->fill = fill; 0917 shapeChangedPriv(BackgroundChanged); 0918 notifyChanged(); 0919 } 0920 0921 QSharedPointer<KoShapeBackground> KoShape::background() const 0922 { 0923 0924 QSharedPointer<KoShapeBackground> bg; 0925 0926 if (!s->inheritBackground) { 0927 bg = s->fill; 0928 } else if (parent()) { 0929 bg = parent()->background(); 0930 } 0931 0932 return bg; 0933 } 0934 0935 void KoShape::setInheritBackground(bool value) 0936 { 0937 0938 s->inheritBackground = value; 0939 if (s->inheritBackground) { 0940 s->fill.clear(); 0941 } 0942 } 0943 0944 bool KoShape::inheritBackground() const 0945 { 0946 return s->inheritBackground; 0947 } 0948 0949 void KoShape::setZIndex(qint16 zIndex) 0950 { 0951 if (s->zIndex == zIndex) 0952 return; 0953 s->zIndex = zIndex; 0954 notifyChanged(); 0955 } 0956 0957 int KoShape::runThrough() const 0958 { 0959 return s->runThrough; 0960 } 0961 0962 void KoShape::setRunThrough(short int runThrough) 0963 { 0964 s->runThrough = runThrough; 0965 } 0966 0967 void KoShape::setVisible(bool on) 0968 { 0969 int _on = (on ? 1 : 0); 0970 if (s->visible == _on) return; 0971 s->visible = _on; 0972 } 0973 0974 bool KoShape::isVisible(bool recursive) const 0975 { 0976 if (!recursive) 0977 return s->visible; 0978 0979 if (!s->visible) 0980 return false; 0981 0982 KoShapeContainer * parentShape = parent(); 0983 0984 if (parentShape) { 0985 return parentShape->isVisible(true); 0986 } 0987 0988 return true; 0989 } 0990 0991 void KoShape::setPrintable(bool on) 0992 { 0993 s->printable = on; 0994 } 0995 0996 bool KoShape::isPrintable() const 0997 { 0998 if (s->visible) 0999 return s->printable; 1000 else 1001 return false; 1002 } 1003 1004 void KoShape::setSelectable(bool selectable) 1005 { 1006 s->selectable = selectable; 1007 } 1008 1009 bool KoShape::isSelectable() const 1010 { 1011 return s->selectable; 1012 } 1013 1014 void KoShape::setGeometryProtected(bool on) 1015 { 1016 s->geometryProtected = on; 1017 } 1018 1019 bool KoShape::isGeometryProtected() const 1020 { 1021 return s->geometryProtected; 1022 } 1023 1024 void KoShape::setContentProtected(bool protect) 1025 { 1026 s->protectContent = protect; 1027 } 1028 1029 bool KoShape::isContentProtected() const 1030 { 1031 return s->protectContent; 1032 } 1033 1034 KoShapeContainer *KoShape::parent() const 1035 { 1036 return d->parent; 1037 } 1038 1039 void KoShape::setKeepAspectRatio(bool keepAspect) 1040 { 1041 s->keepAspect = keepAspect; 1042 1043 shapeChangedPriv(KeepAspectRatioChange); 1044 notifyChanged(); 1045 } 1046 1047 bool KoShape::keepAspectRatio() const 1048 { 1049 return s->keepAspect; 1050 } 1051 1052 QString KoShape::shapeId() const 1053 { 1054 return s->shapeId; 1055 } 1056 1057 void KoShape::setShapeId(const QString &id) 1058 { 1059 s->shapeId = id; 1060 } 1061 1062 KoShapeStrokeModelSP KoShape::stroke() const 1063 { 1064 1065 KoShapeStrokeModelSP stroke; 1066 1067 if (!s->inheritStroke) { 1068 stroke = s->stroke; 1069 } else if (parent()) { 1070 stroke = parent()->stroke(); 1071 } 1072 1073 return stroke; 1074 } 1075 1076 void KoShape::setStroke(KoShapeStrokeModelSP stroke) 1077 { 1078 1079 s->inheritStroke = false; 1080 s->stroke = stroke; 1081 shapeChangedPriv(StrokeChanged); 1082 notifyChanged(); 1083 } 1084 1085 void KoShape::setInheritStroke(bool value) 1086 { 1087 s->inheritStroke = value; 1088 if (s->inheritStroke) { 1089 s->stroke.clear(); 1090 } 1091 } 1092 1093 bool KoShape::inheritStroke() const 1094 { 1095 return s->inheritStroke; 1096 } 1097 1098 void KoShape::setShadow(KoShapeShadow *shadow) 1099 { 1100 if (s->shadow) 1101 s->shadow->deref(); 1102 s->shadow = shadow; 1103 if (s->shadow) { 1104 s->shadow->ref(); 1105 // TODO update changed area 1106 } 1107 shapeChangedPriv(ShadowChanged); 1108 notifyChanged(); 1109 } 1110 1111 KoShapeShadow *KoShape::shadow() const 1112 { 1113 return s->shadow; 1114 } 1115 1116 void KoShape::setClipPath(KoClipPath *clipPath) 1117 { 1118 s->clipPath.reset(clipPath); 1119 shapeChangedPriv(ClipPathChanged); 1120 notifyChanged(); 1121 } 1122 1123 KoClipPath * KoShape::clipPath() const 1124 { 1125 return s->clipPath.data(); 1126 } 1127 1128 void KoShape::setClipMask(KoClipMask *clipMask) 1129 { 1130 s->clipMask.reset(clipMask); 1131 shapeChangedPriv(ClipMaskChanged); 1132 notifyChanged(); 1133 } 1134 1135 KoClipMask* KoShape::clipMask() const 1136 { 1137 return s->clipMask.data(); 1138 } 1139 1140 QTransform KoShape::transform() const 1141 { 1142 return s->localMatrix; 1143 } 1144 1145 QString KoShape::name() const 1146 { 1147 return s->name; 1148 } 1149 1150 void KoShape::setName(const QString &name) 1151 { 1152 s->name = name; 1153 } 1154 1155 void KoShape::waitUntilReady(bool asynchronous) const 1156 { 1157 Q_UNUSED(asynchronous); 1158 } 1159 1160 bool KoShape::isShapeEditable(bool recursive) const 1161 { 1162 if (!s->visible || s->geometryProtected) 1163 return false; 1164 1165 if (recursive && d->parent) { 1166 return d->parent->isShapeEditable(true); 1167 } 1168 1169 return true; 1170 } 1171 1172 KisHandlePainterHelper KoShape::createHandlePainterHelperView(QPainter *painter, KoShape *shape, const KoViewConverter &converter, qreal handleRadius, int decorationThickness) 1173 { 1174 const QTransform originalPainterTransform = painter->transform(); 1175 1176 painter->setTransform(shape->absoluteTransformation() * 1177 converter.documentToView() * 1178 painter->transform()); 1179 1180 // move c-tor 1181 return KisHandlePainterHelper(painter, originalPainterTransform, handleRadius, decorationThickness); 1182 } 1183 1184 KisHandlePainterHelper KoShape::createHandlePainterHelperDocument(QPainter *painter, KoShape *shape, qreal handleRadius, int decorationThickness) 1185 { 1186 const QTransform originalPainterTransform = painter->transform(); 1187 1188 painter->setTransform(shape->absoluteTransformation() * 1189 painter->transform()); 1190 1191 // move c-tor 1192 return KisHandlePainterHelper(painter, originalPainterTransform, handleRadius, decorationThickness); 1193 } 1194 1195 1196 QPointF KoShape::shapeToDocument(const QPointF &point) const 1197 { 1198 return absoluteTransformation().map(point); 1199 } 1200 1201 QRectF KoShape::shapeToDocument(const QRectF &rect) const 1202 { 1203 return absoluteTransformation().mapRect(rect); 1204 } 1205 1206 QPointF KoShape::documentToShape(const QPointF &point) const 1207 { 1208 return absoluteTransformation().inverted().map(point); 1209 } 1210 1211 QRectF KoShape::documentToShape(const QRectF &rect) const 1212 { 1213 return absoluteTransformation().inverted().mapRect(rect); 1214 } 1215 1216 bool KoShape::addDependee(KoShape *shape) 1217 { 1218 if (! shape) 1219 return false; 1220 1221 // refuse to establish a circular dependency 1222 if (shape->hasDependee(this)) 1223 return false; 1224 1225 if (! d->dependees.contains(shape)) 1226 d->dependees.append(shape); 1227 1228 return true; 1229 } 1230 1231 void KoShape::removeDependee(KoShape *shape) 1232 { 1233 int index = d->dependees.indexOf(shape); 1234 if (index >= 0) 1235 d->dependees.removeAt(index); 1236 } 1237 1238 bool KoShape::hasDependee(KoShape *shape) const 1239 { 1240 return d->dependees.contains(shape); 1241 } 1242 1243 QList<KoShape*> KoShape::dependees() const 1244 { 1245 return d->dependees; 1246 } 1247 1248 void KoShape::shapeChanged(ChangeType type, KoShape *shape) 1249 { 1250 Q_UNUSED(type); 1251 Q_UNUSED(shape); 1252 } 1253 1254 KoSnapData KoShape::snapData() const 1255 { 1256 return KoSnapData(); 1257 } 1258 1259 void KoShape::setAdditionalAttribute(const QString &name, const QString &value) 1260 { 1261 s->additionalAttributes.insert(name, value); 1262 } 1263 1264 void KoShape::removeAdditionalAttribute(const QString &name) 1265 { 1266 s->additionalAttributes.remove(name); 1267 } 1268 1269 bool KoShape::hasAdditionalAttribute(const QString &name) const 1270 { 1271 return s->additionalAttributes.contains(name); 1272 } 1273 1274 QString KoShape::additionalAttribute(const QString &name) const 1275 { 1276 return s->additionalAttributes.value(name); 1277 } 1278 1279 void KoShape::setAdditionalStyleAttribute(const char *name, const QString &value) 1280 { 1281 s->additionalStyleAttributes.insert(name, value); 1282 } 1283 1284 void KoShape::removeAdditionalStyleAttribute(const char *name) 1285 { 1286 s->additionalStyleAttributes.remove(name); 1287 } 1288 1289 KoFilterEffectStack *KoShape::filterEffectStack() const 1290 { 1291 return s->filterEffectStack; 1292 } 1293 1294 void KoShape::setFilterEffectStack(KoFilterEffectStack *filterEffectStack) 1295 { 1296 if (s->filterEffectStack) 1297 s->filterEffectStack->deref(); 1298 s->filterEffectStack = filterEffectStack; 1299 if (s->filterEffectStack) { 1300 s->filterEffectStack->ref(); 1301 } 1302 notifyChanged(); 1303 } 1304 1305 QSet<KoShape*> KoShape::toolDelegates() const 1306 { 1307 return d->toolDelegates; 1308 } 1309 1310 void KoShape::setToolDelegates(const QSet<KoShape*> &delegates) 1311 { 1312 d->toolDelegates = delegates; 1313 } 1314 1315 QString KoShape::hyperLink () const 1316 { 1317 return s->hyperLink; 1318 } 1319 1320 void KoShape::setHyperLink(const QString &hyperLink) 1321 { 1322 s->hyperLink = hyperLink; 1323 } 1324 1325 KoShape::ShapeChangeListener::~ShapeChangeListener() 1326 { 1327 Q_FOREACH(KoShape *shape, m_registeredShapes) { 1328 shape->removeShapeChangeListener(this); 1329 } 1330 } 1331 1332 void KoShape::ShapeChangeListener::registerShape(KoShape *shape) 1333 { 1334 KIS_SAFE_ASSERT_RECOVER_RETURN(!m_registeredShapes.contains(shape)); 1335 m_registeredShapes.append(shape); 1336 } 1337 1338 void KoShape::ShapeChangeListener::unregisterShape(KoShape *shape) 1339 { 1340 KIS_SAFE_ASSERT_RECOVER_RETURN(m_registeredShapes.contains(shape)); 1341 m_registeredShapes.removeAll(shape); 1342 } 1343 1344 void KoShape::ShapeChangeListener::notifyShapeChangedImpl(KoShape::ChangeType type, KoShape *shape) 1345 { 1346 KIS_SAFE_ASSERT_RECOVER_RETURN(m_registeredShapes.contains(shape)); 1347 1348 notifyShapeChanged(type, shape); 1349 1350 if (type == KoShape::Deleted) { 1351 unregisterShape(shape); 1352 } 1353 } 1354 1355 void KoShape::addShapeChangeListener(KoShape::ShapeChangeListener *listener) 1356 { 1357 1358 KIS_SAFE_ASSERT_RECOVER_RETURN(!d->listeners.contains(listener)); 1359 listener->registerShape(this); 1360 d->listeners.append(listener); 1361 } 1362 1363 void KoShape::removeShapeChangeListener(KoShape::ShapeChangeListener *listener) 1364 { 1365 1366 KIS_SAFE_ASSERT_RECOVER_RETURN(d->listeners.contains(listener)); 1367 d->listeners.removeAll(listener); 1368 listener->unregisterShape(this); 1369 } 1370 1371 QList<KoShape::ShapeChangeListener *> KoShape::listeners() const 1372 { 1373 return d->listeners; 1374 } 1375 1376 QList<KoShape *> KoShape::linearizeSubtree(const QList<KoShape *> &shapes) 1377 { 1378 QList<KoShape *> result; 1379 1380 Q_FOREACH (KoShape *shape, shapes) { 1381 result << shape; 1382 1383 KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape); 1384 if (container) { 1385 result << linearizeSubtree(container->shapes()); 1386 } 1387 } 1388 1389 return result; 1390 } 1391 1392 QList<KoShape *> KoShape::linearizeSubtreeSorted(const QList<KoShape *> &shapes) 1393 { 1394 QList<KoShape*> sortedShapes = shapes; 1395 std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex); 1396 1397 QList<KoShape *> result; 1398 1399 Q_FOREACH (KoShape *shape, sortedShapes) { 1400 result << shape; 1401 1402 KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape); 1403 if (container) { 1404 result << linearizeSubtreeSorted(container->shapes()); 1405 } 1406 } 1407 1408 return result; 1409 } 1410 1411 void KoShape::setResolution(qreal /*xRes*/, qreal /*yRes*/) 1412 { 1413 }