File indexing completed on 2024-05-12 15:56:48
0001 /* This file is part of the KDE project 0002 * SPDX-FileCopyrightText: 2006 Thomas Zander <zander@kde.org> 0003 * SPDX-FileCopyrightText: 2007 Jan Hambrecht <jaham@gmx.net> 0004 * 0005 * SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "KoShapeGroup.h" 0009 #include "KoShapeContainerModel.h" 0010 #include "KoShapeContainer_p.h" 0011 #include "KoShapeLayer.h" 0012 #include "SimpleShapeContainerModel.h" 0013 #include "KoShapeSavingContext.h" 0014 #include "KoShapeLoadingContext.h" 0015 #include "KoXmlWriter.h" 0016 #include "KoShapeRegistry.h" 0017 #include "KoShapeStrokeModel.h" 0018 #include "KoShapeShadow.h" 0019 #include "KoInsets.h" 0020 0021 #include <FlakeDebug.h> 0022 0023 #include <QPainter> 0024 0025 class ShapeGroupContainerModel : public SimpleShapeContainerModel 0026 { 0027 public: 0028 ShapeGroupContainerModel(KoShapeGroup *group) : m_group(group) {} 0029 ~ShapeGroupContainerModel() override {} 0030 0031 ShapeGroupContainerModel(const ShapeGroupContainerModel &rhs, KoShapeGroup *group) 0032 : SimpleShapeContainerModel(rhs), 0033 m_group(group) 0034 { 0035 } 0036 0037 void add(KoShape *child) override 0038 { 0039 SimpleShapeContainerModel::add(child); 0040 m_group->invalidateSizeCache(); 0041 } 0042 0043 void remove(KoShape *child) override 0044 { 0045 SimpleShapeContainerModel::remove(child); 0046 m_group->invalidateSizeCache(); 0047 } 0048 0049 void childChanged(KoShape *shape, KoShape::ChangeType type) override 0050 { 0051 SimpleShapeContainerModel::childChanged(shape, type); 0052 //debugFlake << type; 0053 switch (type) { 0054 case KoShape::PositionChanged: 0055 case KoShape::RotationChanged: 0056 case KoShape::ScaleChanged: 0057 case KoShape::ShearChanged: 0058 case KoShape::SizeChanged: 0059 case KoShape::GenericMatrixChange: 0060 case KoShape::ParameterChanged: 0061 case KoShape::ClipPathChanged : 0062 case KoShape::ClipMaskChanged : 0063 m_group->invalidateSizeCache(); 0064 break; 0065 default: 0066 break; 0067 } 0068 } 0069 0070 private: // members 0071 KoShapeGroup * m_group; 0072 }; 0073 0074 class KoShapeGroup::Private 0075 { 0076 public: 0077 Private() {} 0078 0079 Private(const Private &) {} 0080 0081 virtual ~Private() = default; 0082 0083 mutable QRectF savedOutlineRect; 0084 mutable bool sizeCached = false; 0085 0086 }; 0087 0088 KoShapeGroup::KoShapeGroup() 0089 : KoShapeContainer() 0090 , d(new Private) 0091 { 0092 setModelInit(new ShapeGroupContainerModel(this)); 0093 } 0094 0095 KoShapeGroup::KoShapeGroup(const KoShapeGroup &rhs) 0096 : KoShapeContainer(rhs) 0097 , d(new Private(*rhs.d)) 0098 { 0099 ShapeGroupContainerModel *otherModel = dynamic_cast<ShapeGroupContainerModel*>(rhs.model()); 0100 KIS_ASSERT_RECOVER_RETURN(otherModel); 0101 setModelInit(new ShapeGroupContainerModel(*otherModel, this)); 0102 } 0103 0104 KoShapeGroup::~KoShapeGroup() 0105 { 0106 /** 0107 * HACK alert: model will use KoShapeGroup::invalidateSizeCache(), which uses 0108 * KoShapeGroup's d-pointer. We have to manually remove child shapes from the 0109 * model in the destructor of KoShapeGroup as the instance d is no longer accessible 0110 * since ~KoShapeGroup() is executed 0111 */ 0112 model()->deleteOwnedShapes(); 0113 } 0114 0115 KoShape *KoShapeGroup::cloneShape() const 0116 { 0117 return new KoShapeGroup(*this); 0118 } 0119 0120 void KoShapeGroup::paintComponent(QPainter &painter) const 0121 { 0122 Q_UNUSED(painter); 0123 } 0124 0125 bool KoShapeGroup::hitTest(const QPointF &position) const 0126 { 0127 Q_UNUSED(position); 0128 return false; 0129 } 0130 0131 void KoShapeGroup::tryUpdateCachedSize() const 0132 { 0133 if (!d->sizeCached) { 0134 QRectF bound; 0135 Q_FOREACH (KoShape *shape, shapes()) { 0136 bound |= shape->transformation().mapRect(shape->outlineRect()); 0137 } 0138 d->savedOutlineRect = bound; 0139 KoShape::setSizeImpl(bound.size()); 0140 d->sizeCached = true; 0141 } 0142 } 0143 0144 QSizeF KoShapeGroup::size() const 0145 { 0146 tryUpdateCachedSize(); 0147 return KoShape::size(); 0148 } 0149 0150 void KoShapeGroup::setSize(const QSizeF &size) 0151 { 0152 QSizeF oldSize = this->size(); 0153 if (!shapeCount() || oldSize.isNull()) return; 0154 0155 const QTransform scale = 0156 QTransform::fromScale(size.width() / oldSize.width(), size.height() / oldSize.height()); 0157 0158 setTransformation(scale * transformation()); 0159 0160 KoShapeContainer::setSize(size); 0161 } 0162 0163 QRectF KoShapeGroup::outlineRect() const 0164 { 0165 tryUpdateCachedSize(); 0166 return d->savedOutlineRect; 0167 } 0168 0169 QRectF KoShapeGroup::boundingRect() const 0170 { 0171 QRectF groupBound = KoShape::boundingRect(shapes()); 0172 0173 if (shadow()) { 0174 KoInsets insets; 0175 shadow()->insets(insets); 0176 groupBound.adjust(-insets.left, -insets.top, insets.right, insets.bottom); 0177 } 0178 return groupBound; 0179 } 0180 0181 void KoShapeGroup::shapeChanged(ChangeType type, KoShape *shape) 0182 { 0183 Q_UNUSED(shape); 0184 KoShapeContainer::shapeChanged(type, shape); 0185 switch (type) { 0186 case KoShape::StrokeChanged: 0187 break; 0188 default: 0189 break; 0190 } 0191 0192 invalidateSizeCache(); 0193 } 0194 0195 void KoShapeGroup::invalidateSizeCache() 0196 { 0197 d->sizeCached = false; 0198 }