KoSelection.cpp 7.21 KB
Newer Older
Thomas Zander's avatar
Thomas Zander committed
1 2 3 4
/* This file is part of the KDE project

   Copyright (C) 2006 Boudewijn Rempt <boud@valdyas.org>
   Copyright (C) 2006 Thorsten Zachmann <zachmann@kde.org>
5
   Copyright (C) 2006 Jan Hambrecht <jaham@gmx.net>
6
   Copyright (C) 2006-2007 Thomas Zander <zander@kde.org>
Thomas Zander's avatar
Thomas Zander committed
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

   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 "KoSelection.h"
#include "KoShapeContainer.h"
#include "KoShapeGroup.h"
27
#include "KoPointerEvent.h"
Thomas Zander's avatar
Thomas Zander committed
28 29 30 31

#include <QPainter>
#include <QTimer>

32 33 34 35 36 37 38 39 40 41 42

class KoSelection::Private {
public:
    Private() : eventTriggered(false), activeLayer(0) {}
    QList<KoShape*> selectedShapes;
    bool eventTriggered;

    KoShapeLayer *activeLayer;
};


Thomas Zander's avatar
Thomas Zander committed
43
KoSelection::KoSelection()
44
    : d(new Private())
Thomas Zander's avatar
Thomas Zander committed
45 46 47 48 49
{
}

KoSelection::~KoSelection()
{
50
    delete d;
Thomas Zander's avatar
Thomas Zander committed
51 52
}

53
void KoSelection::paint( QPainter &painter, const KoViewConverter &converter)
Thomas Zander's avatar
Thomas Zander committed
54
{
55 56
    Q_UNUSED(painter);
    Q_UNUSED(converter);
Thomas Zander's avatar
Thomas Zander committed
57 58
}

59 60 61 62 63 64
void KoSelection::selectGroupChilds( KoShapeGroup *group )
{
    if( ! group )
        return;

    foreach(KoShape *shape, group->iterator()) {
65
        if( d->selectedShapes.contains(shape))
66
            continue;
67
        d->selectedShapes << shape;
68 69 70 71 72 73 74

        KoShapeGroup* childGroup = dynamic_cast<KoShapeGroup*>( shape );
        if( childGroup )
            selectGroupChilds( childGroup );
    }
}

Thomas Zander's avatar
Thomas Zander committed
75 76 77
void KoSelection::select(KoShape * object)
{
    Q_ASSERT(object != this);
78
    Q_ASSERT(object);
79 80
    if(! object->isSelectable())
        return;
81 82
    if(!d->selectedShapes.contains(object))
        d->selectedShapes << object;
83 84 85 86

    KoShapeGroup* group = dynamic_cast<KoShapeGroup*>(object);
    if( group )
        selectGroupChilds( group );
87 88 89

    KoShapeContainer *parent = object->parent();
    while( parent ) {
90 91
        KoShapeGroup *parentGroup = dynamic_cast<KoShapeGroup*>(parent);
        if( ! parentGroup ) break;
92 93
        if( ! d->selectedShapes.contains(parentGroup) ) {
            d->selectedShapes << parentGroup;
94
            selectGroupChilds( parentGroup );
Thomas Zander's avatar
Thomas Zander committed
95
        }
96
        parent = parentGroup->parent();
Thomas Zander's avatar
Thomas Zander committed
97
    }
98

99
    if(d->selectedShapes.count() == 1)
100
        setTransformation( object->transformationMatrix(0) );
101
    else
102 103 104 105
        setTransformation( QMatrix() );

    updateSizeAndPosition();

Thomas Zander's avatar
Thomas Zander committed
106 107 108 109 110
    requestSelectionChangedEvent();
}

void KoSelection::deselect(KoShape * object)
{
111
    if(! d->selectedShapes.contains(object))
Thomas Zander's avatar
Thomas Zander committed
112 113 114
        return;
    KoShapeGroup *group = dynamic_cast<KoShapeGroup*>(object->parent());
    if(group) {
115
        d->selectedShapes.removeAll(group);
Thomas Zander's avatar
Thomas Zander committed
116
        foreach(KoShape *shape, group->iterator())
117
            d->selectedShapes.removeAll(shape);
Thomas Zander's avatar
Thomas Zander committed
118 119
    }
    else
120
        d->selectedShapes.removeAll( object );
121

122
    if(d->selectedShapes.count() == 1)
123 124
        setTransformation( firstSelectedShape()->transformationMatrix(0) );

Thomas Zander's avatar
Thomas Zander committed
125 126 127 128 129
    requestSelectionChangedEvent();
}

void KoSelection::deselectAll()
{
130 131 132
    // reset the transformation matrix of the selection
    setTransformation( QMatrix() );

133
    if(d->selectedShapes.isEmpty())
Thomas Zander's avatar
Thomas Zander committed
134
        return;
135
    d->selectedShapes.clear();
Thomas Zander's avatar
Thomas Zander committed
136 137 138 139
    requestSelectionChangedEvent();
}

void KoSelection::requestSelectionChangedEvent() {
140
    if(d->eventTriggered)
Thomas Zander's avatar
Thomas Zander committed
141
        return;
142
    d->eventTriggered = true;
Thomas Zander's avatar
Thomas Zander committed
143 144 145 146
    QTimer::singleShot(0, this, SLOT(selectionChangedEvent()));
}

void KoSelection::selectionChangedEvent() {
147
    d->eventTriggered = false;
148
    setScale(1,1);
149 150
    //just as well use the oppertunity to update the size and position
    updateSizeAndPosition();
Thomas Zander's avatar
Thomas Zander committed
151 152 153 154 155
    emit selectionChanged();
}

int KoSelection::count() const
{
156
    return d->selectedShapes.count();
Thomas Zander's avatar
Thomas Zander committed
157 158 159 160 161 162
}

bool KoSelection::hitTest( const QPointF &position ) const
{
    if ( count() > 1 )
    {
163
        QRectF bb( boundingRect() );
Thomas Zander's avatar
Thomas Zander committed
164 165 166
        return bb.contains( position );
    }
    else if ( count() == 1 )
167
        return ( *d->selectedShapes.begin() )->hitTest( position );
Thomas Zander's avatar
Thomas Zander committed
168 169 170
    else // count == 0
        return false;
}
171 172 173 174
void KoSelection::updateSizeAndPosition()
{
    QRectF bb = sizeRect();
    QMatrix matrix = transformationMatrix(0);
175
    setSize( bb.size() );
176 177 178
    QPointF p = matrix.map(bb.topLeft() + matrix.inverted().map( position()) );
    setPosition( p );
}
Thomas Zander's avatar
Thomas Zander committed
179

180
QRectF KoSelection::sizeRect() const
Thomas Zander's avatar
Thomas Zander committed
181 182 183
{
    bool first=true;
    QRectF bb;
184

185
    QMatrix itmat = transformationMatrix(0).inverted();
186

187
    if ( !d->selectedShapes.isEmpty() )
Thomas Zander's avatar
Thomas Zander committed
188
    {
189 190
        QList<KoShape*>::const_iterator it = d->selectedShapes.begin();
        for ( ; it != d->selectedShapes.end(); ++it ) {
Thomas Zander's avatar
Thomas Zander committed
191 192 193
            if( dynamic_cast<KoShapeGroup*>( *it ))
                continue;
            if(first) {
194
                bb = ((*it)->transformationMatrix(0) * itmat).mapRect(QRectF(QPointF(),(*it)->size()));
Thomas Zander's avatar
Thomas Zander committed
195 196 197
                first = false;
            }
            else
198
                bb = bb.unite( ((*it)->transformationMatrix(0) * itmat).mapRect(QRectF(QPointF(),(*it)->size())) );
Thomas Zander's avatar
Thomas Zander committed
199 200
        }
    }
201

202 203
    return bb;
}
204

205 206 207
QRectF KoSelection::boundingRect() const
{
    return transformationMatrix(0).mapRect( sizeRect() );
Thomas Zander's avatar
Thomas Zander committed
208 209
}

210 211
const QList<KoShape*> KoSelection::selectedShapes(KoFlake::SelectionType strip) const {
    QList<KoShape*> answer;
Thomas Zander's avatar
Thomas Zander committed
212 213
    // strip the child objects when there is also a parent included.
    bool doStripping = strip == KoFlake::StrippedSelection;
214
    foreach (KoShape *shape, d->selectedShapes) {
Thomas Zander's avatar
Thomas Zander committed
215
        KoShapeContainer *container = shape->parent();
216 217
        if(strip != KoFlake::TopLevelSelection && dynamic_cast<KoShapeGroup*>(shape))
            // since a KoShapeGroup
218 219
            // guarentees all its children are selected at the same time as itself
            // is selected we will only return its children.
Thomas Zander's avatar
Thomas Zander committed
220 221 222
            continue;
        bool add = true;
        while(doStripping && add && container) {
223
            if(dynamic_cast<KoShapeGroup*>(container) == 0 && d->selectedShapes.contains(container))
Thomas Zander's avatar
Thomas Zander committed
224 225 226
                add = false;
            container = container->parent();
        }
227
        if(strip == KoFlake::TopLevelSelection && container && d->selectedShapes.contains(container))
228
            add = false;
Thomas Zander's avatar
Thomas Zander committed
229 230 231 232 233 234 235 236 237 238
        if(add)
            answer << shape;
    }
    return answer;
}

bool KoSelection::isSelected(const KoShape *object) const {
    if(object == this)
        return true;

239
    foreach(KoShape *shape, d->selectedShapes)
240
        if(shape == object)
Thomas Zander's avatar
Thomas Zander committed
241 242 243 244
            return true;

    return false;
}
Laurent Montel's avatar
Laurent Montel committed
245

246
KoShape *KoSelection::firstSelectedShape(KoFlake::SelectionType strip) const {
247
    QList<KoShape*> set = selectedShapes(strip);
248 249 250 251 252
    if(set.isEmpty())
        return 0;
    return *(set.begin());
}

253
void KoSelection::setActiveLayer( KoShapeLayer* layer ) {
254
    d->activeLayer = layer;
255 256
}

257
KoShapeLayer* KoSelection::activeLayer() const {
258
    return d->activeLayer;
259 260
}

Laurent Montel's avatar
Laurent Montel committed
261
#include "KoSelection.moc"