File indexing completed on 2024-05-19 04:24:57

0001 /* This file is part of the KDE project
0002 
0003    SPDX-FileCopyrightText: 2006-2008 Thorsten Zachmann <zachmann@kde.org>
0004    SPDX-FileCopyrightText: 2006-2010 Thomas Zander <zander@kde.org>
0005    SPDX-FileCopyrightText: 2009-2010 Jan Hambrecht <jaham@gmx.net>
0006 
0007    SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "KoShapeManager.h"
0011 #include "KoShapeManager_p.h"
0012 #include "KoSelection.h"
0013 #include "KoToolManager.h"
0014 #include "KoPointerEvent.h"
0015 #include "KoShape.h"
0016 #include "KoShape_p.h"
0017 #include "KoCanvasBase.h"
0018 #include "KoShapeContainer.h"
0019 #include "KoShapeStrokeModel.h"
0020 #include "KoShapeGroup.h"
0021 #include "KoToolProxy.h"
0022 #include "KoShapeShadow.h"
0023 #include "KoShapeLayer.h"
0024 #include "KoFilterEffect.h"
0025 #include "KoFilterEffectStack.h"
0026 #include "KoFilterEffectRenderContext.h"
0027 #include "KoShapeBackground.h"
0028 #include <KoRTree.h>
0029 #include "KoClipPath.h"
0030 #include "KoClipMaskPainter.h"
0031 #include "KoViewConverter.h"
0032 #include "KisQPainterStateSaver.h"
0033 #include "KoSvgTextShape.h"
0034 #include <QApplication>
0035 
0036 #include <QPainter>
0037 #include <QPainterPath>
0038 #include <QTimer>
0039 #include <QThread>
0040 #include <FlakeDebug.h>
0041 
0042 #include "kis_painting_tweaks.h"
0043 #include "kis_debug.h"
0044 #include "KisForest.h"
0045 #include <unordered_set>
0046 
0047 
0048 namespace {
0049 
0050 /**
0051  * Returns whether the shape should be added to the RTree for collision and ROI
0052  * detection.
0053  */
0054 inline bool shapeUsedInRenderingTree(KoShape *shape)
0055 {
0056     // FIXME: make more general!
0057 
0058     return !dynamic_cast<KoShapeGroup*>(shape) &&
0059             !dynamic_cast<KoShapeLayer*>(shape);
0060 }
0061 
0062 /**
0063  * Returns whether a shape should be added to the rendering tree because of
0064  * its clip mask/path or effects.
0065  */
0066 inline bool shapeHasGroupEffects(KoShape *shape) {
0067     return shape->clipPath() ||
0068         (shape->filterEffectStack() && !shape->filterEffectStack()->isEmpty()) ||
0069         shape->clipMask();
0070 }
0071 
0072 /**
0073  * Returns true if the shape is not fully transparent
0074  */
0075 inline bool shapeIsVisible(KoShape *shape) {
0076     return shape->isVisible(false) && shape->transparency() < 1.0;
0077 }
0078 
0079 
0080 /**
0081  * Populate \p tree with the subtree of shapes pointed by a shape \p parentShape.
0082  * All new shapes are added as children of \p parentIt. Please take it into account
0083  * that \c *parentIt might be not the same as \c parentShape, because \c parentShape
0084  * may be hidden from rendering.
0085  */
0086 void populateRenderSubtree(KoShape *parentShape,
0087                            KisForest<KoShape*>::child_iterator parentIt,
0088                            KisForest<KoShape*> &tree,
0089                            std::function<bool(KoShape*)> shouldIncludeNode,
0090                            std::function<bool(KoShape*)> shouldEnterSubtree)
0091 {
0092     KoShapeContainer *parentContainer = dynamic_cast<KoShapeContainer*>(parentShape);
0093     if (!parentContainer) return;
0094 
0095     QList<KoShape*> children = parentContainer->shapes();
0096     std::sort(children.begin(), children.end(), KoShape::compareShapeZIndex);
0097 
0098     for (auto it = children.constBegin(); it != children.constEnd(); ++it) {
0099         auto newParentIt = parentIt;
0100 
0101         if (shouldIncludeNode(*it)) {
0102             newParentIt = tree.insert(childEnd(parentIt), *it);
0103         }
0104 
0105         if (shouldEnterSubtree(*it)) {
0106             populateRenderSubtree(*it, newParentIt, tree, shouldIncludeNode, shouldEnterSubtree);
0107         }
0108     }
0109 
0110 }
0111 
0112 /**
0113  * Build a rendering tree for **leaf** nodes defined by \p leafNodes
0114  *
0115  * Sometimes we should render only a part of the layer (e.g. when we render
0116  * in patches). So we shouldn't render the whole graph. The problem is that
0117  * some of the shapes may have parents with clip paths/masks and/or effects.
0118  * In such a case, these parents should be also included into the rendering
0119  * process.
0120  *
0121  * \c buildRenderTree() builds a graph for such rendering. It includes the
0122  * leaf shapes themselves, and all parent shapes that have some effects affecting
0123  * these shapes.
0124  */
0125 void buildRenderTree(QList<KoShape*> leafShapes,
0126                      KisForest<KoShape*> &tree)
0127 {
0128     QList<KoShape*> sortedShapes = leafShapes;
0129     std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex);
0130 
0131     std::unordered_set<KoShape*> includedShapes;
0132 
0133     Q_FOREACH (KoShape *shape, sortedShapes) {
0134         bool shouldSkipShape = !shapeIsVisible(shape);
0135         if (shouldSkipShape) continue;
0136 
0137         bool shapeIsPartOfIncludedSubtree = false;
0138         QVector<KoShape*> hierarchy = {shape};
0139 
0140         while ((shape = shape->parent())) {
0141             if (!shapeIsVisible(shape)) {
0142                 shouldSkipShape = true;
0143                 break;
0144             }
0145 
0146             if (includedShapes.find(shape) != end(includedShapes)) {
0147                 shapeIsPartOfIncludedSubtree = true;
0148                 break;
0149             }
0150 
0151             if (shapeHasGroupEffects(shape)) {
0152                 hierarchy << shape;
0153             }
0154         }
0155 
0156         if (shouldSkipShape) continue;
0157 
0158         if (!shapeIsPartOfIncludedSubtree &&
0159             includedShapes.find(hierarchy.last()) == end(includedShapes)) {
0160 
0161             tree.insert(childEnd(tree), hierarchy.last());
0162         }
0163         std::copy(hierarchy.begin(), hierarchy.end(),
0164                   std::inserter(includedShapes, end(includedShapes)));
0165     }
0166 
0167     auto shouldIncludeShape =
0168         [includedShapes] (KoShape *shape) {
0169             // included shapes are guaranteed to be visible
0170             return includedShapes.find(shape) != end(includedShapes);
0171         };
0172 
0173     for (auto it = childBegin(tree); it != childEnd(tree); ++it) {
0174         populateRenderSubtree(*it, it, tree, shouldIncludeShape, &shapeIsVisible);
0175     }
0176 }
0177 
0178 /**
0179  * Render the prebuilt rendering tree on \p painter
0180  */
0181 void renderShapes(typename KisForest<KoShape*>::child_iterator beginIt,
0182                   typename KisForest<KoShape*>::child_iterator endIt,
0183                   QPainter &painter)
0184 {
0185     for (auto it = beginIt; it != endIt; ++it) {
0186         KoShape *shape = *it;
0187 
0188         KisQPainterStateSaver saver(&painter);
0189 
0190         if (!isEnd(parent(it))) {
0191             painter.setTransform(shape->transformation() * painter.transform());
0192         } else {
0193             painter.setTransform(shape->absoluteTransformation() * painter.transform());
0194         }
0195 
0196         KoClipPath::applyClipping(shape, painter);
0197 
0198         qreal transparency = shape->transparency(true);
0199         if (transparency > 0.0) {
0200             painter.setOpacity(1.0-transparency);
0201         }
0202 
0203         if (shape->shadow()) {
0204             KisQPainterStateSaver saver(&painter);
0205             shape->shadow()->paint(shape, painter);
0206         }
0207 
0208         QScopedPointer<KoClipMaskPainter> clipMaskPainter;
0209         QPainter *shapePainter = &painter;
0210 
0211         KoClipMask *clipMask = shape->clipMask();
0212         if (clipMask) {
0213             const QRectF bounds = painter.transform().mapRect(shape->outlineRect());
0214 
0215             clipMaskPainter.reset(new KoClipMaskPainter(&painter, bounds/*shape->boundingRect())*/));
0216             shapePainter = clipMaskPainter->shapePainter();
0217         }
0218 
0219         /**
0220          * We expect the shape to save/restore the painter's state itself. Such design was not
0221          * not always here, so we need a period of sanity checks to ensure all the shapes are
0222          * ported correctly.
0223          */
0224         const QTransform sanityCheckTransformSaved = shapePainter->transform();
0225 
0226         renderShapes(childBegin(it), childEnd(it), *shapePainter);
0227 
0228         Q_FOREACH(const KoShape::PaintOrder p, shape->paintOrder()) {
0229             if (p == KoShape::Fill) {
0230                 shape->paint(*shapePainter);
0231             } else if (p == KoShape::Stroke) {
0232                 shape->paintStroke(*shapePainter);
0233             } else if (p == KoShape::Markers)  {
0234                 shape->paintMarkers(*shapePainter);
0235             }
0236         }
0237 
0238         KIS_SAFE_ASSERT_RECOVER(shapePainter->transform() == sanityCheckTransformSaved) {
0239             shapePainter->setTransform(sanityCheckTransformSaved);
0240         }
0241 
0242         if (clipMask) {
0243             clipMaskPainter->maskPainter()->save();
0244 
0245             shape->clipMask()->drawMask(clipMaskPainter->maskPainter(), shape);
0246             clipMaskPainter->renderOnGlobalPainter();
0247 
0248             clipMaskPainter->maskPainter()->restore();
0249         }
0250     }
0251 }
0252 
0253 }
0254 
0255 void KoShapeManager::Private::updateTree()
0256 {
0257     bool selectionModified = false;
0258     bool anyModified = false;
0259 
0260     {
0261         QMutexLocker l(&this->treeMutex);
0262 
0263         Q_FOREACH (KoShape *shape, aggregate4update) {
0264             selectionModified = selectionModified || selection->isSelected(shape);
0265             anyModified = true;
0266         }
0267 
0268         foreach (KoShape *shape, aggregate4update) {
0269             if (!shapeUsedInRenderingTree(shape)) continue;
0270 
0271             tree.remove(shape);
0272             QRectF br(shape->boundingRect());
0273             tree.insert(br, shape);
0274         }
0275 
0276         aggregate4update.clear();
0277         shapeIndexesBeforeUpdate.clear();
0278     }
0279 
0280     if (selectionModified) {
0281         emit q->selectionContentChanged();
0282     }
0283     if (anyModified) {
0284         emit q->contentChanged();
0285     }
0286 }
0287 
0288 void KoShapeManager::Private::forwardCompressedUpdate()
0289 {
0290     bool shouldUpdateDecorations = false;
0291     QRectF scheduledUpdate;
0292 
0293     {
0294         QMutexLocker l(&shapesMutex);
0295 
0296         if (!compressedUpdate.isEmpty()) {
0297             scheduledUpdate = compressedUpdate;
0298             compressedUpdate = QRect();
0299         }
0300 
0301         Q_FOREACH (const KoShape *shape, compressedUpdatedShapes) {
0302             if (selection->isSelected(shape)) {
0303                 shouldUpdateDecorations = true;
0304                 break;
0305             }
0306         }
0307         compressedUpdatedShapes.clear();
0308     }
0309 
0310     if (shouldUpdateDecorations && canvas->toolProxy()) {
0311         canvas->toolProxy()->repaintDecorations();
0312     }
0313     canvas->updateCanvas(scheduledUpdate);
0314 
0315 }
0316 
0317 KoShapeManager::KoShapeManager(KoCanvasBase *canvas, const QList<KoShape *> &shapes)
0318     : d(new Private(this, canvas))
0319 {
0320     Q_ASSERT(d->canvas); // not optional.
0321     connect(d->selection, SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged()));
0322     setShapes(shapes);
0323 
0324     /**
0325      * Shape manager uses uses queued signals, therefore it should belong
0326      * to the GUI thread.
0327      */
0328     this->moveToThread(qApp->thread());
0329     connect(this, SIGNAL(forwardUpdate()), this, SLOT(forwardCompressedUpdate()));
0330 }
0331 
0332 KoShapeManager::KoShapeManager(KoCanvasBase *canvas)
0333     : d(new Private(this, canvas))
0334 {
0335     Q_ASSERT(d->canvas); // not optional.
0336     connect(d->selection, SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged()));
0337 
0338     // see a comment in another constructor
0339     this->moveToThread(qApp->thread());
0340     connect(this, SIGNAL(forwardUpdate()), this, SLOT(forwardCompressedUpdate()));
0341 }
0342 
0343 void KoShapeManager::Private::unlinkFromShapesRecursively(const QList<KoShape*> &shapes)
0344 {
0345     Q_FOREACH (KoShape *shape, shapes) {
0346         shape->removeShapeManager(q);
0347 
0348         KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
0349         if (container) {
0350             unlinkFromShapesRecursively(container->shapes());
0351         }
0352     }
0353 }
0354 
0355 KoShapeManager::~KoShapeManager()
0356 {
0357     d->unlinkFromShapesRecursively(d->shapes);
0358     d->shapes.clear();
0359 
0360     delete d;
0361 }
0362 
0363 void KoShapeManager::setShapes(const QList<KoShape *> &shapes, Repaint repaint)
0364 {
0365     {
0366         QMutexLocker l1(&d->shapesMutex);
0367         QMutexLocker l2(&d->treeMutex);
0368 
0369         //clear selection
0370         d->selection->deselectAll();
0371         d->unlinkFromShapesRecursively(d->shapes);
0372         d->compressedUpdate = QRect();
0373         d->compressedUpdatedShapes.clear();
0374         d->aggregate4update.clear();
0375         d->shapeIndexesBeforeUpdate.clear();
0376         d->tree.clear();
0377         d->shapes.clear();
0378     }
0379 
0380     Q_FOREACH (KoShape *shape, shapes) {
0381         addShape(shape, repaint);
0382     }
0383 }
0384 
0385 void KoShapeManager::addShape(KoShape *shape, Repaint repaint)
0386 {
0387     {
0388         QMutexLocker l1(&d->shapesMutex);
0389 
0390         if (d->shapes.contains(shape))
0391             return;
0392         shape->addShapeManager(this);
0393         d->shapes.append(shape);
0394 
0395         if (shapeUsedInRenderingTree(shape)) {
0396             QMutexLocker l2(&d->treeMutex);
0397 
0398             QRectF br(shape->boundingRect());
0399             d->tree.insert(br, shape);
0400         }
0401     }
0402 
0403     if (repaint == PaintShapeOnAdd) {
0404         shape->update();
0405     }
0406 
0407     // add the children of a KoShapeContainer
0408     KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
0409 
0410     if (container) {
0411         foreach (KoShape *containerShape, container->shapes()) {
0412             addShape(containerShape, repaint);
0413         }
0414     }
0415 }
0416 
0417 void KoShapeManager::remove(KoShape *shape)
0418 {
0419     QRectF dirtyRect;
0420     {
0421         QMutexLocker l1(&d->shapesMutex);
0422         QMutexLocker l2(&d->treeMutex);
0423 
0424         dirtyRect = shape->boundingRect();
0425 
0426         shape->removeShapeManager(this);
0427         d->selection->deselect(shape);
0428         d->aggregate4update.remove(shape);
0429         d->compressedUpdatedShapes.remove(shape);
0430 
0431         if (shapeUsedInRenderingTree(shape)) {
0432             d->tree.remove(shape);
0433         }
0434         d->shapes.removeAll(shape);
0435     }
0436 
0437     if (!dirtyRect.isEmpty()) {
0438         d->canvas->updateCanvas(dirtyRect);
0439     }
0440 
0441     // remove the children of a KoShapeContainer
0442     KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
0443     if (container) {
0444         foreach (KoShape *containerShape, container->shapes()) {
0445             remove(containerShape);
0446         }
0447     }
0448 }
0449 
0450 KoShapeManager::ShapeInterface::ShapeInterface(KoShapeManager *_q)
0451     : q(_q)
0452 {
0453 }
0454 
0455 void KoShapeManager::ShapeInterface::notifyShapeDestructed(KoShape *shape)
0456 {
0457     QMutexLocker l1(&q->d->shapesMutex);
0458     QMutexLocker l2(&q->d->treeMutex);
0459 
0460     q->d->selection->deselect(shape);
0461     q->d->aggregate4update.remove(shape);
0462     q->d->compressedUpdatedShapes.remove(shape);
0463 
0464     // we cannot access RTTI of the semi-destructed shape, so just
0465     // unlink it lazily
0466     if (q->d->tree.contains(shape)) {
0467         q->d->tree.remove(shape);
0468     }
0469 
0470     q->d->shapes.removeAll(shape);
0471 }
0472 
0473 
0474 KoShapeManager::ShapeInterface *KoShapeManager::shapeInterface()
0475 {
0476     return &d->shapeInterface;
0477 }
0478 
0479 void KoShapeManager::preparePaintJobs(PaintJobsOrder &jobsOrder,
0480                                       KoShape *excludeRoot)
0481 {
0482     d->updateTree();
0483 
0484     QMutexLocker l1(&d->shapesMutex);
0485 
0486     QSet<KoShape*> rootShapesSet;
0487     Q_FOREACH (KoShape *shape, d->shapes) {
0488         while (shape->parent() && shape->parent() != excludeRoot) {
0489             shape = shape->parent();
0490         }
0491 
0492         if (!rootShapesSet.contains(shape) && shape != excludeRoot) {
0493             rootShapesSet.insert(shape);
0494         }
0495     }
0496 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
0497     const QList<KoShape*> rootShapes(rootShapesSet.begin(), rootShapesSet.end());
0498 #else
0499     const QList<KoShape*> rootShapes = QList<KoShape*>::fromSet(rootShapesSet);
0500 #endif
0501     QList<KoShape*> newRootShapes;
0502 
0503     Q_FOREACH (KoShape *srcShape, rootShapes) {
0504         KIS_SAFE_ASSERT_RECOVER(srcShape->parent() == excludeRoot
0505                                 || !srcShape->parent()) {
0506             continue;
0507         }
0508 
0509         KoShape *clonedShape = srcShape->cloneShape();
0510 
0511         KoShapeContainer *parentShape = srcShape->parent();
0512 
0513         if (parentShape && !parentShape->transformation().isIdentity()) {
0514             clonedShape->applyAbsoluteTransformation(parentShape->transformation());
0515         }
0516 
0517         newRootShapes << clonedShape;
0518     }
0519 
0520     PaintJobsOrder result;
0521 
0522     PaintJob::SharedSafeStorage shapesStorage = std::make_shared<PaintJob::ShapesStorage>();
0523     Q_FOREACH (KoShape *shape, newRootShapes) {
0524         shapesStorage->emplace_back(std::unique_ptr<KoShape>(shape));
0525     }
0526 
0527     const QList<KoShape*> originalShapes = KoShape::linearizeSubtreeSorted(rootShapes);
0528     const QList<KoShape*> clonedShapes = KoShape::linearizeSubtreeSorted(newRootShapes);
0529     KIS_SAFE_ASSERT_RECOVER_RETURN(clonedShapes.size() == originalShapes.size());
0530 
0531     QHash<KoShape*, KoShape*> clonedFromOriginal;
0532     for (int i = 0; i < originalShapes.size(); i++) {
0533         clonedFromOriginal[originalShapes[i]] = clonedShapes[i];
0534     }
0535 
0536 
0537     for (auto it = std::begin(jobsOrder.jobs); it != std::end(jobsOrder.jobs); ++it) {
0538         QMutexLocker l(&d->treeMutex);
0539         QList<KoShape*> unsortedOriginalShapes = d->tree.intersects(it->docUpdateRect);
0540 
0541         it->allClonedShapes = shapesStorage;
0542 
0543         Q_FOREACH (KoShape *shape, unsortedOriginalShapes) {
0544             KIS_SAFE_ASSERT_RECOVER(shapeUsedInRenderingTree(shape)) { continue; }
0545             it->shapes << clonedFromOriginal[shape];
0546         }
0547     }
0548 }
0549 
0550 void KoShapeManager::paintJob(QPainter &painter, const KoShapeManager::PaintJob &job)
0551 {
0552     painter.setPen(Qt::NoPen);  // painters by default have a black stroke, lets turn that off.
0553     painter.setBrush(Qt::NoBrush);
0554 
0555     KisForest<KoShape*> renderTree;
0556     buildRenderTree(job.shapes, renderTree);
0557 
0558     renderShapes(childBegin(renderTree), childEnd(renderTree), painter);
0559 }
0560 
0561 void KoShapeManager::paint(QPainter &painter)
0562 {
0563     d->updateTree();
0564 
0565     QMutexLocker l1(&d->shapesMutex);
0566 
0567     painter.setPen(Qt::NoPen);  // painters by default have a black stroke, lets turn that off.
0568     painter.setBrush(Qt::NoBrush);
0569 
0570     QList<KoShape*> unsortedShapes;
0571     if (painter.hasClipping()) {
0572         QMutexLocker l(&d->treeMutex);
0573 
0574         QRectF rect = KisPaintingTweaks::safeClipBoundingRect(painter);
0575         unsortedShapes = d->tree.intersects(rect);
0576     } else {
0577         unsortedShapes = d->shapes;
0578         warnFlake << "KoShapeManager::paint  Painting with a painter that has no clipping will lead to too much being painted!";
0579     }
0580 
0581     KisForest<KoShape*> renderTree;
0582     buildRenderTree(unsortedShapes, renderTree);
0583     renderShapes(childBegin(renderTree), childEnd(renderTree), painter);
0584 }
0585 
0586 void KoShapeManager::renderSingleShape(KoShape *shape, QPainter &painter)
0587 {
0588     KisForest<KoShape*> renderTree;
0589 
0590     KoViewConverter converter;
0591 
0592     auto root = renderTree.insert(childBegin(renderTree), shape);
0593     populateRenderSubtree(shape, root, renderTree, &shapeIsVisible, &shapeIsVisible);
0594     renderShapes(childBegin(renderTree), childEnd(renderTree), painter);
0595 }
0596 
0597 KoShape *KoShapeManager::shapeAt(const QPointF &position, KoFlake::ShapeSelection selection, bool omitHiddenShapes)
0598 {
0599     d->updateTree();
0600 
0601     QMutexLocker l(&d->shapesMutex);
0602 
0603     QList<KoShape*> sortedShapes;
0604 
0605     {
0606         QMutexLocker l(&d->treeMutex);
0607         sortedShapes = d->tree.contains(position);
0608     }
0609 
0610     std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex);
0611     KoShape *firstUnselectedShape = 0;
0612     for (int count = sortedShapes.count() - 1; count >= 0; count--) {
0613         KoShape *shape = sortedShapes.at(count);
0614         if (omitHiddenShapes && ! shape->isVisible())
0615             continue;
0616         if (! shape->hitTest(position))
0617             continue;
0618 
0619         switch (selection) {
0620         case KoFlake::ShapeOnTop:
0621             if (shape->isSelectable())
0622                 return shape;
0623             break;
0624         case KoFlake::Selected:
0625             if (d->selection->isSelected(shape))
0626                 return shape;
0627             break;
0628         case KoFlake::Unselected:
0629             if (! d->selection->isSelected(shape))
0630                 return shape;
0631             break;
0632         case KoFlake::NextUnselected:
0633             // we want an unselected shape
0634             if (d->selection->isSelected(shape))
0635                 continue;
0636             // memorize the first unselected shape
0637             if (! firstUnselectedShape)
0638                 firstUnselectedShape = shape;
0639             // check if the shape above is selected
0640             if (count + 1 < sortedShapes.count() && d->selection->isSelected(sortedShapes.at(count + 1)))
0641                 return shape;
0642             break;
0643         }
0644     }
0645     // if we want the next unselected below a selected but there was none selected,
0646     // return the first found unselected shape
0647     if (selection == KoFlake::NextUnselected && firstUnselectedShape)
0648         return firstUnselectedShape;
0649 
0650     if (d->selection->hitTest(position))
0651         return d->selection;
0652 
0653     return 0; // missed everything
0654 }
0655 
0656 QList<KoShape *> KoShapeManager::shapesAt(const QRectF &rect, bool omitHiddenShapes, bool containedMode)
0657 {
0658     QMutexLocker l(&d->shapesMutex);
0659 
0660     d->updateTree();
0661     QList<KoShape*> shapes;
0662 
0663     {
0664         QMutexLocker l(&d->treeMutex);
0665         shapes = containedMode ? d->tree.contained(rect) : d->tree.intersects(rect);
0666     }
0667 
0668     for (int count = shapes.count() - 1; count >= 0; count--) {
0669 
0670         KoShape *shape = shapes.at(count);
0671 
0672         if (omitHiddenShapes && !shape->isVisible()) {
0673             shapes.removeAt(count);
0674         } else {
0675             const QPainterPath outline = shape->absoluteTransformation().map(shape->outline());
0676 
0677             if (!containedMode && !outline.intersects(rect) && !outline.contains(rect)) {
0678                 shapes.removeAt(count);
0679 
0680             } else if (containedMode) {
0681 
0682                 QPainterPath containingPath;
0683                 containingPath.addRect(rect);
0684 
0685                 if (!containingPath.contains(outline)) {
0686                     shapes.removeAt(count);
0687                 }
0688             }
0689         }
0690     }
0691 
0692     return shapes;
0693 }
0694 
0695 void KoShapeManager::update(const QRectF &rect, const KoShape *shape, bool selectionHandles)
0696 {
0697     if (d->updatesBlocked) return;
0698 
0699     {
0700         QMutexLocker l(&d->shapesMutex);
0701 
0702         d->compressedUpdate |= rect;
0703 
0704         if (selectionHandles) {
0705             d->compressedUpdatedShapes.insert(shape);
0706         }
0707     }
0708 
0709     emit(forwardUpdate());
0710 }
0711 
0712 void KoShapeManager::setUpdatesBlocked(bool value)
0713 {
0714     d->updatesBlocked = value;
0715 }
0716 
0717 bool KoShapeManager::updatesBlocked() const
0718 {
0719     return d->updatesBlocked;
0720 }
0721 void KoShapeManager::notifyShapeChanged(KoShape *shape)
0722 {
0723     {
0724         QMutexLocker l(&d->treeMutex);
0725 
0726         Q_ASSERT(shape);
0727         if (d->aggregate4update.contains(shape)) {
0728             return;
0729         }
0730 
0731         d->aggregate4update.insert(shape);
0732         d->shapeIndexesBeforeUpdate.insert(shape, shape->zIndex());
0733     }
0734 
0735     KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
0736     if (container) {
0737         Q_FOREACH (KoShape *child, container->shapes())
0738             notifyShapeChanged(child);
0739     }
0740 }
0741 
0742 QList<KoShape*> KoShapeManager::shapes() const
0743 {
0744     QMutexLocker l(&d->shapesMutex);
0745 
0746     return d->shapes;
0747 }
0748 
0749 QList<KoShape*> KoShapeManager::topLevelShapes() const
0750 {
0751     QMutexLocker l(&d->shapesMutex);
0752 
0753     QList<KoShape*> shapes;
0754     // get all toplevel shapes
0755     Q_FOREACH (KoShape *shape, d->shapes) {
0756         if (!shape->parent() || dynamic_cast<KoShapeLayer*>(shape->parent())) {
0757             shapes.append(shape);
0758         }
0759     }
0760     return shapes;
0761 }
0762 
0763 KoSelection *KoShapeManager::selection() const
0764 {
0765     return d->selection;
0766 }
0767 
0768 void KoShapeManager::explicitlyIssueShapeChangedSignals()
0769 {
0770     d->updateTree();
0771 }
0772 
0773 KoCanvasBase *KoShapeManager::canvas()
0774 {
0775     return d->canvas;
0776 }
0777 
0778 //have to include this because of Q_PRIVATE_SLOT
0779 #include "moc_KoShapeManager.cpp"