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 @@
#include <QByteArray>
#include <FlakeDebug.h>
#include "kis_assert.h"
#include <limits>
#include "KoOdfGradientBackground.h"
......@@ -594,16 +596,25 @@ bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
void KoShape::setParent(KoShapeContainer *parent)
{
Q_D(KoShape);
if (d->parent == parent)
if (d->parent == parent) {
return;
}
KoShapeContainer *oldParent = d->parent;
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) {
d->parent = parent;
parent->addShape(this);
parent->shapeInterface()->addShape(this);
}
notifyChanged();
d->shapeChanged(ParentChanged);
}
......
......@@ -34,7 +34,8 @@
KoShapeContainerPrivate::KoShapeContainerPrivate(KoShapeContainer *q)
: KoShapePrivate(q),
model(0)
shapeInterface(q),
model(0)
{
}
......@@ -45,6 +46,7 @@ KoShapeContainerPrivate::~KoShapeContainerPrivate()
KoShapeContainerPrivate::KoShapeContainerPrivate(const KoShapeContainerPrivate &rhs, KoShapeContainer *q)
: KoShapePrivate(rhs, q),
shapeInterface(q),
model(0)
{
}
......@@ -81,49 +83,12 @@ KoShapeContainer::~KoShapeContainer()
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);
}
void KoShapeContainer::removeShape(KoShape *shape)
{
Q_D(KoShapeContainer);
Q_ASSERT(shape);
if (d->model == 0)
return;
d->model->remove(shape);
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
......@@ -269,3 +234,51 @@ KoShapeContainerModel *KoShapeContainer::model() const
Q_D(const KoShapeContainer);
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:
*/
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.
......@@ -211,6 +204,40 @@ public:
*/
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:
/**
* This hook is for inheriting classes that need to do something on adding/removing
......
......@@ -20,6 +20,7 @@
#define KOSHAPECONTAINERPRIVATE_H
#include "KoShape_p.h"
#include "KoShapeContainer.h"
#include "kritaflake_export.h"
class KoShapeContainerModel;
......@@ -35,6 +36,7 @@ public:
KoShapeContainerPrivate(const KoShapeContainerPrivate &rhs, KoShapeContainer *q);
KoShapeContainer::ShapeInterface shapeInterface;
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