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 }