libs/flake

KoSelection.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE project
00002 
00003    Copyright (C) 2006 Boudewijn Rempt <boud@valdyas.org>
00004    Copyright (C) 2006 Thorsten Zachmann <zachmann@kde.org>
00005    Copyright (C) 2006 Jan Hambrecht <jaham@gmx.net>
00006    Copyright (C) 2006-2007,2009 Thomas Zander <zander@kde.org>
00007 
00008    This library is free software; you can redistribute it and/or
00009    modify it under the terms of the GNU Library General Public
00010    License as published by the Free Software Foundation; either
00011    version 2 of the License, or (at your option) any later version.
00012 
00013    This library is distributed in the hope that it will be useful,
00014    but WITHOUT ANY WARRANTY; without even the implied warranty of
00015    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016    Library General Public License for more details.
00017 
00018    You should have received a copy of the GNU Library General Public License
00019    along with this library; see the file COPYING.LIB.  If not, write to
00020    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00021  * Boston, MA 02110-1301, USA.
00022 */
00023 
00024 #include "KoSelection.h"
00025 #include "KoSelection_p.h"
00026 #include "KoShapeContainer.h"
00027 #include "KoShapeGroup.h"
00028 #include "KoPointerEvent.h"
00029 
00030 #include <QPainter>
00031 #include <QTimer>
00032 
00033 QRectF KoSelectionPrivate::sizeRect()
00034 {
00035     bool first = true;
00036     QRectF bb;
00037 
00038     QMatrix invSelectionTransform = q->absoluteTransformation(0).inverted();
00039 
00040     QRectF bound;
00041 
00042     if (!selectedShapes.isEmpty()) {
00043         QList<KoShape*>::const_iterator it = selectedShapes.constBegin();
00044         for (; it != selectedShapes.constEnd(); ++it) {
00045             if (dynamic_cast<KoShapeGroup*>(*it))
00046                 continue;
00047 
00048             const QMatrix shapeTransform = (*it)->absoluteTransformation(0);
00049             const QRectF shapeRect(QRectF(QPointF(), (*it)->size()));
00050 
00051             if (first) {
00052                 bb = (shapeTransform * invSelectionTransform).mapRect(shapeRect);
00053                 bound = shapeTransform.mapRect( shapeRect );
00054                 first = false;
00055             } else {
00056                 bb = bb.united((shapeTransform * invSelectionTransform).mapRect(shapeRect));
00057                 bound = bound.united( shapeTransform.mapRect( shapeRect ) );
00058             }
00059         }
00060     }
00061 
00062     globalBound = bound;
00063     return bb;
00064 }
00065 
00066 void KoSelectionPrivate::requestSelectionChangedEvent()
00067 {
00068     if (eventTriggered)
00069         return;
00070     eventTriggered = true;
00071     QTimer::singleShot(0, q, SLOT(selectionChangedEvent()));
00072 }
00073 
00074 void KoSelectionPrivate::selectionChangedEvent()
00075 {
00076     eventTriggered = false;
00077     q->setScale(1, 1);
00078     emit q->selectionChanged();
00079 }
00080 
00081 void KoSelectionPrivate::selectGroupChildren(KoShapeGroup *group)
00082 {
00083     if (! group)
00084         return;
00085 
00086     foreach(KoShape *shape, group->childShapes()) {
00087         if (selectedShapes.contains(shape))
00088             continue;
00089         selectedShapes << shape;
00090 
00091         KoShapeGroup* childGroup = dynamic_cast<KoShapeGroup*>(shape);
00092         if (childGroup)
00093             selectGroupChildren(childGroup);
00094     }
00095 }
00096 
00097 void KoSelectionPrivate::deselectGroupChildren(KoShapeGroup *group)
00098 {
00099     if (! group)
00100         return;
00101 
00102     foreach(KoShape *shape, group->childShapes()) {
00103         if (selectedShapes.contains(shape))
00104             selectedShapes.removeAll(shape);
00105 
00106         KoShapeGroup* childGroup = dynamic_cast<KoShapeGroup*>(shape);
00107         if (childGroup)
00108             deselectGroupChildren(childGroup);
00109     }
00110 }
00111 
00113 
00114 KoSelection::KoSelection()
00115     : KoShape(*(new KoSelectionPrivate(this)))
00116 {
00117 }
00118 
00119 KoSelection::~KoSelection()
00120 {
00121 }
00122 
00123 void KoSelection::paint(QPainter &painter, const KoViewConverter &converter)
00124 {
00125     Q_UNUSED(painter);
00126     Q_UNUSED(converter);
00127 }
00128 
00129 void KoSelection::select(KoShape *shape, bool recursive)
00130 {
00131     Q_D(KoSelection);
00132     Q_ASSERT(shape != this);
00133     Q_ASSERT(shape);
00134     if (!shape->isSelectable() || !shape->isVisible(true))
00135         return;
00136 
00137     // save old number of selected shapes
00138     uint oldSelectionCount = d->selectedShapes.count();
00139 
00140     if (!d->selectedShapes.contains(shape))
00141         d->selectedShapes << shape;
00142 
00143     // automatically recursively select all child shapes downwards in the hierarchy
00144     KoShapeGroup* group = dynamic_cast<KoShapeGroup*>(shape);
00145     if (group)
00146         d->selectGroupChildren(group);
00147 
00148     if (recursive) {
00149         // recursively select all parents and their children upwards the hierarchy
00150         KoShapeContainer *parent = shape->parent();
00151         while (parent) {
00152             KoShapeGroup *parentGroup = dynamic_cast<KoShapeGroup*>(parent);
00153             if (! parentGroup) break;
00154             if (! d->selectedShapes.contains(parentGroup)) {
00155                 d->selectedShapes << parentGroup;
00156                 d->selectGroupChildren(parentGroup);
00157             }
00158             parent = parentGroup->parent();
00159         }
00160     }
00161 
00162     if (d->selectedShapes.count() == 1) {
00163         setTransformation(shape->absoluteTransformation(0));
00164         updateSizeAndPosition();
00165     }
00166     else {
00167         // reset global bound if there were no shapes selected before
00168         if( ! oldSelectionCount )
00169             d->globalBound = QRectF();
00170 
00171         setTransformation(QMatrix());
00172         // we are resetting the transformation here anyway,
00173         // so we can just add the newly selected shapes to the bounding box
00174         // in document coordinates and then use that size and position
00175         uint newSelectionCount = d->selectedShapes.count();
00176         for( uint i = oldSelectionCount; i < newSelectionCount; ++i ) {
00177             KoShape * shape = d->selectedShapes[i];
00178             const QMatrix shapeTransform = shape->absoluteTransformation(0);
00179             const QRectF shapeRect(QRectF(QPointF(), shape->size()));
00180 
00181             d->globalBound = d->globalBound.united( shapeTransform.mapRect( shapeRect ) );
00182         }
00183         setSize(d->globalBound.size());
00184         setPosition(d->globalBound.topLeft());
00185     }
00186 
00187     d->requestSelectionChangedEvent();
00188 }
00189 
00190 void KoSelection::deselect(KoShape *shape, bool recursive)
00191 {
00192     Q_D(KoSelection);
00193     if (! d->selectedShapes.contains(shape))
00194         return;
00195 
00196     d->selectedShapes.removeAll(shape);
00197 
00198     KoShapeGroup *group = dynamic_cast<KoShapeGroup*>(shape);
00199     if (recursive) {
00200         // recursively find the top group upwards int the hierarchy
00201         KoShapeGroup *parentGroup = dynamic_cast<KoShapeGroup*>(shape->parent());
00202         while (parentGroup) {
00203             group = parentGroup;
00204             parentGroup = dynamic_cast<KoShapeGroup*>(parentGroup->parent());
00205         }
00206     }
00207     if (group)
00208         d->deselectGroupChildren(group);
00209 
00210     if (count() == 1)
00211         setTransformation(firstSelectedShape()->absoluteTransformation(0));
00212 
00213     updateSizeAndPosition();
00214 
00215     d->requestSelectionChangedEvent();
00216 }
00217 
00218 void KoSelection::deselectAll()
00219 {
00220     Q_D(KoSelection);
00221     // reset the transformation matrix of the selection
00222     setTransformation(QMatrix());
00223 
00224     if (d->selectedShapes.isEmpty())
00225         return;
00226     d->selectedShapes.clear();
00227     d->requestSelectionChangedEvent();
00228 }
00229 
00230 int KoSelection::count() const
00231 {
00232     Q_D(const KoSelection);
00233     int count = 0;
00234     foreach(KoShape *shape, d->selectedShapes)
00235         if (dynamic_cast<KoShapeGroup*>(shape) == 0)
00236             ++count;
00237     return count;
00238 }
00239 
00240 bool KoSelection::hitTest(const QPointF &position) const
00241 {
00242     Q_D(const KoSelection);
00243     if (count() > 1) {
00244         QRectF bb(boundingRect());
00245         return bb.contains(position);
00246     } else if (count() == 1)
00247         return (*d->selectedShapes.begin())->hitTest(position);
00248     else // count == 0
00249         return false;
00250 }
00251 void KoSelection::updateSizeAndPosition()
00252 {
00253     Q_D(KoSelection);
00254     QRectF bb = d->sizeRect();
00255     QMatrix matrix = absoluteTransformation(0);
00256     setSize(bb.size());
00257     QPointF p = matrix.map(bb.topLeft() + matrix.inverted().map(position()));
00258     setPosition(p);
00259 }
00260 
00261 QRectF KoSelection::boundingRect() const
00262 {
00263     return absoluteTransformation(0).mapRect(QRectF(QPointF(), size()));
00264 }
00265 
00266 const QList<KoShape*> KoSelection::selectedShapes(KoFlake::SelectionType strip) const
00267 {
00268     Q_D(const KoSelection);
00269     QList<KoShape*> answer;
00270     // strip the child objects when there is also a parent included.
00271     bool doStripping = strip == KoFlake::StrippedSelection;
00272     foreach(KoShape *shape, d->selectedShapes) {
00273         KoShapeContainer *container = shape->parent();
00274         if (strip != KoFlake::TopLevelSelection && dynamic_cast<KoShapeGroup*>(shape))
00275             // since a KoShapeGroup
00276             // guarentees all its children are selected at the same time as itself
00277             // is selected we will only return its children.
00278             continue;
00279         bool add = true;
00280         while (doStripping && add && container) {
00281             if (dynamic_cast<KoShapeGroup*>(container) == 0 && d->selectedShapes.contains(container))
00282                 add = false;
00283             container = container->parent();
00284         }
00285         if (strip == KoFlake::TopLevelSelection && container && d->selectedShapes.contains(container))
00286             add = false;
00287         if (add)
00288             answer << shape;
00289     }
00290     return answer;
00291 }
00292 
00293 bool KoSelection::isSelected(const KoShape *shape) const
00294 {
00295     Q_D(const KoSelection);
00296     if (shape == this)
00297         return true;
00298 
00299     foreach (KoShape *s, d->selectedShapes) {
00300         if (s == shape)
00301             return true;
00302     }
00303 
00304     return false;
00305 }
00306 
00307 KoShape *KoSelection::firstSelectedShape(KoFlake::SelectionType strip) const
00308 {
00309     QList<KoShape*> set = selectedShapes(strip);
00310     if (set.isEmpty())
00311         return 0;
00312     return *(set.begin());
00313 }
00314 
00315 void KoSelection::setActiveLayer(KoShapeLayer* layer)
00316 {
00317     Q_D(KoSelection);
00318     d->activeLayer = layer;
00319     emit currentLayerChanged(layer);
00320 }
00321 
00322 KoShapeLayer* KoSelection::activeLayer() const
00323 {
00324     Q_D(const KoSelection);
00325     return d->activeLayer;
00326 }
00327 
00328 void KoSelection::saveOdf(KoShapeSavingContext &) const
00329 {
00330 }
00331 
00332 bool KoSelection::loadOdf(const KoXmlElement &, KoShapeLoadingContext &)
00333 {
00334     return true;
00335 }
00336 
00337 #include "KoSelection.moc"