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"