Warning, file /office/calligra/libs/flake/KoSelection.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* This file is part of the KDE project
0002 
0003    Copyright (C) 2006 Boudewijn Rempt <boud@valdyas.org>
0004    Copyright (C) 2006 Thorsten Zachmann <zachmann@kde.org>
0005    Copyright (C) 2006 Jan Hambrecht <jaham@gmx.net>
0006    Copyright (C) 2006-2007,2009 Thomas Zander <zander@kde.org>
0007 
0008    This library is free software; you can redistribute it and/or
0009    modify it under the terms of the GNU Library General Public
0010    License as published by the Free Software Foundation; either
0011    version 2 of the License, or (at your option) any later version.
0012 
0013    This library is distributed in the hope that it will be useful,
0014    but WITHOUT ANY WARRANTY; without even the implied warranty of
0015    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0016    Library General Public License for more details.
0017 
0018    You should have received a copy of the GNU Library General Public License
0019    along with this library; see the file COPYING.LIB.  If not, write to
0020    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0021  * Boston, MA 02110-1301, USA.
0022 */
0023 
0024 #include "KoSelection.h"
0025 #include "KoSelection_p.h"
0026 #include "KoShapeContainer.h"
0027 #include "KoShapeGroup.h"
0028 #include "KoPointerEvent.h"
0029 #include "KoShapePaintingContext.h"
0030 
0031 #include <QPainter>
0032 #include <QTimer>
0033 
0034 QRectF KoSelectionPrivate::sizeRect()
0035 {
0036     bool first = true;
0037     QRectF bb;
0038 
0039     QTransform invSelectionTransform = q->absoluteTransformation(0).inverted();
0040 
0041     QRectF bound;
0042 
0043     if (!selectedShapes.isEmpty()) {
0044         QList<KoShape*>::const_iterator it = selectedShapes.constBegin();
0045         for (; it != selectedShapes.constEnd(); ++it) {
0046             if (dynamic_cast<KoShapeGroup*>(*it))
0047                 continue;
0048 
0049             const QTransform shapeTransform = (*it)->absoluteTransformation(0);
0050             const QRectF shapeRect(QRectF(QPointF(), (*it)->size()));
0051 
0052             if (first) {
0053                 bb = (shapeTransform * invSelectionTransform).mapRect(shapeRect);
0054                 bound = shapeTransform.mapRect(shapeRect);
0055                 first = false;
0056             } else {
0057                 bb = bb.united((shapeTransform * invSelectionTransform).mapRect(shapeRect));
0058                 bound = bound.united(shapeTransform.mapRect(shapeRect));
0059             }
0060         }
0061     }
0062 
0063     globalBound = bound;
0064     return bb;
0065 }
0066 
0067 void KoSelectionPrivate::requestSelectionChangedEvent()
0068 {
0069     if (eventTriggered)
0070         return;
0071     eventTriggered = true;
0072     QTimer::singleShot(0, q, SLOT(selectionChangedEvent()));
0073 }
0074 
0075 void KoSelectionPrivate::selectionChangedEvent()
0076 {
0077     eventTriggered = false;
0078     emit q->selectionChanged();
0079 }
0080 
0081 void KoSelectionPrivate::selectGroupChildren(KoShapeGroup *group)
0082 {
0083     if (! group)
0084         return;
0085 
0086     foreach(KoShape *shape, group->shapes()) {
0087         if (selectedShapes.contains(shape))
0088             continue;
0089         selectedShapes << shape;
0090 
0091         KoShapeGroup *childGroup = dynamic_cast<KoShapeGroup*>(shape);
0092         if (childGroup)
0093             selectGroupChildren(childGroup);
0094     }
0095 }
0096 
0097 void KoSelectionPrivate::deselectGroupChildren(KoShapeGroup *group)
0098 {
0099     if (! group)
0100         return;
0101 
0102     foreach(KoShape *shape, group->shapes()) {
0103         if (selectedShapes.contains(shape))
0104             selectedShapes.removeAll(shape);
0105 
0106         KoShapeGroup *childGroup = dynamic_cast<KoShapeGroup*>(shape);
0107         if (childGroup)
0108             deselectGroupChildren(childGroup);
0109     }
0110 }
0111 
0112 ////////////
0113 
0114 KoSelection::KoSelection()
0115     : KoShape(*(new KoSelectionPrivate(this)))
0116 {
0117 }
0118 
0119 KoSelection::~KoSelection()
0120 {
0121 }
0122 
0123 void KoSelection::paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintcontext)
0124 {
0125     Q_UNUSED(painter);
0126     Q_UNUSED(converter);
0127     Q_UNUSED(paintcontext);
0128 }
0129 
0130 void KoSelection::select(KoShape *shape, bool recursive)
0131 {
0132     Q_D(KoSelection);
0133     Q_ASSERT(shape != this);
0134     Q_ASSERT(shape);
0135     if (!shape->isSelectable() || !shape->isVisible(true))
0136         return;
0137 
0138     // save old number of selected shapes
0139     int oldSelectionCount = d->selectedShapes.count();
0140 
0141     if (!d->selectedShapes.contains(shape))
0142         d->selectedShapes << shape;
0143 
0144     // automatically recursively select all child shapes downwards in the hierarchy
0145     KoShapeGroup *group = dynamic_cast<KoShapeGroup*>(shape);
0146     if (group)
0147         d->selectGroupChildren(group);
0148 
0149     if (recursive) {
0150         // recursively select all parents and their children upwards the hierarchy
0151         KoShapeContainer *parent = shape->parent();
0152         while (parent) {
0153             KoShapeGroup *parentGroup = dynamic_cast<KoShapeGroup*>(parent);
0154             if (! parentGroup) break;
0155             if (! d->selectedShapes.contains(parentGroup)) {
0156                 d->selectedShapes << parentGroup;
0157                 d->selectGroupChildren(parentGroup);
0158             }
0159             parent = parentGroup->parent();
0160         }
0161     }
0162 
0163     if (d->selectedShapes.count() == 1) {
0164         setTransformation(shape->absoluteTransformation(0));
0165         updateSizeAndPosition();
0166     } else {
0167         // reset global bound if there were no shapes selected before
0168         if (!oldSelectionCount)
0169             d->globalBound = QRectF();
0170 
0171         setTransformation(QTransform());
0172         // we are resetting the transformation here anyway,
0173         // so we can just add the newly selected shapes to the bounding box
0174         // in document coordinates and then use that size and position
0175         int newSelectionCount = d->selectedShapes.count();
0176         for (int i = oldSelectionCount; i < newSelectionCount; ++i) {
0177             KoShape *shape = d->selectedShapes[i];
0178 
0179             // don't add the rect of the group rect, as it can be invalid
0180             if (dynamic_cast<KoShapeGroup*>(shape)) {
0181                 continue;
0182             }
0183             const QTransform shapeTransform = shape->absoluteTransformation(0);
0184             const QRectF shapeRect(QRectF(QPointF(), shape->size()));
0185 
0186             d->globalBound = d->globalBound.united(shapeTransform.mapRect(shapeRect));
0187         }
0188         setSize(d->globalBound.size());
0189         setPosition(d->globalBound.topLeft());
0190     }
0191 
0192     d->requestSelectionChangedEvent();
0193 }
0194 
0195 void KoSelection::deselect(KoShape *shape, bool recursive)
0196 {
0197     Q_D(KoSelection);
0198     if (! d->selectedShapes.contains(shape))
0199         return;
0200 
0201     d->selectedShapes.removeAll(shape);
0202 
0203     KoShapeGroup *group = dynamic_cast<KoShapeGroup*>(shape);
0204     if (recursive) {
0205         // recursively find the top group upwards int the hierarchy
0206         KoShapeGroup *parentGroup = dynamic_cast<KoShapeGroup*>(shape->parent());
0207         while (parentGroup) {
0208             group = parentGroup;
0209             parentGroup = dynamic_cast<KoShapeGroup*>(parentGroup->parent());
0210         }
0211     }
0212     if (group)
0213         d->deselectGroupChildren(group);
0214 
0215     if (count() == 1)
0216         setTransformation(firstSelectedShape()->absoluteTransformation(0));
0217 
0218     updateSizeAndPosition();
0219 
0220     d->requestSelectionChangedEvent();
0221 }
0222 
0223 void KoSelection::deselectAll()
0224 {
0225     Q_D(KoSelection);
0226     // reset the transformation matrix of the selection
0227     setTransformation(QTransform());
0228 
0229     if (d->selectedShapes.isEmpty())
0230         return;
0231     d->selectedShapes.clear();
0232     d->requestSelectionChangedEvent();
0233 }
0234 
0235 int KoSelection::count() const
0236 {
0237     Q_D(const KoSelection);
0238     int count = 0;
0239     foreach(KoShape *shape, d->selectedShapes)
0240         if (dynamic_cast<KoShapeGroup*>(shape) == 0)
0241             ++count;
0242     return count;
0243 }
0244 
0245 bool KoSelection::hitTest(const QPointF &position) const
0246 {
0247     Q_D(const KoSelection);
0248     if (count() > 1) {
0249         QRectF bb(boundingRect());
0250         return bb.contains(position);
0251     } else if (count() == 1) {
0252         return (*d->selectedShapes.begin())->hitTest(position);
0253     } else { // count == 0
0254         return false;
0255     }
0256 }
0257 void KoSelection::updateSizeAndPosition()
0258 {
0259     Q_D(KoSelection);
0260     QRectF bb = d->sizeRect();
0261     QTransform matrix = absoluteTransformation(0);
0262     setSize(bb.size());
0263     QPointF p = matrix.map(bb.topLeft() + matrix.inverted().map(position()));
0264     setPosition(p);
0265 }
0266 
0267 QRectF KoSelection::boundingRect() const
0268 {
0269     return absoluteTransformation(0).mapRect(QRectF(QPointF(), size()));
0270 }
0271 
0272 const QList<KoShape*> KoSelection::selectedShapes(KoFlake::SelectionType strip) const
0273 {
0274     Q_D(const KoSelection);
0275     QList<KoShape*> answer;
0276     // strip the child objects when there is also a parent included.
0277     bool doStripping = strip == KoFlake::StrippedSelection;
0278     foreach(KoShape *shape, d->selectedShapes) {
0279         KoShapeContainer *container = shape->parent();
0280         if (strip != KoFlake::TopLevelSelection && dynamic_cast<KoShapeGroup*>(shape))
0281             // since a KoShapeGroup
0282             // guarentees all its children are selected at the same time as itself
0283             // is selected we will only return its children.
0284             continue;
0285         bool add = true;
0286         while (doStripping && add && container) {
0287             if (dynamic_cast<KoShapeGroup*>(container) == 0 && d->selectedShapes.contains(container))
0288                 add = false;
0289             container = container->parent();
0290         }
0291         if (strip == KoFlake::TopLevelSelection && container && d->selectedShapes.contains(container))
0292             add = false;
0293         if (add)
0294             answer << shape;
0295     }
0296     return answer;
0297 }
0298 
0299 bool KoSelection::isSelected(const KoShape *shape) const
0300 {
0301     Q_D(const KoSelection);
0302     if (shape == this)
0303         return true;
0304 
0305     foreach (KoShape *s, d->selectedShapes) {
0306         if (s == shape)
0307             return true;
0308     }
0309 
0310     return false;
0311 }
0312 
0313 KoShape *KoSelection::firstSelectedShape(KoFlake::SelectionType strip) const
0314 {
0315     QList<KoShape*> set = selectedShapes(strip);
0316     if (set.isEmpty())
0317         return 0;
0318     return *(set.begin());
0319 }
0320 
0321 void KoSelection::setActiveLayer(KoShapeLayer *layer)
0322 {
0323     Q_D(KoSelection);
0324     d->activeLayer = layer;
0325     emit currentLayerChanged(layer);
0326 }
0327 
0328 KoShapeLayer* KoSelection::activeLayer() const
0329 {
0330     Q_D(const KoSelection);
0331     return d->activeLayer;
0332 }
0333 
0334 void KoSelection::saveOdf(KoShapeSavingContext &) const
0335 {
0336 }
0337 
0338 bool KoSelection::loadOdf(const KoXmlElement &, KoShapeLoadingContext &)
0339 {
0340     return true;
0341 }
0342 
0343 //have to include this because of Q_PRIVATE_SLOT
0344 #include "moc_KoSelection.cpp"