Krita Source Code Documentation
Loading...
Searching...
No Matches
KoShapeManager.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2
3 SPDX-FileCopyrightText: 2006-2008 Thorsten Zachmann <zachmann@kde.org>
4 SPDX-FileCopyrightText: 2006-2010 Thomas Zander <zander@kde.org>
5 SPDX-FileCopyrightText: 2009-2010 Jan Hambrecht <jaham@gmx.net>
6
7 SPDX-License-Identifier: LGPL-2.0-or-later
8*/
9
10#include "KoShapeManager.h"
11#include "KoShapeManager_p.h"
12#include "KoSelection.h"
13#include "KoToolManager.h"
14#include "KoPointerEvent.h"
15#include "KoShape.h"
16#include "KoShape_p.h"
17#include "KoCanvasBase.h"
18#include "KoShapeContainer.h"
19#include "KoShapeStrokeModel.h"
20#include "KoShapeGroup.h"
21#include "KoToolProxy.h"
22#include "KoShapeLayer.h"
23#include "KoShapeBackground.h"
24#include <KoRTree.h>
25#include "KoClipPath.h"
26#include "KoClipMaskPainter.h"
27#include "KoViewConverter.h"
29#include "KoSvgTextShape.h"
30#include <QApplication>
31
32#include <QPainter>
33#include <QPainterPath>
34#include <QThread>
35#include <QMutexLocker>
36#include <FlakeDebug.h>
37
38#include "kis_painting_tweaks.h"
39#include "kis_debug.h"
40#include "KisForest.h"
41#include <unordered_set>
42
43
44namespace {
45
50inline bool shapeUsedInRenderingTree(KoShape *shape)
51{
52 // FIXME: make more general!
53
54 return !dynamic_cast<KoShapeGroup*>(shape) &&
55 !dynamic_cast<KoShapeLayer*>(shape);
56}
57
62inline bool shapeHasGroupEffects(KoShape *shape) {
63 return shape->clipPath() ||
64 shape->clipMask();
65}
66
70inline bool shapeIsVisible(KoShape *shape) {
71 return shape->isVisible(false) && shape->transparency() < 1.0;
72}
73
74
81void populateRenderSubtree(KoShape *parentShape,
84 std::function<bool(KoShape*)> shouldIncludeNode,
85 std::function<bool(KoShape*)> shouldEnterSubtree)
86{
87 KoShapeContainer *parentContainer = dynamic_cast<KoShapeContainer*>(parentShape);
88 if (!parentContainer) return;
89
90 QList<KoShape*> children = parentContainer->shapes();
91 std::sort(children.begin(), children.end(), KoShape::compareShapeZIndex);
92
93 for (auto it = children.constBegin(); it != children.constEnd(); ++it) {
94 auto newParentIt = parentIt;
95
96 if (shouldIncludeNode(*it)) {
97 newParentIt = tree.insert(childEnd(parentIt), *it);
98 }
99
100 if (shouldEnterSubtree(*it)) {
101 populateRenderSubtree(*it, newParentIt, tree, shouldIncludeNode, shouldEnterSubtree);
102 }
103 }
104
105}
106
120void buildRenderTree(QList<KoShape*> leafShapes,
122{
123 QList<KoShape*> sortedShapes = leafShapes;
124 std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex);
125
126 std::unordered_set<KoShape*> includedShapes;
127
128 Q_FOREACH (KoShape *shape, sortedShapes) {
129 bool shouldSkipShape = !shapeIsVisible(shape);
130 if (shouldSkipShape) continue;
131
132 bool shapeIsPartOfIncludedSubtree = false;
133 QVector<KoShape*> hierarchy = {shape};
134
135 while ((shape = shape->parent())) {
136 if (!shapeIsVisible(shape)) {
137 shouldSkipShape = true;
138 break;
139 }
140
141 if (includedShapes.find(shape) != end(includedShapes)) {
142 shapeIsPartOfIncludedSubtree = true;
143 break;
144 }
145
146 if (shapeHasGroupEffects(shape)) {
147 hierarchy << shape;
148 }
149 }
150
151 if (shouldSkipShape) continue;
152
153 if (!shapeIsPartOfIncludedSubtree &&
154 includedShapes.find(hierarchy.last()) == end(includedShapes)) {
155
156 tree.insert(childEnd(tree), hierarchy.last());
157 }
158 std::copy(hierarchy.begin(), hierarchy.end(),
159 std::inserter(includedShapes, end(includedShapes)));
160 }
161
162 auto shouldIncludeShape =
163 [includedShapes] (KoShape *shape) {
164 // included shapes are guaranteed to be visible
165 return includedShapes.find(shape) != end(includedShapes);
166 };
167
168 for (auto it = childBegin(tree); it != childEnd(tree); ++it) {
169 populateRenderSubtree(*it, it, tree, shouldIncludeShape, &shapeIsVisible);
170 }
171}
172
176void renderShapes(typename KisForest<KoShape*>::child_iterator beginIt,
178 QPainter &painter)
179{
180 for (auto it = beginIt; it != endIt; ++it) {
181 KoShape *shape = *it;
182
183 KisQPainterStateSaver saver(&painter);
184
185 if (!isEnd(parent(it))) {
186 painter.setTransform(shape->transformation() * painter.transform());
187 } else {
188 painter.setTransform(shape->absoluteTransformation() * painter.transform());
189 }
190
191 KoClipPath::applyClipping(shape, painter);
192
193 qreal transparency = shape->transparency(true);
194 if (transparency > 0.0) {
195 painter.setOpacity(1.0-transparency);
196 }
197
198 QScopedPointer<KoClipMaskPainter> clipMaskPainter;
199 QPainter *shapePainter = &painter;
200
201 KoClipMask *clipMask = shape->clipMask();
202 if (clipMask) {
207 const QRectF bounds = painter.transform().mapRect(shape->outlineRect() & painter.clipBoundingRect());
208
209 clipMaskPainter.reset(new KoClipMaskPainter(&painter, bounds));
210 shapePainter = clipMaskPainter->shapePainter();
211 }
212
218 const QTransform sanityCheckTransformSaved = shapePainter->transform();
219
220 renderShapes(childBegin(it), childEnd(it), *shapePainter);
221
222 Q_FOREACH(const KoShape::PaintOrder p, shape->paintOrder()) {
223 if (p == KoShape::Fill) {
224 shape->paint(*shapePainter);
225 } else if (p == KoShape::Stroke) {
226 shape->paintStroke(*shapePainter);
227 } else if (p == KoShape::Markers) {
228 shape->paintMarkers(*shapePainter);
229 }
230 }
231
232 KIS_SAFE_ASSERT_RECOVER(shapePainter->transform() == sanityCheckTransformSaved) {
233 shapePainter->setTransform(sanityCheckTransformSaved);
234 }
235
236 if (clipMask) {
237 clipMaskPainter->maskPainter()->save();
238
239 shape->clipMask()->drawMask(clipMaskPainter->maskPainter(), shape);
240 clipMaskPainter->renderOnGlobalPainter();
241
242 clipMaskPainter->maskPainter()->restore();
243 }
244 }
245}
246
247}
248
249void KoShapeManager::Private::updateTree()
250{
251 bool selectionModified = false;
252 bool anyModified = false;
253
254 {
255 QMutexLocker l(&this->treeMutex);
256
257 Q_FOREACH (KoShape *shape, aggregate4update) {
258 selectionModified = selectionModified || selection->isSelected(shape);
259 anyModified = true;
260 }
261
262 foreach (KoShape *shape, aggregate4update) {
263 if (!shapeUsedInRenderingTree(shape)) continue;
264
265 tree.remove(shape);
266 QRectF br(shape->boundingRect());
267 tree.insert(br, shape);
268 }
269
270 aggregate4update.clear();
271 }
272
273 if (selectionModified) {
274 Q_EMIT q->selectionContentChanged();
275 }
276 if (anyModified) {
277 Q_EMIT q->contentChanged();
278 }
279}
280
281void KoShapeManager::Private::forwardCompressedUpdate()
282{
283 bool shouldUpdateDecorations = false;
284 QRectF scheduledUpdate;
285
286 {
287 QMutexLocker l(&shapesMutex);
288
289 if (!compressedUpdate.isEmpty()) {
290 scheduledUpdate = compressedUpdate;
291 compressedUpdate = QRect();
292 }
293
294 Q_FOREACH (const KoShape *shape, compressedUpdatedShapes) {
295 if (selection->isSelected(shape)) {
296 shouldUpdateDecorations = true;
297 break;
298 }
299 }
300 compressedUpdatedShapes.clear();
301 }
302
303 if (shouldUpdateDecorations && canvas->toolProxy()) {
304 canvas->toolProxy()->repaintDecorations();
305 }
306 canvas->updateCanvas(scheduledUpdate);
307
308}
309
311 : d(new Private(this, canvas))
312{
313 Q_ASSERT(d->canvas); // not optional.
314 connect(d->selection, SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged()));
316
321 this->moveToThread(qApp->thread());
322 connect(this, SIGNAL(forwardUpdate()), this, SLOT(forwardCompressedUpdate()));
323}
324
326 : d(new Private(this, canvas))
327{
328 Q_ASSERT(d->canvas); // not optional.
329 connect(d->selection, SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged()));
330
331 // see a comment in another constructor
332 this->moveToThread(qApp->thread());
333 connect(this, SIGNAL(forwardUpdate()), this, SLOT(forwardCompressedUpdate()));
334}
335
336void KoShapeManager::Private::unlinkFromShapesRecursively(const QList<KoShape*> &shapes)
337{
338 Q_FOREACH (KoShape *shape, shapes) {
339 shape->removeShapeManager(q);
340
341 KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
342 if (container) {
343 unlinkFromShapesRecursively(container->shapes());
344 }
345 }
346}
347
349{
350 d->unlinkFromShapesRecursively(d->shapes);
351 d->shapes.clear();
352
353 delete d;
354}
355
357{
358 {
359 QMutexLocker l1(&d->shapesMutex);
360 QMutexLocker l2(&d->treeMutex);
361
362 //clear selection
363 d->selection->deselectAll();
364 d->unlinkFromShapesRecursively(d->shapes);
365 d->compressedUpdate = QRect();
366 d->compressedUpdatedShapes.clear();
367 d->aggregate4update.clear();
368 d->tree.clear();
369 d->shapes.clear();
370 }
371
372 Q_FOREACH (KoShape *shape, shapes) {
373 addShape(shape, repaint);
374 }
375}
376
378{
379 {
380 QMutexLocker l1(&d->shapesMutex);
381
382 if (d->shapes.contains(shape))
383 return;
384 shape->addShapeManager(this);
385 d->shapes.append(shape);
386
387 if (shapeUsedInRenderingTree(shape)) {
388 QMutexLocker l2(&d->treeMutex);
389
390 QRectF br(shape->boundingRect());
391 d->tree.insert(br, shape);
392 }
393 }
394
395 if (repaint == PaintShapeOnAdd) {
396 shape->update();
397 }
398
399 // add the children of a KoShapeContainer
400 KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
401
402 if (container) {
403 foreach (KoShape *containerShape, container->shapes()) {
404 addShape(containerShape, repaint);
405 }
406 }
407}
408
410{
411 QRectF dirtyRect;
412 {
413 QMutexLocker l1(&d->shapesMutex);
414 QMutexLocker l2(&d->treeMutex);
415
416 dirtyRect = shape->boundingRect();
417
418 shape->removeShapeManager(this);
419 d->selection->deselect(shape);
420 d->aggregate4update.remove(shape);
421 d->compressedUpdatedShapes.remove(shape);
422
423 if (shapeUsedInRenderingTree(shape)) {
424 d->tree.remove(shape);
425 }
426 d->shapes.removeAll(shape);
427 }
428
429 if (!dirtyRect.isEmpty()) {
430 d->canvas->updateCanvas(dirtyRect);
431 }
432
433 // remove the children of a KoShapeContainer
434 KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
435 if (container) {
436 foreach (KoShape *containerShape, container->shapes()) {
437 remove(containerShape);
438 }
439 }
440}
441
446
448{
449 QMutexLocker l1(&q->d->shapesMutex);
450 QMutexLocker l2(&q->d->treeMutex);
451
452 q->d->selection->deselect(shape);
453 q->d->aggregate4update.remove(shape);
454 q->d->compressedUpdatedShapes.remove(shape);
455
456 // we cannot access RTTI of the semi-destructed shape, so just
457 // unlink it lazily
458 if (q->d->tree.contains(shape)) {
459 q->d->tree.remove(shape);
460 }
461
462 q->d->shapes.removeAll(shape);
463}
464
465
467{
468 return &d->shapeInterface;
469}
470
472 KoShape *excludeRoot)
473{
474 d->updateTree();
475
476 QMutexLocker l1(&d->shapesMutex);
477
478 QSet<KoShape*> rootShapesSet;
479 Q_FOREACH (KoShape *shape, d->shapes) {
480 while (shape->parent() && shape->parent() != excludeRoot) {
481 shape = shape->parent();
482 }
483
484 if (!rootShapesSet.contains(shape) && shape != excludeRoot) {
485 rootShapesSet.insert(shape);
486 }
487 }
488 const QList<KoShape*> rootShapes(rootShapesSet.begin(), rootShapesSet.end());
489 QList<KoShape*> newRootShapes;
490
491 Q_FOREACH (KoShape *srcShape, rootShapes) {
492 KIS_SAFE_ASSERT_RECOVER(srcShape->parent() == excludeRoot
493 || !srcShape->parent()) {
494 continue;
495 }
496
497 KoShape *clonedShape = srcShape->cloneShape();
498
499 KoShapeContainer *parentShape = srcShape->parent();
500
501 if (parentShape && !parentShape->transformation().isIdentity()) {
502 clonedShape->applyAbsoluteTransformation(parentShape->transformation());
503 }
504
505 newRootShapes << clonedShape;
506 }
507
508 PaintJobsOrder result;
509
510 PaintJob::SharedSafeStorage shapesStorage = std::make_shared<PaintJob::ShapesStorage>();
511 Q_FOREACH (KoShape *shape, newRootShapes) {
512 shapesStorage->emplace_back(std::unique_ptr<KoShape>(shape));
513 }
514
515 const QList<KoShape*> originalShapes = KoShape::linearizeSubtreeSorted(rootShapes);
516 const QList<KoShape*> clonedShapes = KoShape::linearizeSubtreeSorted(newRootShapes);
517 KIS_SAFE_ASSERT_RECOVER_RETURN(clonedShapes.size() == originalShapes.size());
518
519 QHash<KoShape*, KoShape*> clonedFromOriginal;
520 for (int i = 0; i < originalShapes.size(); i++) {
521 clonedFromOriginal[originalShapes[i]] = clonedShapes[i];
522 }
523
524
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);
528
529 it->allClonedShapes = shapesStorage;
530
531 Q_FOREACH (KoShape *shape, unsortedOriginalShapes) {
532 KIS_SAFE_ASSERT_RECOVER(shapeUsedInRenderingTree(shape)) { continue; }
533 it->shapes << clonedFromOriginal[shape];
534 }
535 }
536}
537
538void KoShapeManager::paintJob(QPainter &painter, const KoShapeManager::PaintJob &job)
539{
540 painter.setPen(Qt::NoPen); // painters by default have a black stroke, lets turn that off.
541 painter.setBrush(Qt::NoBrush);
542
543 KisForest<KoShape*> renderTree;
544 buildRenderTree(job.shapes, renderTree);
545
546 renderShapes(childBegin(renderTree), childEnd(renderTree), painter);
547}
548
549void KoShapeManager::paint(QPainter &painter)
550{
551 d->updateTree();
552
553 QMutexLocker l1(&d->shapesMutex);
554
555 painter.setPen(Qt::NoPen); // painters by default have a black stroke, lets turn that off.
556 painter.setBrush(Qt::NoBrush);
557
558 QList<KoShape*> unsortedShapes;
559 if (painter.hasClipping()) {
560 QMutexLocker l(&d->treeMutex);
561
563 unsortedShapes = d->tree.intersects(rect);
564 } else {
565 unsortedShapes = d->shapes;
566 warnFlake << "KoShapeManager::paint Painting with a painter that has no clipping will lead to too much being painted!";
567 }
568
569 KisForest<KoShape*> renderTree;
570 buildRenderTree(unsortedShapes, renderTree);
571 renderShapes(childBegin(renderTree), childEnd(renderTree), painter);
572}
573
574void KoShapeManager::renderSingleShape(KoShape *shape, QPainter &painter)
575{
576 KisForest<KoShape*> renderTree;
577
578 KoViewConverter converter;
579
580 auto root = renderTree.insert(childBegin(renderTree), shape);
581 populateRenderSubtree(shape, root, renderTree, &shapeIsVisible, &shapeIsVisible);
582 renderShapes(childBegin(renderTree), childEnd(renderTree), painter);
583}
584
585KoShape *KoShapeManager::shapeAt(const QPointF &position, KoFlake::ShapeSelection selection, bool omitHiddenShapes)
586{
587 d->updateTree();
588
589 QMutexLocker l(&d->shapesMutex);
590
591 QList<KoShape*> sortedShapes;
592
593 {
594 QMutexLocker l(&d->treeMutex);
595 sortedShapes = d->tree.contains(position);
596 }
597
598 std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex);
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())
603 continue;
604 if (! shape->hitTest(position))
605 continue;
606
607 switch (selection) {
609 if (shape->isSelectable())
610 return shape;
611 break;
613 if (d->selection->isSelected(shape))
614 return shape;
615 break;
617 if (! d->selection->isSelected(shape))
618 return shape;
619 break;
621 // we want an unselected shape
622 if (d->selection->isSelected(shape))
623 continue;
624 // memorize the first unselected shape
625 if (! firstUnselectedShape)
626 firstUnselectedShape = shape;
627 // check if the shape above is selected
628 if (count + 1 < sortedShapes.count() && d->selection->isSelected(sortedShapes.at(count + 1)))
629 return shape;
630 break;
631 }
632 }
633 // if we want the next unselected below a selected but there was none selected,
634 // return the first found unselected shape
635 if (selection == KoFlake::NextUnselected && firstUnselectedShape)
636 return firstUnselectedShape;
637
638 if (d->selection->hitTest(position))
639 return d->selection;
640
641 return 0; // missed everything
642}
643
644QList<KoShape *> KoShapeManager::shapesAt(const QRectF &rect, bool omitHiddenShapes, bool containedMode)
645{
646 d->updateTree();
648
649 {
650 QMutexLocker l(&d->treeMutex);
651 shapes = containedMode ? d->tree.contained(rect) : d->tree.intersects(rect);
652 }
653
654 for (int count = shapes.count() - 1; count >= 0; count--) {
655
656 KoShape *shape = shapes.at(count);
657
658 if (omitHiddenShapes && !shape->isVisible()) {
659 shapes.removeAt(count);
660 } else {
661 const QPainterPath outline = shape->absoluteTransformation().map(shape->outline());
662
663 if (!containedMode && !outline.intersects(rect) && !outline.contains(rect)) {
664 shapes.removeAt(count);
665
666 } else if (containedMode) {
667
668 QPainterPath containingPath;
669 containingPath.addRect(rect);
670
671 if (!containingPath.contains(outline)) {
672 shapes.removeAt(count);
673 }
674 }
675 }
676 }
677
678 return shapes;
679}
680
681void KoShapeManager::update(const QRectF &rect, const KoShape *shape, bool selectionHandles)
682{
683 if (d->updatesBlocked) return;
684
685 {
686 QMutexLocker l(&d->shapesMutex);
687
688 d->compressedUpdate |= rect;
689
690 if (selectionHandles) {
691 d->compressedUpdatedShapes.insert(shape);
692 }
693 }
694
695 emit(forwardUpdate());
696}
697
699{
700 d->updatesBlocked = value;
701}
702
704{
705 return d->updatesBlocked;
706}
708{
709 {
710 QMutexLocker l(&d->treeMutex);
711
712 Q_ASSERT(shape);
713 if (d->aggregate4update.contains(shape)) {
714 return;
715 }
716
717 d->aggregate4update.insert(shape);
718 }
719
720 KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
721 if (container) {
722 Q_FOREACH (KoShape *child, container->shapes())
723 notifyShapeChanged(child);
724 }
725}
726
728{
729 QMutexLocker l(&d->shapesMutex);
730
731 return d->shapes;
732}
733
735{
736 QMutexLocker l(&d->shapesMutex);
737
739 // get all toplevel shapes
740 Q_FOREACH (KoShape *shape, d->shapes) {
741 if (!shape->parent() || dynamic_cast<KoShapeLayer*>(shape->parent())) {
742 shapes.append(shape);
743 }
744 }
745 return shapes;
746}
747
749{
750 return d->selection;
751}
752
754{
755 d->updateTree();
756}
757
759{
760 return d->canvas;
761}
762
763//have to include this because of Q_PRIVATE_SLOT
764#include "moc_KoShapeManager.cpp"
#define warnFlake
Definition FlakeDebug.h:16
float value(const T *src, size_t ch)
const Params2D p
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...
Definition KisForest.h:943
static void applyClipping(KoShape *clippedShape, QPainter &painter)
Applies the clipping to the given painter.
QList< KoShape * > shapes() const
Repaint
enum for add()
@ 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)
Private *const d
KoCanvasBase * canvas
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)
KoSelection * selection
KoShapeManager * q
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
Definition KoShape.cpp:195
virtual QRectF outlineRect() const
Definition KoShape.cpp:566
virtual QPainterPath outline() const
Definition KoShape.cpp:559
virtual QVector< PaintOrder > paintOrder() const
paintOrder
Definition KoShape.cpp:693
bool isSelectable() const
Definition KoShape.cpp:837
void addShapeManager(KoShapeManager *manager)
Definition KoShape.cpp:120
void applyAbsoluteTransformation(const QTransform &matrix)
Definition KoShape.cpp:353
static bool compareShapeZIndex(KoShape *s1, KoShape *s2)
Definition KoShape.cpp:393
virtual QRectF boundingRect() const
Get the bounding box of the shape.
Definition KoShape.cpp:299
KoClipPath * clipPath() const
Returns the currently set clip path or 0 if there is no clip path set.
Definition KoShape.cpp:933
virtual void update() const
Definition KoShape.cpp:534
KoShapeContainer * parent() const
Definition KoShape.cpp:862
void removeShapeManager(KoShapeManager *manager)
Definition KoShape.cpp:125
static QList< KoShape * > linearizeSubtreeSorted(const QList< KoShape * > &shapes)
Definition KoShape.cpp:1194
QTransform absoluteTransformation() const
Definition KoShape.cpp:335
virtual void paintMarkers(QPainter &painter) const
paintStroke paints the shape's markers
Definition KoShape.cpp:202
KoClipMask * clipMask() const
Returns the currently set clip mask or 0 if there is no clip mask set.
Definition KoShape.cpp:945
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
Definition KoShape.cpp:172
QTransform transformation() const
Returns the shapes local transformation matrix.
Definition KoShape.cpp:383
@ Stroke
Definition KoShape.h:117
@ Markers
Definition KoShape.h:118
virtual bool hitTest(const QPointF &position) const
Check if the shape is hit on position.
Definition KoShape.cpp:280
bool isVisible(bool recursive=true) const
Definition KoShape.cpp:802
qreal transparency(bool recursive=false) const
Definition KoShape.cpp:650
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define bounds(x, a, b)
bool isEnd(const ChildIterator< T, is_const > &it)
Definition KisForest.h:341
ChildIterator< value_type, is_const > childBegin(const ChildIterator< value_type, is_const > &it)
Definition KisForest.h:290
ChildIterator< value_type, is_const > parent(const ChildIterator< value_type, is_const > &it)
Definition KisForest.h:327
ChildIterator< value_type, is_const > childEnd(const ChildIterator< value_type, is_const > &it)
Definition KisForest.h:300
QRect safeClipBoundingRect(const QPainter &painter)
ShapeSelection
Definition KoFlake.h:70
@ Unselected
return the first unselected on top.
Definition KoFlake.h:72
@ Selected
return the first selected with the highest z-ordering (i.e. on top).
Definition KoFlake.h:71
@ ShapeOnTop
return the shape highest z-ordering, regardless of selection.
Definition KoFlake.h:74
@ NextUnselected
return the first unselected directly under a selected shape, or the top most one if nothing is select...
Definition KoFlake.h:73
void drawMask(QPainter *painter, KoShape *shape)
std::shared_ptr< ShapesStorage > SharedSafeStorage
QList< KoShape * > shapes
void notifyShapeDestructed(KoShape *shape)
KisCanvas2 * canvas