KoShapeContainer.cpp 6.87 KB
Newer Older
Thomas Zander's avatar
Thomas Zander committed
1
/* This file is part of the KDE project
2
 * Copyright (C) 2006-2007 Thomas Zander <zander@kde.org>
Thomas Zander's avatar
Thomas Zander committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */
19
#include <kdebug.h>
Thomas Zander's avatar
Thomas Zander committed
20 21

#include "KoShapeContainer.h"
22
#include "KoShapeContainerModel.h"
Thomas Zander's avatar
Thomas Zander committed
23 24 25

#include <QPointF>
#include <QPainter>
26
#include <QPainterPath>
Thomas Zander's avatar
Thomas Zander committed
27

Thomas Zander's avatar
Thomas Zander committed
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
class ChildrenData : public KoShapeContainerModel {
public:
    ChildrenData() {}
    ~ChildrenData() {
        qDeleteAll(m_relations);
    }

    void add(KoShape *child) {
        Relation *r = new Relation(child);
        m_relations.append(r);
    }

    void setClipping(const KoShape *child, bool clipping) {
        Relation *relation = findRelation(child);
        if(relation == 0) // throw exception?
            return;
        if(relation->m_inside == clipping)
            return;
        relation->m_inside = clipping;
        relation->child()->repaint();
        relation->child()->recalcMatrix();
        relation->child()->repaint();
    }

    bool childClipped(const KoShape *child) const {
        Relation *relation = findRelation(child);
        if(relation == 0) // throw exception?
            return false;
        return relation->m_inside;
    }

    void remove(KoShape *child) {
        Relation *relation = findRelation(child);
        if(relation == 0)
            return;
        m_relations.removeAll(relation);
    }

    int count() const {
        return m_relations.count();
    }

    QList<KoShape*> iterator() const {
        QList<KoShape*> answer;
        foreach (Relation *relation, m_relations)
            answer.append(relation->child());
        return answer;
    }

    void containerChanged(KoShapeContainer *) { }

private:
    /**
     * This class is a simple data-storage class for Relation objects.
     */
    class Relation {
        public:
            explicit Relation(KoShape *child) :m_inside(false) , m_child(child) { }
            KoShape* child() { return m_child; }
            bool m_inside; ///< if true, the child will be clipped by the parent.
        private:
            KoShape *m_child;
    };

    Relation* findRelation(const KoShape *child) const {
        foreach(Relation *relation, m_relations) {
            if(relation->child() == child)
                return relation;
        }
        return 0;
    }

private: // members
    QList <Relation *> m_relations;
};

104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
class KoShapeContainer::Private {
public:
    Private() : children(0) {}
    ~Private() {
        if(children)
        {
            foreach (KoShape *shape, children->iterator())
                shape->setParent(0);
            delete children;
        }
    }
    KoShapeContainerModel *children;
};

KoShapeContainer::KoShapeContainer() : KoShape(), d(new Private()) {
Thomas Zander's avatar
Thomas Zander committed
119 120
}

121
KoShapeContainer::KoShapeContainer(KoShapeContainerModel *model)
122 123 124 125
: KoShape(),
 d(new Private())
{
    d->children = model;
Thomas Zander's avatar
Thomas Zander committed
126 127 128
}

KoShapeContainer::~KoShapeContainer() {
129
    delete d;
Thomas Zander's avatar
Thomas Zander committed
130 131
}

132 133
void KoShapeContainer::addChild(KoShape *shape) {
    Q_ASSERT(shape);
134
    if(shape->parent() == this && iterator().contains( shape ) )
135
        return;
136 137
    if(d->children == 0)
        d->children = new ChildrenData();
138 139
    if( shape->parent() )
        shape->parent()->removeChild( shape );
140
    d->children->add(shape);
141 142
    shape->setParent(this);
    childCountChanged();
Thomas Zander's avatar
Thomas Zander committed
143 144
}

145 146
void KoShapeContainer::removeChild(KoShape *shape) {
    Q_ASSERT(shape);
147
    if(d->children == 0)
Thomas Zander's avatar
Thomas Zander committed
148
        return;
149
    d->children->remove(shape);
150 151
    shape->setParent(0);
    childCountChanged();
Thomas Zander's avatar
Thomas Zander committed
152 153 154
}

