File indexing completed on 2024-05-12 15:56:47

0001 /* This file is part of the KDE project
0002  * SPDX-FileCopyrightText: 2006-2010 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 #include "KoShapeContainer.h"
0008 #include "KoShapeContainer_p.h"
0009 #include "KoShapeContainerModel.h"
0010 #include "KoShapeStrokeModel.h"
0011 #include "SimpleShapeContainerModel.h"
0012 #include "KoShapeSavingContext.h"
0013 
0014 #include <QPointF>
0015 #include <QPainter>
0016 #include <QPainterPath>
0017 
0018 #include "kis_painting_tweaks.h"
0019 #include "kis_assert.h"
0020 
0021 KoShapeContainer::Private::Private(KoShapeContainer *q)
0022     : shapeInterface(q)
0023     , model(0)
0024 {
0025 }
0026 
0027 KoShapeContainer::Private::~Private()
0028 {
0029     delete model;
0030 }
0031 
0032 KoShapeContainer::Private::Private(const KoShapeContainer::Private &rhs, KoShapeContainer *q)
0033     : shapeInterface(q)
0034     , model(0)
0035 {
0036     Q_UNUSED(rhs);
0037 }
0038 
0039 KoShapeContainer::KoShapeContainer(KoShapeContainerModel *model)
0040     : KoShape()
0041     , d(new Private(this))
0042 {
0043     d->model = model;
0044 }
0045 
0046 KoShapeContainer::KoShapeContainer(const KoShapeContainer &rhs)
0047     : KoShape(rhs)
0048     , d(new Private(*(rhs.d.data()), this))
0049 {
0050 }
0051 
0052 KoShapeContainer::~KoShapeContainer()
0053 {
0054     if (d->model) {
0055         d->model->deleteOwnedShapes();
0056     }
0057 }
0058 
0059 void KoShapeContainer::addShape(KoShape *shape)
0060 {
0061     shape->setParent(this);
0062 }
0063 
0064 void KoShapeContainer::removeShape(KoShape *shape)
0065 {
0066     shape->setParent(0);
0067 }
0068 
0069 int KoShapeContainer::shapeCount() const
0070 {
0071     if (d->model == 0)
0072         return 0;
0073     return d->model->count();
0074 }
0075 
0076 void KoShapeContainer::setClipped(const KoShape *child, bool clipping)
0077 {
0078     if (d->model == 0)
0079         return;
0080     d->model->setClipped(child, clipping);
0081 }
0082 
0083 void KoShapeContainer::setInheritsTransform(const KoShape *shape, bool inherit)
0084 {
0085     if (d->model == 0)
0086         return;
0087     d->model->setInheritsTransform(shape, inherit);
0088 }
0089 
0090 bool KoShapeContainer::inheritsTransform(const KoShape *shape) const
0091 {
0092     if (d->model == 0)
0093         return false;
0094     return d->model->inheritsTransform(shape);
0095 }
0096 
0097 void KoShapeContainer::paint(QPainter &painter) const
0098 {
0099     // Shape container paints only its internal component part. All the children are rendered
0100     // by the shape manager itself
0101 
0102     painter.save();
0103     paintComponent(painter);
0104     painter.restore();
0105 }
0106 
0107 void KoShapeContainer::shapeChanged(ChangeType type, KoShape* shape)
0108 {
0109     Q_UNUSED(shape);
0110     if (d->model == 0)
0111         return;
0112     if (!(type == RotationChanged || type == ScaleChanged || type == ShearChanged
0113           || type == SizeChanged || type == PositionChanged || type == GenericMatrixChange))
0114         return;
0115     d->model->containerChanged(this, type);
0116     Q_FOREACH (KoShape *shape, d->model->shapes())
0117         shape->notifyChanged();
0118 }
0119 
0120 bool KoShapeContainer::isClipped(const KoShape *child) const
0121 {
0122     if (d->model == 0) // throw exception??
0123         return false;
0124     return d->model->isClipped(child);
0125 }
0126 
0127 void KoShapeContainer::update() const
0128 {
0129     KoShape::update();
0130     if (d->model)
0131         Q_FOREACH (KoShape *shape, d->model->shapes())
0132             shape->update();
0133 }
0134 
0135 QList<KoShape*> KoShapeContainer::shapes() const
0136 {
0137     if (d->model == 0)
0138         return QList<KoShape*>();
0139 
0140     return d->model->shapes();
0141 }
0142 
0143 KoShapeContainerModel *KoShapeContainer::model() const
0144 {
0145     return d->model;
0146 }
0147 
0148 void KoShapeContainer::setModel(KoShapeContainerModel *model)
0149 {
0150     d->model = model;
0151 }
0152 
0153 void KoShapeContainer::setModelInit(KoShapeContainerModel *model)
0154 {
0155     setModel(model);
0156     // HACK ALERT: the shapes are copied inside the model,
0157     //             but we still need to connect the to the
0158     //             hierarchy here!
0159     if (d->model) {
0160         Q_FOREACH (KoShape *shape, d->model->shapes()) {
0161             if (shape) { // Note: shape can be 0 because not all shapes
0162                 //       implement cloneShape, e.g. the text shape.
0163                 shape->setParent(this);
0164             }
0165         }
0166     }
0167 }
0168 
0169 KoShapeContainer::ShapeInterface *KoShapeContainer::shapeInterface()
0170 {
0171     return &d->shapeInterface;
0172 }
0173 
0174 KoShapeContainer::ShapeInterface::ShapeInterface(KoShapeContainer *_q)
0175     : q(_q)
0176 {
0177 }
0178 
0179 void KoShapeContainer::ShapeInterface::addShape(KoShape *shape)
0180 {
0181     KoShapeContainer::Private * const d = q->d.data();
0182 
0183     KIS_SAFE_ASSERT_RECOVER_RETURN(shape);
0184 
0185     if (shape->parent() == q && q->shapes().contains(shape)) {
0186         return;
0187     }
0188 
0189     // TODO add a method to create a default model depending on the shape container
0190     if (!d->model) {
0191         d->model = new SimpleShapeContainerModel();
0192     }
0193 
0194     if (shape->parent() && shape->parent() != q) {
0195         shape->parent()->shapeInterface()->removeShape(shape);
0196     }
0197 
0198     d->model->add(shape);
0199     d->model->shapeHasBeenAddedToHierarchy(shape, q);
0200 }
0201 
0202 void KoShapeContainer::ShapeInterface::removeShape(KoShape *shape)
0203 {
0204     KoShapeContainer::Private * const d = q->d.data();
0205 
0206     KIS_SAFE_ASSERT_RECOVER_RETURN(shape);
0207     KIS_SAFE_ASSERT_RECOVER_RETURN(d->model);
0208     KIS_SAFE_ASSERT_RECOVER_RETURN(d->model->shapes().contains(shape));
0209 
0210     d->model->shapeToBeRemovedFromHierarchy(shape, q);
0211     d->model->remove(shape);
0212 
0213     KoShapeContainer *grandparent = q->parent();
0214     if (grandparent) {
0215         grandparent->model()->childChanged(q, KoShape::ChildChanged);
0216     }
0217 }