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