30#include <QApplication>
33#include <QPainterPath>
35#include <QMutexLocker>
41#include <unordered_set>
50inline bool shapeUsedInRenderingTree(
KoShape *shape)
62inline bool shapeHasGroupEffects(
KoShape *shape) {
70inline bool shapeIsVisible(
KoShape *shape) {
81void populateRenderSubtree(
KoShape *parentShape,
84 std::function<
bool(
KoShape*)> shouldIncludeNode,
85 std::function<
bool(
KoShape*)> shouldEnterSubtree)
88 if (!parentContainer)
return;
93 for (
auto it = children.constBegin(); it != children.constEnd(); ++it) {
94 auto newParentIt = parentIt;
96 if (shouldIncludeNode(*it)) {
100 if (shouldEnterSubtree(*it)) {
101 populateRenderSubtree(*it, newParentIt, tree, shouldIncludeNode, shouldEnterSubtree);
126 std::unordered_set<KoShape*> includedShapes;
128 Q_FOREACH (
KoShape *shape, sortedShapes) {
129 bool shouldSkipShape = !shapeIsVisible(shape);
130 if (shouldSkipShape)
continue;
132 bool shapeIsPartOfIncludedSubtree =
false;
135 while ((shape = shape->
parent())) {
136 if (!shapeIsVisible(shape)) {
137 shouldSkipShape =
true;
141 if (includedShapes.find(shape) != end(includedShapes)) {
142 shapeIsPartOfIncludedSubtree =
true;
146 if (shapeHasGroupEffects(shape)) {
151 if (shouldSkipShape)
continue;
153 if (!shapeIsPartOfIncludedSubtree &&
154 includedShapes.find(hierarchy.last()) == end(includedShapes)) {
158 std::copy(hierarchy.begin(), hierarchy.end(),
159 std::inserter(includedShapes, end(includedShapes)));
162 auto shouldIncludeShape =
163 [includedShapes] (
KoShape *shape) {
165 return includedShapes.find(shape) != end(includedShapes);
169 populateRenderSubtree(*it, it, tree, shouldIncludeShape, &shapeIsVisible);
180 for (
auto it = beginIt; it != endIt; ++it) {
186 painter.setTransform(shape->
transformation() * painter.transform());
194 if (transparency > 0.0) {
195 painter.setOpacity(1.0-transparency);
198 QScopedPointer<KoClipMaskPainter> clipMaskPainter;
199 QPainter *shapePainter = &painter;
207 const QRectF
bounds = painter.transform().mapRect(shape->
outlineRect() & painter.clipBoundingRect());
210 shapePainter = clipMaskPainter->shapePainter();
218 const QTransform sanityCheckTransformSaved = shapePainter->transform();
224 shape->
paint(*shapePainter);
233 shapePainter->setTransform(sanityCheckTransformSaved);
237 clipMaskPainter->maskPainter()->save();
240 clipMaskPainter->renderOnGlobalPainter();
242 clipMaskPainter->maskPainter()->restore();
249void KoShapeManager::Private::updateTree()
251 bool selectionModified =
false;
252 bool anyModified =
false;
255 QMutexLocker l(&this->treeMutex);
257 Q_FOREACH (
KoShape *shape, aggregate4update) {
258 selectionModified = selectionModified || selection->isSelected(shape);
262 foreach (
KoShape *shape, aggregate4update) {
263 if (!shapeUsedInRenderingTree(shape))
continue;
270 aggregate4update.clear();
273 if (selectionModified) {
274 Q_EMIT q->selectionContentChanged();
277 Q_EMIT q->contentChanged();
281void KoShapeManager::Private::forwardCompressedUpdate()
283 bool shouldUpdateDecorations =
false;
284 QRectF scheduledUpdate;
287 QMutexLocker l(&shapesMutex);
289 if (!compressedUpdate.isEmpty()) {
290 scheduledUpdate = compressedUpdate;
291 compressedUpdate = QRect();
294 Q_FOREACH (
const KoShape *shape, compressedUpdatedShapes) {
295 if (selection->isSelected(shape)) {
296 shouldUpdateDecorations =
true;
300 compressedUpdatedShapes.clear();
303 if (shouldUpdateDecorations && canvas->toolProxy()) {
304 canvas->toolProxy()->repaintDecorations();
306 canvas->updateCanvas(scheduledUpdate);
321 this->moveToThread(qApp->thread());
332 this->moveToThread(qApp->thread());
336void KoShapeManager::Private::unlinkFromShapesRecursively(
const QList<KoShape*> &shapes)
338 Q_FOREACH (
KoShape *shape, shapes) {
343 unlinkFromShapesRecursively(container->
shapes());
350 d->unlinkFromShapesRecursively(
d->shapes);
359 QMutexLocker l1(&
d->shapesMutex);
360 QMutexLocker l2(&
d->treeMutex);
363 d->selection->deselectAll();
364 d->unlinkFromShapesRecursively(
d->shapes);
365 d->compressedUpdate = QRect();
366 d->compressedUpdatedShapes.clear();
367 d->aggregate4update.clear();
380 QMutexLocker l1(&
d->shapesMutex);
382 if (
d->shapes.contains(shape))
385 d->shapes.append(shape);
387 if (shapeUsedInRenderingTree(shape)) {
388 QMutexLocker l2(&
d->treeMutex);
391 d->tree.insert(br, shape);
413 QMutexLocker l1(&
d->shapesMutex);
414 QMutexLocker l2(&
d->treeMutex);
419 d->selection->deselect(shape);
420 d->aggregate4update.remove(shape);
421 d->compressedUpdatedShapes.remove(shape);
423 if (shapeUsedInRenderingTree(shape)) {
424 d->tree.remove(shape);
426 d->shapes.removeAll(shape);
429 if (!dirtyRect.isEmpty()) {
449 QMutexLocker l1(&
q->
d->shapesMutex);
450 QMutexLocker l2(&
q->
d->treeMutex);
452 q->
d->selection->deselect(shape);
453 q->
d->aggregate4update.remove(shape);
454 q->
d->compressedUpdatedShapes.remove(shape);
458 if (
q->
d->tree.contains(shape)) {
459 q->
d->tree.remove(shape);
462 q->
d->shapes.removeAll(shape);
468 return &
d->shapeInterface;
476 QMutexLocker l1(&
d->shapesMutex);
478 QSet<KoShape*> rootShapesSet;
479 Q_FOREACH (
KoShape *shape,
d->shapes) {
480 while (shape->
parent() && shape->
parent() != excludeRoot) {
484 if (!rootShapesSet.contains(shape) && shape != excludeRoot) {
485 rootShapesSet.insert(shape);
488 const QList<KoShape*> rootShapes(rootShapesSet.begin(), rootShapesSet.end());
491 Q_FOREACH (
KoShape *srcShape, rootShapes) {
505 newRootShapes << clonedShape;
511 Q_FOREACH (
KoShape *shape, newRootShapes) {
512 shapesStorage->emplace_back(std::unique_ptr<KoShape>(shape));
519 QHash<KoShape*, KoShape*> clonedFromOriginal;
520 for (
int i = 0; i < originalShapes.size(); i++) {
521 clonedFromOriginal[originalShapes[i]] = clonedShapes[i];
525 for (
auto it = std::begin(jobsOrder.
jobs); it != std::end(jobsOrder.
jobs); ++it) {
526 QMutexLocker l(&
d->treeMutex);
527 QList<KoShape*> unsortedOriginalShapes =
d->tree.intersects(it->docUpdateRect);
529 it->allClonedShapes = shapesStorage;
531 Q_FOREACH (
KoShape *shape, unsortedOriginalShapes) {
533 it->shapes << clonedFromOriginal[shape];
540 painter.setPen(Qt::NoPen);
541 painter.setBrush(Qt::NoBrush);
544 buildRenderTree(job.
shapes, renderTree);
546 renderShapes(childBegin(renderTree), childEnd(renderTree), painter);
553 QMutexLocker l1(&
d->shapesMutex);
555 painter.setPen(Qt::NoPen);
556 painter.setBrush(Qt::NoBrush);
559 if (painter.hasClipping()) {
560 QMutexLocker l(&
d->treeMutex);
563 unsortedShapes =
d->tree.intersects(
rect);
565 unsortedShapes =
d->shapes;
566 warnFlake <<
"KoShapeManager::paint Painting with a painter that has no clipping will lead to too much being painted!";
570 buildRenderTree(unsortedShapes, renderTree);
571 renderShapes(childBegin(renderTree), childEnd(renderTree), painter);
580 auto root = renderTree.
insert(childBegin(renderTree), shape);
581 populateRenderSubtree(shape, root, renderTree, &shapeIsVisible, &shapeIsVisible);
582 renderShapes(childBegin(renderTree), childEnd(renderTree), painter);
589 QMutexLocker l(&
d->shapesMutex);
594 QMutexLocker l(&
d->treeMutex);
595 sortedShapes =
d->tree.contains(position);
599 KoShape *firstUnselectedShape = 0;
600 for (
int count = sortedShapes.count() - 1; count >= 0; count--) {
601 KoShape *shape = sortedShapes.at(count);
602 if (omitHiddenShapes && ! shape->
isVisible())
604 if (! shape->
hitTest(position))
613 if (
d->selection->isSelected(shape))
617 if (!
d->selection->isSelected(shape))
622 if (
d->selection->isSelected(shape))
625 if (! firstUnselectedShape)
626 firstUnselectedShape = shape;
628 if (count + 1 < sortedShapes.count() &&
d->selection->isSelected(sortedShapes.at(count + 1)))
636 return firstUnselectedShape;
638 if (
d->selection->hitTest(position))
650 QMutexLocker l(&
d->treeMutex);
651 shapes = containedMode ?
d->tree.contained(
rect) :
d->tree.intersects(
rect);
654 for (
int count =
shapes.count() - 1; count >= 0; count--) {
658 if (omitHiddenShapes && !shape->
isVisible()) {
663 if (!containedMode && !outline.intersects(
rect) && !outline.contains(
rect)) {
666 }
else if (containedMode) {
668 QPainterPath containingPath;
669 containingPath.addRect(
rect);
671 if (!containingPath.contains(outline)) {
683 if (
d->updatesBlocked)
return;
686 QMutexLocker l(&
d->shapesMutex);
688 d->compressedUpdate |=
rect;
690 if (selectionHandles) {
691 d->compressedUpdatedShapes.insert(shape);
700 d->updatesBlocked =
value;
705 return d->updatesBlocked;
710 QMutexLocker l(&
d->treeMutex);
713 if (
d->aggregate4update.contains(shape)) {
717 d->aggregate4update.insert(shape);
729 QMutexLocker l(&
d->shapesMutex);
736 QMutexLocker l(&
d->shapesMutex);
740 Q_FOREACH (
KoShape *shape,
d->shapes) {
764#include "moc_KoShapeManager.cpp"
float value(const T *src, size_t ch)
void updateCanvas(const QRectF &rc) override
child_iterator insert(child_iterator pos, X &&value)
Inserts element value into position pos. value becomes the child of the same parent as pos and is pla...
static void applyClipping(KoShape *clippedShape, QPainter &painter)
Applies the clipping to the given painter.
QList< KoShape * > shapes() const
@ PaintShapeOnAdd
Causes each shapes 'update()' to be called after being added to the shapeManager.
QList< KoShape * > topLevelShapes() const
KoShapeManager(KoCanvasBase *canvas)
~KoShapeManager() override
QList< KoShape * > shapes
QList< KoShape * > shapesAt(const QRectF &rect, bool omitHiddenShapes=true, bool containedMode=false)
KoShape * shapeAt(const QPointF &position, KoFlake::ShapeSelection selection=KoFlake::ShapeOnTop, bool omitHiddenShapes=true)
KoShapeManager::ShapeInterface shapeInterface
void forwardCompressedUpdate()
void paintJob(QPainter &painter, const KoShapeManager::PaintJob &job)
void setShapes(const QList< KoShape * > &shapes, Repaint repaint=PaintShapeOnAdd)
void selectionChanged()
emitted when the selection is changed
void update(const QRectF &rect, const KoShape *shape=0, bool selectionHandles=false)
void setUpdatesBlocked(bool value)
void paint(QPainter &painter)
void explicitlyIssueShapeChangedSignals()
void notifyShapeChanged(KoShape *shape)
static void renderSingleShape(KoShape *shape, QPainter &painter)
renderSingleShape renders a shape on painter. This method includes all the needed steps for painting ...
void forwardUpdate()
Emitted upon update and used to update the canvas.
void remove(KoShape *shape)
void addShape(KoShape *shape, KoShapeManager::Repaint repaint=PaintShapeOnAdd)
void preparePaintJobs(PaintJobsOrder &jobsOrder, KoShape *excludeRoot)
virtual void paintStroke(QPainter &painter) const
paintStroke paints the shape's stroked outline
virtual QRectF outlineRect() const
virtual QPainterPath outline() const
virtual QVector< PaintOrder > paintOrder() const
paintOrder
bool isSelectable() const
void addShapeManager(KoShapeManager *manager)
void applyAbsoluteTransformation(const QTransform &matrix)
static bool compareShapeZIndex(KoShape *s1, KoShape *s2)
virtual QRectF boundingRect() const
Get the bounding box of the shape.
KoClipPath * clipPath() const
Returns the currently set clip path or 0 if there is no clip path set.
virtual void update() const
KoShapeContainer * parent() const
void removeShapeManager(KoShapeManager *manager)
static QList< KoShape * > linearizeSubtreeSorted(const QList< KoShape * > &shapes)
QTransform absoluteTransformation() const
virtual void paintMarkers(QPainter &painter) const
paintStroke paints the shape's markers
KoClipMask * clipMask() const
Returns the currently set clip mask or 0 if there is no clip mask set.
virtual void paint(QPainter &painter) const =0
Paint the shape fill The class extending this one is responsible for painting itself....
virtual KoShape * cloneShape() const
creates a deep copy of the shape or shape's subtree
QTransform transformation() const
Returns the shapes local transformation matrix.
virtual bool hitTest(const QPointF &position) const
Check if the shape is hit on position.
bool isVisible(bool recursive=true) const
qreal transparency(bool recursive=false) const
#define KIS_SAFE_ASSERT_RECOVER(cond)
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
bool isEnd(const ChildIterator< T, is_const > &it)
ChildIterator< value_type, is_const > childBegin(const ChildIterator< value_type, is_const > &it)
ChildIterator< value_type, is_const > parent(const ChildIterator< value_type, is_const > &it)
ChildIterator< value_type, is_const > childEnd(const ChildIterator< value_type, is_const > &it)
QRect safeClipBoundingRect(const QPainter &painter)
@ Unselected
return the first unselected on top.
@ Selected
return the first selected with the highest z-ordering (i.e. on top).
@ ShapeOnTop
return the shape highest z-ordering, regardless of selection.
@ NextUnselected
return the first unselected directly under a selected shape, or the top most one if nothing is select...
void drawMask(QPainter *painter, KoShape *shape)
std::shared_ptr< ShapesStorage > SharedSafeStorage
QList< KoShape * > shapes
ShapeInterface(KoShapeManager *_q)
void notifyShapeDestructed(KoShape *shape)