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