KoShapeManager.cpp 7.92 KB
Newer Older
Thomas Zander's avatar
Thomas Zander committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
/* This file is part of the KDE project

   Copyright (C) 2006 Thorsten Zachmann <zachmann@kde.org>
   Copyright (C) 2006 Thomas Zander <zander@kde.org>

   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.
*/

#include "KoShapeManager.h"
#include "KoSelection.h"
#include "KoShape.h"
#include "KoCanvasBase.h"
#include "KoShapeContainer.h"
#include "KoShapeBorderModel.h"
28
#include "KoShapeGroup.h"
29
#include "KoToolProxy.h"
Thomas Zander's avatar
Thomas Zander committed
30 31 32

#include <QPainter>

Thomas Zander's avatar
Thomas Zander committed
33
KoShapeManager::KoShapeManager( KoCanvasBase *canvas, const QList<KoShape *> &shapes )
Thomas Zander's avatar
Thomas Zander committed
34
: m_selection( new KoSelection() )
Thomas Zander's avatar
Thomas Zander committed
35
, m_canvas( canvas )
36
, m_tree( 4, 2 )
Thomas Zander's avatar
Thomas Zander committed
37
{
Thomas Zander's avatar
Thomas Zander committed
38
    Q_ASSERT(m_canvas); // not optional.
Thomas Zander's avatar
Thomas Zander committed
39
    connect( m_selection, SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged()) );
Thomas Zander's avatar
Thomas Zander committed
40
    setShapes(shapes);
41
    m_selection->addShapeManager( this );
Thomas Zander's avatar
Thomas Zander committed
42 43 44
}

KoShapeManager::KoShapeManager(KoCanvasBase *canvas)
Thomas Zander's avatar
Thomas Zander committed
45
: m_shapes()
Thomas Zander's avatar
Thomas Zander committed
46
, m_selection( new KoSelection() )
Thomas Zander's avatar
Thomas Zander committed
47
, m_canvas( canvas )
48
, m_tree( 4, 2 )
Thomas Zander's avatar
Thomas Zander committed
49
{
Thomas Zander's avatar
Thomas Zander committed
50
    Q_ASSERT(m_canvas); // not optional.
Thomas Zander's avatar
Thomas Zander committed
51
    connect( m_selection, SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged()) );
52
    m_selection->addShapeManager( this );
Thomas Zander's avatar
Thomas Zander committed
53 54 55 56 57
}


KoShapeManager::~KoShapeManager()
{
58 59
    foreach(KoShape *shape, m_shapes)
        shape->removeShapeManager( this );
Thomas Zander's avatar
Thomas Zander committed
60 61 62 63
    delete m_selection;
}


Thomas Zander's avatar
Thomas Zander committed
64
void KoShapeManager::setShapes( const QList<KoShape *> &shapes )
Thomas Zander's avatar
Thomas Zander committed
65
{
66 67 68 69 70 71
    foreach(KoShape *shape, m_shapes)
    {
        m_aggregate4update.remove( shape );
        m_tree.remove( shape );
        shape->removeShapeManager( this );
    }
Thomas Zander's avatar
Thomas Zander committed
72 73
    m_shapes = shapes;
    foreach(KoShape *shape, m_shapes)
74 75 76
    {
        add( shape );
    }
Thomas Zander's avatar
Thomas Zander committed
77 78
}

79 80
void KoShapeManager::add( KoShape *shape )
{
81 82
    if(m_shapes.contains(shape))
        return;
83
    shape->addShapeManager( this );
Thomas Zander's avatar
Thomas Zander committed
84
    m_shapes.append(shape);
85 86 87
    if( ! dynamic_cast<KoShapeGroup*>( shape ))
    {
        QRectF br( shape->boundingRect() );
88
        m_tree.insert( br, shape );
89
    }
90
    shape->repaint();
Thomas Zander's avatar
Thomas Zander committed
91 92
}

93 94 95
void KoShapeManager::remove( KoShape *shape )
{
    shape->removeShapeManager( this );
96
    m_selection->deselect( shape );
97 98
    m_aggregate4update.remove( shape );
    m_tree.remove( shape );
Thomas Zander's avatar
Thomas Zander committed
99
    m_shapes.removeAll(shape);
Thomas Zander's avatar
Thomas Zander committed
100 101
}

102
void KoShapeManager::paint( QPainter &painter, const KoViewConverter &converter, bool forPrint)
Thomas Zander's avatar
Thomas Zander committed
103
{
104
    updateTree();
Thomas Zander's avatar
Thomas Zander committed
105 106
    QPen pen(Qt::NoPen);  // painters by default have a black stroke, lets turn that off.
    painter.setPen(pen);
Thomas Zander's avatar
Thomas Zander committed
107 108
    QList<KoShape*> sortedShapes( m_tree.intersects( converter.viewToDocument( painter.clipRegion().boundingRect() ) ) );
    qSort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex);
Thomas Zander's avatar
Thomas Zander committed
109
    const QRegion clipRegion = painter.clipRegion();
110

