Commit b95ea924 authored by Dmitry Kazakov's avatar Dmitry Kazakov

Fix grouping/ungrouping of shapes

There was a weird recursion problem in setParent()/addShape()
functions. Now KoShape can use only specialized interface of
the container that should not be used by anyone else.
parent 14e5df4e
...@@ -70,6 +70,8 @@ ...@@ -70,6 +70,8 @@
#include <QByteArray> #include <QByteArray>
#include <FlakeDebug.h> #include <FlakeDebug.h>
#include "kis_assert.h"
#include <limits> #include <limits>
#include "KoOdfGradientBackground.h" #include "KoOdfGradientBackground.h"
...@@ -594,16 +596,25 @@ bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2) ...@@ -594,16 +596,25 @@ bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
void KoShape::setParent(KoShapeContainer *parent) void KoShape::setParent(KoShapeContainer *parent)
{ {
Q_D(KoShape); Q_D(KoShape);
if (d->parent == parent)
if (d->parent == parent) {
return; return;
}
KoShapeContainer *oldParent = d->parent; KoShapeContainer *oldParent = d->parent;
d->parent = 0; // avoids recursive removing d->parent = 0; // avoids recursive removing
if (oldParent)
oldParent->removeShape(this); if (oldParent) {
oldParent->shapeInterface()->removeShape(this);
}
KIS_SAFE_ASSERT_RECOVER_NOOP(parent != this);
if (parent && parent != this) { if (parent && parent != this) {
d->parent = parent; d->parent = parent;
parent->addShape(this); parent->shapeInterface()->addShape(this);
} }
notifyChanged(); notifyChanged();
d->shapeChanged(ParentChanged); d->shapeChanged(ParentChanged);
} }
......
...@@ -34,7 +34,8 @@ ...@@ -34,7 +34,8 @@
KoShapeContainerPrivate::KoShapeContainerPrivate(KoShapeContainer *q) KoShapeContainerPrivate::KoShapeContainerPrivate(KoShapeContainer *q)
: KoShapePrivate(q), : KoShapePrivate(q),
model(0) shapeInterface(q),
model(0)
{ {
} }
...@@ -45,6 +46,7 @@ KoShapeContainerPrivate::~KoShapeContainerPrivate() ...@@ -45,6 +46,7 @@ KoShapeContainerPrivate::~KoShapeContainerPrivate()
KoShapeContainerPrivate::KoShapeContainerPrivate(const KoShapeContainerPrivate &rhs, KoShapeContainer *q) KoShapeContainerPrivate::KoShapeContainerPrivate(const KoShapeContainerPrivate &rhs, KoShapeContainer *q)
: KoShapePrivate(rhs, q), : KoShapePrivate(rhs, q),
shapeInterface(q),
model(0) model(0)
{ {
} }
...@@ -81,49 +83,12 @@ KoShapeContainer::~KoShapeContainer() ...@@ -81,49 +83,12 @@ KoShapeContainer::~KoShapeContainer()
void KoShapeContainer::addShape(KoShape *shape) void KoShapeContainer::addShape(KoShape *shape)
{ {
Q_D(KoShapeContainer);
Q_ASSERT(shape);
if (shape->parent() == this && shapes().contains(shape))
return;
// TODO add a method to create a default model depending on the shape container
if (d->model == 0)
d->model = new SimpleShapeContainerModel();
if (shape->parent() && shape->parent() != this)
shape->parent()->removeShape(shape);
d->model->add(shape);
shape->setParent(this); shape->setParent(this);
} }
void KoShapeContainer::removeShape(KoShape *shape) void KoShapeContainer::removeShape(KoShape *shape)
{ {
Q_D(KoShapeContainer);
Q_ASSERT(shape);
if (d->model == 0)
return;
d->model->remove(shape);
shape->setParent(0); shape->setParent(0);
KoShapeContainer * grandparent = parent();
if (grandparent) {
grandparent->model()->childChanged(this, KoShape::ChildChanged);
}
}
void KoShapeContainer::removeAllShapes()
{
Q_D(KoShapeContainer);
if (d->model == 0)
return;
for(int i = d->model->shapes().count() - 1; i >= 0; --i) {
KoShape *shape = d->model->shapes()[i];
d->model->remove(shape);
shape->setParent(0);
}
KoShapeContainer * grandparent = parent();
if (grandparent) {
grandparent->model()->childChanged(this, KoShape::ChildChanged);
}
} }
int KoShapeContainer::shapeCount() const int KoShapeContainer::shapeCount() const
...@@ -269,3 +234,51 @@ KoShapeContainerModel *KoShapeContainer::model() const ...@@ -269,3 +234,51 @@ KoShapeContainerModel *KoShapeContainer::model() const
Q_D(const KoShapeContainer); Q_D(const KoShapeContainer);
return d->model; return d->model;
} }
KoShapeContainer::ShapeInterface *KoShapeContainer::shapeInterface()
{
Q_D(KoShapeContainer);
return &d->shapeInterface;
}
KoShapeContainer::ShapeInterface::ShapeInterface(KoShapeContainer *_q)
: q(_q)
{
}
void KoShapeContainer::ShapeInterface::addShape(KoShape *shape)
{
KoShapeContainerPrivate * const d = q->d_func();
Q_ASSERT(shape);
if (shape->parent() == q && q->shapes().contains(shape)) {
return;
}
// TODO add a method to create a default model depending on the shape container
if (!d->model) {
d->model = new SimpleShapeContainerModel();
}
if (shape->parent() && shape->parent() != q) {
shape->parent()->shapeInterface()->removeShape(shape);
}
d->model->add(shape);
}
void KoShapeContainer::ShapeInterface::removeShape(KoShape *shape)
{
KoShapeContainerPrivate * const d = q->d_func();
KIS_SAFE_ASSERT_RECOVER_RETURN(shape);
KIS_SAFE_ASSERT_RECOVER_RETURN(d->model);
KIS_SAFE_ASSERT_RECOVER_RETURN(d->model->shapes().contains(shape));
d->model->remove(shape);
KoShapeContainer *grandparent = q->parent();
if (grandparent) {
grandparent->model()->childChanged(q, KoShape::ChildChanged);
}
}
...@@ -104,13 +104,6 @@ public: ...@@ -104,13 +104,6 @@ public:
*/ */
void removeShape(KoShape *shape); void removeShape(KoShape *shape);
/**
* Remove all children to be completely separated from the container.
*
* All the shapes will only be removed from the container but not be deleted.
*/
void removeAllShapes();
/** /**
* Return the current number of children registered. * Return the current number of children registered.
* @return the current number of children registered. * @return the current number of children registered.
...@@ -211,6 +204,40 @@ public: ...@@ -211,6 +204,40 @@ public:
*/ */
KoShapeContainerModel *model() const; KoShapeContainerModel *model() const;
/**
* A special interface for KoShape to use during setParent call. Don't use
* these method directly for managing shapes hierarchy! Use shape->setParent()
* instead.
*/
struct ShapeInterface {
ShapeInterface(KoShapeContainer *_q);
/**
* Add a child to this container.
*
* This container will NOT take over ownership of the shape. The caller or those creating
* the shape is responsible to delete it if not needed any longer.
*
* @param shape the child to be managed in the container.
*/
void addShape(KoShape *shape);
/**
* Remove a child to be completely separated from the container.
*
* The shape will only be removed from this container but not be deleted.
*
* @param shape the child to be removed.
*/
void removeShape(KoShape *shape);
protected:
KoShapeContainer *q;
};
ShapeInterface* shapeInterface();
protected: protected:
/** /**
* This hook is for inheriting classes that need to do something on adding/removing * This hook is for inheriting classes that need to do something on adding/removing
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#define KOSHAPECONTAINERPRIVATE_H #define KOSHAPECONTAINERPRIVATE_H
#include "KoShape_p.h" #include "KoShape_p.h"
#include "KoShapeContainer.h"
#include "kritaflake_export.h" #include "kritaflake_export.h"
class KoShapeContainerModel; class KoShapeContainerModel;
...@@ -35,6 +36,7 @@ public: ...@@ -35,6 +36,7 @@ public:
KoShapeContainerPrivate(const KoShapeContainerPrivate &rhs, KoShapeContainer *q); KoShapeContainerPrivate(const KoShapeContainerPrivate &rhs, KoShapeContainer *q);
KoShapeContainer::ShapeInterface shapeInterface;
KoShapeContainerModel *model; KoShapeContainerModel *model;
}; };
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment