Commit 7699443c authored by Dmitry Kazakov's avatar Dmitry Kazakov

Implement KoShape::cloneShape()

This is a mandatory function to be able to load shapes from SVG,
because the same shape may be instantiated from different places.
We still need to discuss whether we need to really "share" the
shape template, but for now I just deep-copy them (which is the
easiest because of shape normalization problem we have).

There are the following drawbacks/hacks in this patch:

1) Not all the shapes have KoShape::cloneShape() implemented! Basically,
   it is defined only for group shape and all the descendants of a path
   shape (which are the only shapes used in SVG). Other shapes use the
   default implementation which simply returns null.

   Ideally, there should be no default implementation and all the shapes
   should define it. But, given that we are going to deprecate quite a lot
   of stuff, I'll keep them just unimplemented for now.

2) The following shape properties are not yet copied during cloning:

   * toolDelegates
   * dependees
   * shadow
   * border
   * filterEffectStack

   All the properties, except of tool delegates will probably be
   deprecated soon. And for the tool delegates we need to invent
   something ingenious to handle the pointers to recover pointers
   to the *cloned* shapes.

3) I cannot guarantee TextShape's work anymore. I just blindly
   refactored it to use QScopedPointer to QTextDocument instead of
   the previous raw pointers trickery and never tested it. Hope it
   still works...