Thomas Zander's avatar
Thomas Zander committed
111
    foreach ( KoShape * shape, sortedShapes ) {
112
        if(! shape->isVisible() || ( shape->parent() && ! shape->parent()->isVisible() ) )
Thomas Zander's avatar
Thomas Zander committed
113 114 115 116
            continue;
        if(shape->parent() != 0 && shape->parent()->childClipped(shape))
            continue;
        if(painter.hasClipping()) {
Thomas Zander's avatar
Thomas Zander committed
117
            QRectF shapeBox = shape->boundingRect();
118
            shapeBox = converter.documentToView(shapeBox);
Thomas Zander's avatar
Thomas Zander committed
119
            QRegion shapeRegion = QRegion(shapeBox.toRect());
Thomas Zander's avatar
Thomas Zander committed
120

Thomas Zander's avatar
Thomas Zander committed
121
            if(clipRegion.intersect(shapeRegion).isEmpty())
Thomas Zander's avatar
Thomas Zander committed
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
                continue;
        }
        painter.save();
        painter.setMatrix( shape->transformationMatrix(&converter) * painter.matrix() );

        painter.save();
        shape->paint( painter, converter );
        painter.restore();
        if(shape->border()) {
            painter.save();
            shape->border()->paintBorder(shape, painter, converter);
            painter.restore();
        }
        if(! forPrint) {
            painter.save();
            painter.setRenderHint( QPainter::Antialiasing, false );
            shape->paintDecorations( painter, converter, m_selection->isSelected(shape) );
            painter.restore();
        }
        painter.restore();  // for the matrix
    }

144 145 146 147 148 149 150 151 152 153 154
#if 0
    // paint tree
    double zx = 0;
    double zy = 0;
    converter.zoom( &zx, &zy );
    painter.save();
    painter.scale( zx, zy );
    m_tree.paint( painter );
    painter.restore();
#endif

Thomas Zander's avatar
Thomas Zander committed
155 156 157 158
    if(! forPrint)
        m_selection->paint( painter, converter );
}

Thomas Zander's avatar
Thomas Zander committed
159
KoShape * KoShapeManager::shapeAt( const QPointF &position, KoFlake::ShapeSelection selection, bool omitHiddenShapes )
Thomas Zander's avatar
Thomas Zander committed
160
{
161
    updateTree();
Thomas Zander's avatar
Thomas Zander committed
162 163
    QList<KoShape*> sortedShapes( m_tree.contains( position ) );
    qSort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex);
Thomas Zander's avatar
Thomas Zander committed
164
    KoShape *firstUnselectedShape = 0;
Thomas Zander's avatar
Thomas Zander committed
165 166
    for(int count = sortedShapes.count()-1; count >= 0; count--) {
        KoShape *shape = sortedShapes.at(count);
167
        if ( omitHiddenShapes && ! shape->isVisible() )
168
            continue;
169 170 171 172
        if ( ! shape->hitTest( position ) )
            continue;

        switch ( selection )
Thomas Zander's avatar
Thomas Zander committed
173
        {
Thomas Zander's avatar
Thomas Zander committed
174
            case KoFlake::ShapeOnTop:
175
                return shape;
Thomas Zander's avatar
Thomas Zander committed
176
            case KoFlake::Selected:
177 178 179
                if ( m_selection->isSelected( shape ) )
                    return shape;
                break;
Thomas Zander's avatar
Thomas Zander committed
180
            case KoFlake::Unselected:
181 182 183
                if ( ! m_selection->isSelected( shape ) )
                    return shape;
                break;
Thomas Zander's avatar
Thomas Zander committed
184
            case KoFlake::NextUnselected:
185 186 187 188
                // we want an unselected shape
                if ( m_selection->isSelected( shape ) )
                    continue;
                // memorize the first unselected shape
Thomas Zander's avatar
Thomas Zander committed
189
                if( ! firstUnselectedShape )
190 191
                    firstUnselectedShape = shape;
                // check if the shape above is selected
Thomas Zander's avatar
Thomas Zander committed
192
                if( count + 1 < sortedShapes.count() && m_selection->isSelected( sortedShapes.at(count + 1) ) )
193 194
                    return shape;
                break;
Thomas Zander's avatar
Thomas Zander committed
195 196
        }
    }
197 198
    // if we want the next unselected below a selected but there was none selected, 
    // return the first found unselected shape
Thomas Zander's avatar
Thomas Zander committed
199
    if( selection == KoFlake::NextUnselected && firstUnselectedShape )
200 201
        return firstUnselectedShape;

Thomas Zander's avatar
Thomas Zander committed
202 203 204 205 206
    if ( m_selection->hitTest( position ) )
        return m_selection;

    return 0; // missed everything
}
Laurent Montel's avatar
Laurent Montel committed
207

208
QList<KoShape *> KoShapeManager::shapesAt( const QRectF &rect, bool omitHiddenShapes )
209 210 211 212
{
    updateTree();
    //TODO check if object is really in the rect and not 
    // only the bounding rect of the object.
213 214 215 216 217 218 219 220 221 222 223
    if( omitHiddenShapes ) {
        QList<KoShape*> intersectedShapes( m_tree.intersects( rect ) );
        for(int count = intersectedShapes.count()-1; count >= 0; count--) {
            KoShape *shape = intersectedShapes.at( count );
            if( ! shape->isVisible() )
                intersectedShapes.removeAt( count );
        }
        return intersectedShapes;
    }
    else
        return m_tree.intersects( rect );
224 225 226 227 228 229 230
}

void KoShapeManager::repaint( QRectF &rect, const KoShape *shape, bool selectionHandles )
{
    m_canvas->updateCanvas( rect );
    if ( selectionHandles && m_selection->isSelected( shape ) )
    {
231 232
        if ( m_canvas->toolProxy() )
            m_canvas->toolProxy()->repaintDecorations();
233 234 235 236 237 238 239 240 241 242 243 244 245 246
    }
}

void KoShapeManager::updateTree( KoShape * shape )
{
    m_aggregate4update.insert( shape );
}

void KoShapeManager::updateTree()
{
    foreach ( KoShape * shape, m_aggregate4update )
    {
        m_tree.remove( shape );
        QRectF br( shape->boundingRect() );
247
        m_tree.insert( br, shape );
248 249 250 251
    }
    m_aggregate4update.clear();
}

Laurent Montel's avatar
Laurent Montel committed
252
#include "KoShapeManager.moc"