int  KoShapeContainer::childCount() const {
155
    if(d->children == 0)
Thomas Zander's avatar
Thomas Zander committed
156
        return 0;
157
    return d->children->count();
Thomas Zander's avatar
Thomas Zander committed
158 159 160
}

void KoShapeContainer::setClipping(const KoShape *child, bool clipping) {
161
    if(d->children == 0)
Thomas Zander's avatar
Thomas Zander committed
162
        return;
163
    d->children->setClipping(child, clipping);
Thomas Zander's avatar
Thomas Zander committed
164 165
}

166
void KoShapeContainer::paint(QPainter &painter, const KoViewConverter &converter) {
Thomas Zander's avatar
Thomas Zander committed
167 168 169
    painter.save();
    paintComponent(painter, converter);
    painter.restore();
170
    if(d->children == 0 || d->children->count() == 0)
Thomas Zander's avatar
Thomas Zander committed
171 172
        return;

173
    QList<KoShape*> sortedObjects = d->children->iterator();
174
    qSort(sortedObjects.begin(), sortedObjects.end(), KoShape::compareShapeZIndex);
175 176 177 178 179 180 181 182 183
    QMatrix baseMatrix = matrix().inverted() * painter.matrix();

    // clip the children to the parent outline.
    QMatrix m;
    double zoomX, zoomY;
    converter.zoom(&zoomX, &zoomY);
    m.scale(zoomX, zoomY);
    painter.setClipPath(m.map(outline()));

184
    foreach (KoShape *shape, sortedObjects) {
185
        //kDebug(30006) << "KoShapeContainer::painting shape: " << shape->shapeId() << ", " << shape->boundingRect() << endl;
Thomas Zander's avatar
Thomas Zander committed
186 187
        if(! shape->isVisible())
            continue;
188 189 190 191 192 193 194 195
        if(! childClipped(shape) ) // the shapeManager will have to draw those, or else we can't do clipRects
            return;
        if(painter.hasClipping()) {
            QRectF rect = converter.viewToDocument( painter.clipRegion().boundingRect() );
            rect = matrix().mapRect(rect);
            // don't try to draw a child shape that is not in the clipping rect of the painter.
            if(! rect.intersects(shape->boundingRect()))
                continue;
196 197
        }

198 199
        painter.save();
        painter.setMatrix( shape->transformationMatrix(&converter) * baseMatrix );
Thomas Zander's avatar
Thomas Zander committed
200 201 202 203 204
        shape->paint(painter, converter);
        painter.restore();
    }
}

205
void KoShapeContainer::shapeChanged(ChangeType type) {
206
    if(d->children == 0)
Thomas Zander's avatar
Thomas Zander committed
207
        return;
208 209
    if(! (type == RotationChanged || type == ScaleChanged || type == ShearChanged
                || type == SizeChanged || type == PositionChanged))
Thomas Zander's avatar
Thomas Zander committed
210
        return;
211 212
    d->children->containerChanged(this);
    foreach (KoShape *shape, d->children->iterator())
Thomas Zander's avatar
Thomas Zander committed
213 214 215 216
        shape->recalcMatrix();
}

bool KoShapeContainer::childClipped(const KoShape *child) const {
217
    if(d->children == 0) // throw exception??
Thomas Zander's avatar
Thomas Zander committed
218
        return false;
219
    return d->children->childClipped(child);
Thomas Zander's avatar
Thomas Zander committed
220 221 222 223
}

void KoShapeContainer::repaint() const {
    KoShape::repaint();
224 225
    if(d->children)
        foreach ( KoShape *shape, d->children->iterator())
226
            shape->repaint();
Thomas Zander's avatar
Thomas Zander committed
227 228 229
}

QList<KoShape*> KoShapeContainer::iterator() const {
230
    if(d->children == 0)
231 232
        return QList<KoShape*>();

233
    return d->children->iterator();
Thomas Zander's avatar
Thomas Zander committed
234 235
}

Thomas Zander's avatar
Thomas Zander committed
236 237 238 239
KoShapeContainerModel *KoShapeContainer::model() const {
    return d->children;
}