File indexing completed on 2024-05-19 04:24:55

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 }
0136 
0137 QList<KoShape*> KoShapeContainer::shapes() const
0138 {
0139     if (d->model == 0)
0140         return QList<KoShape*>();
0141 
0142     return d->model->shapes();
0143 }
0144 
0145 KoShapeContainerModel *KoShapeContainer::model() const
0146 {
0147     return d->model;
0148 }
0149 
0150 void KoShapeContainer::setModel(KoShapeContainerModel *model)
0151 {
0152     d->model = model;
0153 }
0154 
0155 void KoShapeContainer::setModelInit(KoShapeContainerModel *model)
0156 {
0157     setModel(model);
0158     // HACK ALERT: the shapes are copied inside the model,
0159     //             but we still need to connect the to the
0160     //             hierarchy here!
0161     if (d->model) {
0162         Q_FOREACH (KoShape *shape, d->model->shapes()) {
0163             if (shape) { // Note: shape can be 0 because not all shapes
0164                 //       implement cloneShape, e.g. the text shape.
0165                 shape->setParent(this);
0166             }
0167         }
0168     }
0169 }
0170 
0171 KoShapeContainer::ShapeInterface *KoShapeContainer::shapeInterface()
0172 {
0173     return &d->shapeInterface;
0174 }
0175 
0176 KoShapeContainer::ShapeInterface::ShapeInterface(KoShapeContainer *_q)
0177     : q(_q)
0178 {
0179 }
0180 
0181 void KoShapeContainer::ShapeInterface::addShape(KoShape *shape)
0182 {
0183     KoShapeContainer::Private * const d = q->d.data();
0184 
0185     KIS_SAFE_ASSERT_RECOVER_RETURN(shape);
0186 
0187     if (shape->parent() == q && q->shapes().contains(shape)) {
0188         return;
0189     }
0190 
0191     // TODO add a method to create a default model depending on the shape container
0192     if (!d->model) {
0193         d->model = new SimpleShapeContainerModel();
0194     }
0195 
0196     if (shape->parent() && shape->parent() != q) {
0197         shape->parent()->shapeInterface()->removeShape(shape);
0198     }
0199 
0200     d->model->add(shape);
0201     d->model->shapeHasBeenAddedToHierarchy(shape, q);
0202 }
0203 
0204 void KoShapeContainer::ShapeInterface::removeShape(KoShape *shape)
0205 {
0206     KoShapeContainer::Private * const d = q->d.data();
0207 
0208     KIS_SAFE_ASSERT_RECOVER_RETURN(shape);
0209     KIS_SAFE_ASSERT_RECOVER_RETURN(d->model);
0210     KIS_SAFE_ASSERT_RECOVER_RETURN(d->model->shapes().contains(shape));
0211 
0212     d->model->shapeToBeRemovedFromHierarchy(shape, q);
0213     d->model->remove(shape);
0214 
0215     KoShapeContainer *grandparent = q->parent();
0216     if (grandparent) {
0217         grandparent->model()->childChanged(q, KoShape::ChildChanged);
0218     }
0219 }