parent bc68cda4
......@@ -57,7 +57,7 @@ void KoCreatePathTool::paint(QPainter &painter, const KoViewConverter &converter
if (pathStarted()) {
KoShapeStroke *stroke(createStroke());
KoShapeStrokeSP stroke(createStroke());
if (stroke) {
d->shape->setStroke(stroke);
......@@ -199,7 +199,7 @@ void KoCreatePathTool::mousePressEvent(KoPointerEvent *event)
d->shape = pathShape;
pathShape->setShapeId(KoPathShapeId);
KoShapeStroke *stroke = new KoShapeStroke(canvas()->resourceManager()->activeStroke());
KoShapeStrokeSP stroke(new KoShapeStroke(canvas()->resourceManager()->activeStroke()));
stroke->setColor(canvas()->resourceManager()->foregroundColor().toQColor());
pathShape->setStroke(stroke);
......@@ -518,11 +518,11 @@ QList<QPointer<QWidget> > KoCreatePathTool::createOptionWidgets()
return list;
}
KoShapeStroke *KoCreatePathTool::createStroke()
KoShapeStrokeSP KoCreatePathTool::createStroke()
{
Q_D(KoCreatePathTool);
KoShapeStroke *stroke = 0;
KoShapeStrokeSP stroke;
if (d->strokeWidget) {
stroke = d->strokeWidget->createShapeStroke();
}
......
......@@ -23,6 +23,7 @@
#include "kritabasicflakes_export.h"
#include <KoFlakeTypes.h>
#include <KoToolBase.h>
#include <QList>
......@@ -101,7 +102,7 @@ protected:
virtual QList<QPointer<QWidget> > createOptionWidgets();
private:
KoShapeStroke *createStroke();
KoShapeStrokeSP createStroke();
Q_DECLARE_PRIVATE(KoCreatePathTool)
Q_PRIVATE_SLOT(d_func(), void angleDeltaChanged(int))
......
......@@ -417,9 +417,9 @@ void KoPencilTool::setDelta(double delta)
m_combineAngle = delta;
}
KoShapeStroke* KoPencilTool::createStroke()
KoShapeStrokeSP KoPencilTool::createStroke()
{
KoShapeStroke *stroke = 0;
KoShapeStrokeSP stroke;
if (m_strokeWidget) {
stroke = m_strokeWidget->createShapeStroke();
}
......
......@@ -20,6 +20,7 @@
#ifndef _KOPENCILTOOL_H_
#define _KOPENCILTOOL_H_
#include "KoFlakeTypes.h"
#include "KoToolBase.h"
class KoPathShape;
......@@ -57,7 +58,7 @@ protected:
*/
virtual void addPathShape(KoPathShape* path, bool closePath);
KoShapeStroke* createStroke();
KoShapeStrokeSP createStroke();
void setFittingError(qreal fittingError);
qreal getFittingError();
......
......@@ -35,7 +35,6 @@ set(kritaflake_SRCS
KoShapeApplicationData.cpp
KoShapeContainer.cpp
KoShapeContainerModel.cpp
KoShapeContainerDefaultModel.cpp
KoShapeGroup.cpp
KoShapeManagerPaintingStrategy.cpp
KoShapeManager.cpp
......
......@@ -28,6 +28,26 @@
#include <KoShapePainter.h>
struct Q_DECL_HIDDEN KoClipMask::Private {
Private() {}
Private(const Private &rhs)
: coordinates(rhs.coordinates),
contentCoordinates(rhs.contentCoordinates),
maskRect(rhs.maskRect),
extraShapeTransform(rhs.extraShapeTransform)
{
Q_FOREACH (KoShape *shape, rhs.shapes) {
KoShape *clonedShape = shape->cloneShape();
KIS_ASSERT_RECOVER(clonedShape) { continue; }
shapes << clonedShape;
}
}
~Private() {
qDeleteAll(shapes);
shapes.clear();
}
CoordinateSystem coordinates = ObjectBoundingBox;
CoordinateSystem contentCoordinates = UserSpaceOnUse;
......@@ -46,13 +66,11 @@ KoClipMask::KoClipMask()
KoClipMask::~KoClipMask()
{
// TODO: yes, yes, shapes are leaked!
}
KoClipMask::KoClipMask(const KoClipMask &rhs)
: m_d(new Private(*rhs.m_d))
{
// TODO: yes, we leak shapes at the moment!
}
KoClipMask *KoClipMask::clone() const
......@@ -146,4 +164,3 @@ void KoClipMask::drawMask(QPainter *painter, KoShape *shape)
painter->restore();
}
......@@ -53,12 +53,14 @@ public:
~Private()
{
if (deleteClipShapes)
if (deleteClipShapes) {
qDeleteAll(clipPathShapes);
clipPathShapes.clear();
}
}
QList<KoShape*> clipPathShapes;
bool deleteClipShapes;
bool deleteClipShapes; // TODO: deprecate this option!
};
KoClipData::KoClipData(KoPathShape *clipPathShape)
......@@ -85,6 +87,19 @@ KoClipData::KoClipData(const QList<KoShape*> &clipPathShapes)
d->clipPathShapes = clipPathShapes;
}
KoClipData::KoClipData(const KoClipData &rhs)
: d(new Private())
{
Q_FOREACH (KoShape *shape, rhs.d->clipPathShapes) {
KoShape *clonedShape = shape->cloneShape();
KIS_ASSERT_RECOVER(clonedShape) { continue; }
d->clipPathShapes << clonedShape;
}
d->deleteClipShapes = rhs.d->deleteClipShapes;
}
KoClipData::~KoClipData()
{
delete d;
......@@ -107,6 +122,16 @@ public:
: clipData(data)
{}
Private(const Private &rhs)
: clipData(new KoClipData(*rhs.clipData)),
clipPath(rhs.clipPath),
clipRule(rhs.clipRule),
coordinates(rhs.coordinates),
initialTransformToShape(rhs.initialTransformToShape),
initialShapeSize(rhs.initialShapeSize)
{
}
~Private()
{
}
......@@ -189,11 +214,21 @@ KoClipPath::KoClipPath(KoShape *clippedShape, KoClipData *clipData)
d->compileClipPath(clippedShape);
}
KoClipPath::KoClipPath(const KoClipPath &rhs)
: d(new Private(*rhs.d))
{
}
KoClipPath::~KoClipPath()
{
delete d;
}
KoClipPath *KoClipPath::clone() const
{
return new KoClipPath(*this);
}
void KoClipPath::setClipRule(Qt::FillRule clipRule)
{
d->clipRule = clipRule;
......
......@@ -45,6 +45,8 @@ public:
explicit KoClipData(const QList<KoShape*> &clipPathShapes);
explicit KoClipData(const KoClipData &rhs);
/// Destroys the clip path data
~KoClipData();
......@@ -87,6 +89,8 @@ public:
~KoClipPath();
KoClipPath *clone() const;
CoordinateSystem coordinates() const;
/// Sets the clip rule to be used for the clip path
......@@ -116,6 +120,9 @@ public:
/// Applies the clipping to the given painter
static void applyClipping(KoShape *clippedShape, QPainter &painter, const KoViewConverter &converter);
private:
KoClipPath(const KoClipPath &rhs);
private:
class Private;
Private * const d;
......
......@@ -49,6 +49,20 @@ KoConnectionShapePrivate::KoConnectionShapePrivate(KoConnectionShape *q)
{
}
KoConnectionShapePrivate::KoConnectionShapePrivate(const KoConnectionShapePrivate &rhs, KoConnectionShape *q)
: KoParameterShapePrivate(rhs, q),
path(rhs.path),
shape1(0), // FIXME: it should point to the new shapes!!!
shape2(0), // FIXME: it should point to the new shapes!!!
connectionPointId1(rhs.connectionPointId1),
connectionPointId2(rhs.connectionPointId2),
connectionType(rhs.connectionType),
forceUpdate(rhs.forceUpdate),
hasCustomPath(rhs.hasCustomPath)
{
}
QPointF KoConnectionShapePrivate::escapeDirection(int handleId) const
{
Q_Q(const KoConnectionShape);
......@@ -299,7 +313,7 @@ void KoConnectionShape::updateConnections()
}
KoConnectionShape::KoConnectionShape()
: KoParameterShape(*(new KoConnectionShapePrivate(this)))
: KoParameterShape(new KoConnectionShapePrivate(this))
{
Q_D(KoConnectionShape);
d->handles.push_back(QPointF(0, 0));
......@@ -313,6 +327,12 @@ KoConnectionShape::KoConnectionShape()
clearConnectionPoints();
}
KoConnectionShape::KoConnectionShape(const KoConnectionShape &rhs)
: KoParameterShape(new KoConnectionShapePrivate(*rhs.d_func(), this))
{
}
KoConnectionShape::~KoConnectionShape()
{
Q_D(KoConnectionShape);
......@@ -322,6 +342,11 @@ KoConnectionShape::~KoConnectionShape()
d->shape2->removeDependee(this);
}
KoShape *KoConnectionShape::cloneShape() const
{
return new KoConnectionShape(*this);
}
void KoConnectionShape::saveOdf(KoShapeSavingContext & context) const
{
Q_D(const KoConnectionShape);
......@@ -445,7 +470,7 @@ bool KoConnectionShape::loadOdf(const KoXmlElement & element, KoShapeLoadingCont
if (d->hasCustomPath) {
KoPathShapeLoader loader(this);
loader.parseSvg(element.attributeNS(KoXmlNS::svg, "d"), true);
if (m_subpaths.size() > 0) {
if (d->subpaths.size() > 0) {
QRectF viewBox = loadOdfViewbox(element);
if (viewBox.isEmpty()) {
// there should be a viewBox to transform the path data
......@@ -503,8 +528,8 @@ void KoConnectionShape::finishLoadingConnection()
p2 = d->handles[EndHandle];
}
QPointF relativeBegin = m_subpaths.first()->first()->point();
QPointF relativeEnd = m_subpaths.last()->last()->point();
QPointF relativeBegin = d->subpaths.first()->first()->point();
QPointF relativeEnd = d->subpaths.last()->last()->point();
QPointF diffRelative(relativeBegin - relativeEnd);
QPointF diffAbsolute(p1 - p2);
......
......@@ -54,6 +54,8 @@ public:
KoConnectionShape();
virtual ~KoConnectionShape();
KoShape* cloneShape() const override;
// reimplemented
virtual void saveOdf(KoShapeSavingContext &context) const;
......@@ -124,6 +126,9 @@ public:
void updateConnections();
protected:
KoConnectionShape(const KoConnectionShape &rhs);
/// reimplemented
void moveHandleAction(int handleId, const QPointF &point, Qt::KeyboardModifiers modifiers = Qt::NoModifier);
......
......@@ -31,6 +31,8 @@
#include <KoShapeStroke.h>
#include <KoShapeLoadingContext.h>
#include "kis_global.h"
KoConnectionShapeFactory::KoConnectionShapeFactory()
: KoShapeFactoryBase(KOCONNECTIONSHAPEID, i18n("Tie"))
{
......@@ -44,7 +46,7 @@ KoConnectionShapeFactory::KoConnectionShapeFactory()
KoShape* KoConnectionShapeFactory::createDefaultShape(KoDocumentResourceManager *) const
{
KoConnectionShape * shape = new KoConnectionShape();
shape->setStroke(new KoShapeStroke());
shape->setStroke(toQShared(new KoShapeStroke()));
shape->setShapeId(KoPathShapeId);
return shape;
}
......
......@@ -25,6 +25,7 @@ class KoConnectionShapePrivate : public KoParameterShapePrivate
{
public:
explicit KoConnectionShapePrivate(KoConnectionShape *q);
explicit KoConnectionShapePrivate(const KoConnectionShapePrivate &rhs, KoConnectionShape *q);
/// Returns escape direction of given handle
QPointF escapeDirection(int handleId) const;
......
/*
* Copyright (c) 2016 Dmitry Kazakov <dimula73@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KOFLAKETYPES_H
#define KOFLAKETYPES_H
class KoShapeStroke;
class KoShapeStrokeModel;
template<class T> class QSharedPointer;
typedef QSharedPointer<KoShapeStrokeModel> KoShapeStrokeModelSP;
typedef QSharedPointer<KoShapeStroke> KoShapeStrokeSP;
#endif // KOFLAKETYPES_H
......@@ -350,6 +350,11 @@ KoImageData &KoImageData::operator=(const KoImageData &other)
return *this;
}
KoShapeUserData *KoImageData::clone() const
{
return new KoImageData(*this);
}
qint64 KoImageData::key() const
{
return d->key;
......
......@@ -68,6 +68,8 @@ public:
inline bool operator!=(const KoImageData &other) const { return !operator==(other); }
bool operator==(const KoImageData &other) const;
KoShapeUserData* clone() const override;
void setImage(const QString &location, KoStore *store, KoImageCollection *collection = 0);
/**
......
......@@ -24,12 +24,24 @@
#include <QPainter>
#include <FlakeDebug.h>
KoParameterShapePrivate::KoParameterShapePrivate(KoParameterShape *shape)
: KoPathShapePrivate(shape),
parametric(true)
{
}
KoParameterShapePrivate::KoParameterShapePrivate(const KoParameterShapePrivate &rhs, KoParameterShape *q)
: KoPathShapePrivate(rhs, q),
handles(rhs.handles)
{
}
KoParameterShape::KoParameterShape()
: KoPathShape(*(new KoParameterShapePrivate(this)))
: KoPathShape(new KoParameterShapePrivate(this))
{
}
KoParameterShape::KoParameterShape(KoParameterShapePrivate &dd)
KoParameterShape::KoParameterShape(KoParameterShapePrivate *dd)
: KoPathShape(dd)
{
}
......
......@@ -137,7 +137,7 @@ protected:
void setHandles(const QList<QPointF> &handles);
/// constructor
KoParameterShape(KoParameterShapePrivate &);
KoParameterShape(KoParameterShapePrivate *);
/**
* @brief Updates the internal state of a KoParameterShape.
......@@ -157,7 +157,7 @@ protected:
*/
virtual void updatePath(const QSizeF &size) = 0;
private:
protected:
Q_DECLARE_PRIVATE(KoParameterShape)
};
......
......@@ -21,19 +21,20 @@
#ifndef KOPARAMETERSHAPE_P_H
#define KOPARAMETERSHAPE_P_H
#include "kritaflake_export.h"
#include <KoParameterShape.h>
#include "KoPathShape_p.h"
#include <QList>
#include <QPointF>
class KoParameterShapePrivate : public KoPathShapePrivate
class KoParameterShape;
class KRITAFLAKE_EXPORT KoParameterShapePrivate : public KoPathShapePrivate
{
public:
explicit KoParameterShapePrivate(KoParameterShape *shape)
: KoPathShapePrivate(shape),
parametric(true)
{
}
explicit KoParameterShapePrivate(KoParameterShape *shape);
explicit KoParameterShapePrivate(const KoParameterShapePrivate &rhs, KoParameterShape *q);
bool parametric;
......
......@@ -65,6 +65,23 @@ KoPathShapePrivate::KoPathShapePrivate(KoPathShape *q)
{
}
KoPathShapePrivate::KoPathShapePrivate(const KoPathShapePrivate &rhs, KoPathShape *q)
: KoTosContainerPrivate(rhs, q),
fillRule(rhs.fillRule),
startMarker(rhs.startMarker),
endMarker(rhs.endMarker)
{
Q_FOREACH (KoSubpath *subPath, rhs.subpaths) {
KoSubpath *clonedSubPath = new KoSubpath();
Q_FOREACH (KoPathPoint *point, *subPath) {
*clonedSubPath << new KoPathPoint(*point);
}
subpaths << clonedSubPath;
}
}
QRectF KoPathShapePrivate::handleRect(const QPointF &p, qreal radius) const
{
return QRectF(p.x() - radius, p.y() - radius, 2*radius, 2*radius);
......@@ -97,29 +114,39 @@ void KoPathShapePrivate::applyViewboxTransformation(const KoXmlElement &element)
}
KoPathShape::KoPathShape()
:KoTosContainer(*(new KoPathShapePrivate(this)))
:KoTosContainer(new KoPathShapePrivate(this))
{
}
KoPathShape::KoPathShape(KoPathShapePrivate &dd)
KoPathShape::KoPathShape(KoPathShapePrivate *dd)
: KoTosContainer(dd)
{
}
KoPathShape::KoPathShape(const KoPathShape &rhs)
: KoTosContainer(new KoPathShapePrivate(*rhs.d_func(), this))
{
}
KoPathShape::~KoPathShape()
{
clear();
}
KoShape *KoPathShape::cloneShape() const
{
return new KoPathShape(*this);
}
void KoPathShape::saveContourOdf(KoShapeSavingContext &context, const QSizeF &scaleFactor) const
{
Q_D(const KoPathShape);
if (m_subpaths.length() <= 1) {
if (d->subpaths.length() <= 1) {
QTransform matrix;
matrix.scale(scaleFactor.width(), scaleFactor.height());
QString points;
KoSubpath *subPath = m_subpaths.first();
KoSubpath *subPath = d->subpaths.first();
KoSubpath::const_iterator pointIt(subPath->constBegin());
KoPathPoint *currPoint= 0;
......@@ -303,7 +330,7 @@ QString KoPathShape::saveStyle(KoGenStyle &style, KoShapeSavingContext &context)
style.addProperty("svg:fill-rule", d->fillRule == Qt::OddEvenFill ? "evenodd" : "nonzero");
KoShapeStroke *lineBorder = dynamic_cast<KoShapeStroke*>(stroke());
QSharedPointer<KoShapeStroke> lineBorder = qSharedPointerDynamicCast<KoShapeStroke>(stroke());
qreal lineWidth = 0;
if (lineBorder) {
lineWidth = lineBorder->lineWidth();
......@@ -332,7 +359,7 @@ void KoPathShape::loadStyle(const KoXmlElement & element, KoShapeLoadingContext
#endif
}
KoShapeStroke *lineBorder = dynamic_cast<KoShapeStroke*>(stroke());
QSharedPointer<KoShapeStroke> lineBorder = qSharedPointerDynamicCast<KoShapeStroke>(stroke());
qreal lineWidth = 0;
if (lineBorder) {
lineWidth = lineBorder->lineWidth();
......@@ -361,12 +388,14 @@ QRect KoPathShape::loadOdfViewbox(const KoXmlElement & element)
void KoPathShape::clear()
{
Q_FOREACH (KoSubpath *subpath, m_subpaths) {
Q_D(KoPathShape);
Q_FOREACH (KoSubpath *subpath, d->subpaths) {
Q_FOREACH (KoPathPoint *point, *subpath)
delete point;
delete subpath;
}
m_subpaths.clear();
d->subpaths.clear();
}
void KoPathShape::paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext)
......@@ -387,13 +416,13 @@ void KoPathShape::paint(QPainter &painter, const KoViewConverter &converter, KoS
void KoPathShapePrivate::paintDebug(QPainter &painter)
{
Q_Q(KoPathShape);
KoSubpathList::const_iterator pathIt(q->m_subpaths.constBegin());
KoSubpathList::const_iterator pathIt(subpaths.constBegin());
int i = 0;
QPen pen(Qt::black, 0);
painter.save();
painter.setPen(pen);
for (; pathIt != q->m_subpaths.constEnd(); ++pathIt) {
for (; pathIt != subpaths.constEnd(); ++pathIt) {
KoSubpath::const_iterator it((*pathIt)->constBegin());
for (; it != (*pathIt)->constEnd(); ++it) {
++i;
......@@ -422,8 +451,8 @@ void KoPathShapePrivate::paintDebug(QPainter &painter)
void KoPathShapePrivate::debugPath() const
{
Q_Q(const KoPathShape);
KoSubpathList::const_iterator pathIt(q->m_subpaths.constBegin());
for (; pathIt != q->m_subpaths.constEnd(); ++pathIt) {
KoSubpathList::const_iterator pathIt(subpaths.constBegin());
for (; pathIt != subpaths.constEnd(); ++pathIt) {
KoSubpath::const_iterator it((*pathIt)->constBegin());
for (; it != (*pathIt)->constEnd(); ++it) {