KoShapeManager.cpp 21.4 KB
Newer Older
Thomas Zander's avatar
Thomas Zander committed
1 2
/* This file is part of the KDE project

3
   Copyright (C) 2006-2008 Thorsten Zachmann <zachmann@kde.org>
4
   Copyright (C) 2006-2010 Thomas Zander <zander@kde.org>
5
   Copyright (C) 2009-2010 Jan Hambrecht <jaham@gmx.net>
6

Thomas Zander's avatar
Thomas Zander committed
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
*/

#include "KoShapeManager.h"
24
#include "KoShapeManager_p.h"
Thomas Zander's avatar
Thomas Zander committed
25
#include "KoSelection.h"
26 27
#include "KoToolManager.h"
#include "KoPointerEvent.h"
Thomas Zander's avatar
Thomas Zander committed
28
#include "KoShape.h"
29
#include "KoShape_p.h"
Thomas Zander's avatar
Thomas Zander committed
30 31
#include "KoCanvasBase.h"
#include "KoShapeContainer.h"
32
#include "KoShapeStrokeModel.h"
33
#include "KoShapeGroup.h"
34
#include "KoToolProxy.h"
35
#include "KoShapeShadow.h"
36
#include "KoShapeLayer.h"
37
#include "KoFilterEffect.h"
38
#include "KoFilterEffectStack.h"
39
#include "KoFilterEffectRenderContext.h"
40
#include "KoShapeBackground.h"
Thomas Zander's avatar
Thomas Zander committed
41
#include <KoRTree.h>
42
#include "KoClipPath.h"
43
#include "KoClipMaskPainter.h"
44
#include "KoShapePaintingContext.h"
45
#include "KoViewConverter.h"
46
#include "KisQPainterStateSaver.h"
47 48
#include "KoSvgTextChunkShape.h"
#include "KoSvgTextShape.h"
49
#include <QApplication>
Thomas Zander's avatar
Thomas Zander committed
50

Thomas Zander's avatar
Thomas Zander committed
51
#include <QPainter>
52
#include <QTimer>
Boudewijn Rempt's avatar
Boudewijn Rempt committed
53
#include <FlakeDebug.h>
54

55 56
#include "kis_painting_tweaks.h"

57 58
bool KoShapeManager::Private::shapeUsedInRenderingTree(KoShape *shape)
{
59 60 61 62 63
    // FIXME: make more general!

    return !dynamic_cast<KoShapeGroup*>(shape) &&
            !dynamic_cast<KoShapeLayer*>(shape) &&
            !(dynamic_cast<KoSvgTextChunkShape*>(shape) && !dynamic_cast<KoSvgTextShape*>(shape));
64
}
65 66 67 68

void KoShapeManager::Private::updateTree()
{
    // for detecting collisions between shapes.
69 70
    DetectCollision detector;
    bool selectionModified = false;
71
    bool anyModified = false;
72
    Q_FOREACH (KoShape *shape, aggregate4update) {
73 74 75
        if (shapeIndexesBeforeUpdate.contains(shape))
            detector.detect(tree, shape, shapeIndexesBeforeUpdate[shape]);
        selectionModified = selectionModified || selection->isSelected(shape);
76
        anyModified = true;
77 78
    }

Thomas Zander's avatar
Thomas Zander committed
79
    foreach (KoShape *shape, aggregate4update) {
80 81
        if (!shapeUsedInRenderingTree(shape)) continue;

82 83 84 85 86 87
        tree.remove(shape);
        QRectF br(shape->boundingRect());
        tree.insert(br, shape);
    }

    // do it again to see which shapes we intersect with _after_ moving.
88
    foreach (KoShape *shape, aggregate4update) {
89
        detector.detect(tree, shape, shapeIndexesBeforeUpdate[shape]);
90
    }
91 92 93 94 95 96 97
    aggregate4update.clear();
    shapeIndexesBeforeUpdate.clear();

    detector.fireSignals();
    if (selectionModified) {
        emit q->selectionContentChanged();
    }
98 99 100
    if (anyModified) {
        emit q->contentChanged();
    }
101 102
}

103
void KoShapeManager::Private::paintGroup(KoShapeGroup *group, QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext)
104
{
Thomas Zander's avatar
Thomas Zander committed
105
    QList<KoShape*> shapes = group->shapes();
106
    std::sort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex);
107
    Q_FOREACH (KoShape *child, shapes) {
108
        // we paint recursively here, so we do not have to check recursively for visibility
109
        if (!child->isVisible(false))
110
            continue;
Thomas Zander's avatar
Thomas Zander committed
111
        KoShapeGroup *childGroup = dynamic_cast<KoShapeGroup*>(child);
112
        if (childGroup) {
113
            paintGroup(childGroup, painter, converter, paintContext);
114
        } else {
115
            painter.save();
116
            KoShapeManager::renderSingleShape(child, painter, converter, paintContext);
117
            painter.restore();
118 119 120 121
        }
    }
}

122
KoShapeManager::KoShapeManager(KoCanvasBase *canvas, const QList<KoShape *> &shapes)
123
    : d(new Private(this, canvas))
Thomas Zander's avatar
Thomas Zander committed
124
{
125
    Q_ASSERT(d->canvas); // not optional.
126
    connect(d->selection, SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged()));
Thomas Zander's avatar
Thomas Zander committed
127
    setShapes(shapes);
128 129 130 131 132 133 134

    /**
     * Shape manager uses signal compressors with timers, therefore
     * it might handle queued signals, therefore it should belong
     * to the GUI thread.
     */
    this->moveToThread(qApp->thread());
Thomas Zander's avatar
Thomas Zander committed
135 136 137
}

KoShapeManager::KoShapeManager(KoCanvasBase *canvas)
138
    : d(new Private(this, canvas))
Thomas Zander's avatar
Thomas Zander committed
139
{
140
    Q_ASSERT(d->canvas); // not optional.
141
    connect(d->selection, SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged()));
142 143 144

    // see a comment in another constructor
    this->moveToThread(qApp->thread());
Thomas Zander's avatar
Thomas Zander committed
145 146
}

147
void KoShapeManager::Private::unlinkFromShapesRecursively(const QList<KoShape*> &shapes)
Thomas Zander's avatar
Thomas Zander committed
148
{
149
    Q_FOREACH (KoShape *shape, shapes) {
150
        shape->removeShapeManager(q);
151 152 153 154 155

        KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
        if (container) {
            unlinkFromShapesRecursively(container->shapes());
        }
156
    }
157 158 159 160 161 162 163
}

KoShapeManager::~KoShapeManager()
{
    d->unlinkFromShapesRecursively(d->shapes);
    d->shapes.clear();

164
    delete d;
Thomas Zander's avatar
Thomas Zander committed
165 166
}

167
void KoShapeManager::setShapes(const QList<KoShape *> &shapes, Repaint repaint)
Thomas Zander's avatar
Thomas Zander committed
168
{
169 170
    QMutexLocker l(&d->mutex);

171
    //clear selection
172
    d->selection->deselectAll();
173
    d->unlinkFromShapesRecursively(d->shapes);
174 175
    d->aggregate4update.clear();
    d->tree.clear();
176
    d->shapes.clear();
177

178
    Q_FOREACH (KoShape *shape, shapes) {
179
        addShape(shape, repaint);
180
    }
Thomas Zander's avatar
Thomas Zander committed
181 182
}

183
void KoShapeManager::addShape(KoShape *shape, Repaint repaint)
184
{
185 186
    QMutexLocker l(&d->mutex);

187
    if (d->shapes.contains(shape))
188
        return;
189
    shape->addShapeManager(this);
190
    d->shapes.append(shape);
191

192
    if (d->shapeUsedInRenderingTree(shape)) {
193 194
        QRectF br(shape->boundingRect());
        d->tree.insert(br, shape);
195
    }
196

197
    if (repaint == PaintShapeOnAdd) {
198
        shape->update();
199
    }
200 201

    // add the children of a KoShapeContainer
Thomas Zander's avatar
Thomas Zander committed
202
    KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
203

204
    if (container) {
Thomas Zander's avatar
Thomas Zander committed
205
        foreach (KoShape *containerShape, container->shapes()) {
206
            addShape(containerShape, repaint);
207 208
        }
    }
209 210 211 212

    Private::DetectCollision detector;
    detector.detect(d->tree, shape, shape->zIndex());
    detector.fireSignals();
Thomas Zander's avatar
Thomas Zander committed
213 214
}

215
void KoShapeManager::remove(KoShape *shape)
216
{
217 218
    QMutexLocker l(&d->mutex);

219 220 221 222
    Private::DetectCollision detector;
    detector.detect(d->tree, shape, shape->zIndex());
    detector.fireSignals();

223
    shape->update();
224
    shape->removeShapeManager(this);
225 226
    d->selection->deselect(shape);
    d->aggregate4update.remove(shape);
227

228 229 230
    if (d->shapeUsedInRenderingTree(shape)) {
        d->tree.remove(shape);
    }
231
    d->shapes.removeAll(shape);
232 233

    // remove the children of a KoShapeContainer
Thomas Zander's avatar
Thomas Zander committed
234
    KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
235
    if (container) {
Thomas Zander's avatar
Thomas Zander committed
236
        foreach (KoShape *containerShape, container->shapes()) {
237 238 239
            remove(containerShape);
        }
    }
240 241 242 243 244 245 246 247 248
}

KoShapeManager::ShapeInterface::ShapeInterface(KoShapeManager *_q)
    : q(_q)
{
}

void KoShapeManager::ShapeInterface::notifyShapeDestructed(KoShape *shape)
{
249 250
    QMutexLocker l(&q->d->mutex);

251 252 253 254 255 256 257 258
    q->d->selection->deselect(shape);
    q->d->aggregate4update.remove(shape);

    // we cannot access RTTI of the semi-destructed shape, so just
    // unlink it lazily
    if (q->d->tree.contains(shape)) {
        q->d->tree.remove(shape);
    }
259

260
    q->d->shapes.removeAll(shape);
Thomas Zander's avatar
Thomas Zander committed
261 262
}

263 264 265 266 267 268 269

KoShapeManager::ShapeInterface *KoShapeManager::shapeInterface()
{
    return &d->shapeInterface;
}


270
void KoShapeManager::paint(QPainter &painter, const KoViewConverter &converter, bool forPrint)
Thomas Zander's avatar
Thomas Zander committed
271
{
272 273
    QMutexLocker l(&d->mutex);

274
    d->updateTree();
275 276
    painter.setPen(Qt::NoPen);  // painters by default have a black stroke, lets turn that off.
    painter.setBrush(Qt::NoBrush);
277

Jan Hambrecht's avatar
Jan Hambrecht committed
278
    QList<KoShape*> unsortedShapes;
279
    if (painter.hasClipping()) {
280
        QRectF rect = converter.viewToDocument(KisPaintingTweaks::safeClipBoundingRect(painter));
281 282
        unsortedShapes = d->tree.intersects(rect);
    } else {
Jan Hambrecht's avatar
Jan Hambrecht committed
283
        unsortedShapes = shapes();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
284
        warnFlake << "KoShapeManager::paint  Painting with a painter that has no clipping will lead to too much being painted!";
285
    }
Jan Hambrecht's avatar
Jan Hambrecht committed
286 287

    // filter all hidden shapes from the list
288
    // also filter shapes with a parent which has filter effects applied
Jan Hambrecht's avatar
Jan Hambrecht committed
289
    QList<KoShape*> sortedShapes;
Thomas Zander's avatar
Thomas Zander committed
290
    foreach (KoShape *shape, unsortedShapes) {
291
        if (!shape->isVisible())
292 293 294 295 296
            continue;
        bool addShapeToList = true;
        // check if one of the shapes ancestors have filter effects
        KoShapeContainer *parent = shape->parent();
        while (parent) {
297 298 299
            // parent must be part of the shape manager to be taken into account
            if (!d->shapes.contains(parent))
                break;
300
            if (parent->filterEffectStack() && !parent->filterEffectStack()->isEmpty()) {
301 302
                addShapeToList = false;
                break;
303 304 305 306
            }
            parent = parent->parent();
        }
        if (addShapeToList) {
307
            sortedShapes.append(shape);
308 309 310
        } else if (parent) {
            sortedShapes.append(parent);
        }
Jan Hambrecht's avatar
Jan Hambrecht committed
311 312
    }

313
    std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex);
314

315
    KoShapePaintingContext paintContext(d->canvas, forPrint); //FIXME
316

317 318
    foreach (KoShape *shape, sortedShapes) {
        renderSingleShape(shape, painter, converter, paintContext);
Thomas Zander's avatar
Thomas Zander committed
319 320
    }

Boudewijn Rempt's avatar
Boudewijn Rempt committed
321
#ifdef CALLIGRA_RTREE_DEBUG
322
    // paint tree
323 324
    qreal zx = 0;
    qreal zy = 0;
325
    converter.zoom(&zx, &zy);
326
    painter.save();
327 328
    painter.scale(zx, zy);
    d->tree.paint(painter);
329 330 331
    painter.restore();
#endif

332
    if (! forPrint) {
333
        KoShapePaintingContext paintContext(d->canvas, forPrint); //FIXME
334 335
        d->selection->paint(painter, converter, paintContext);
    }
Thomas Zander's avatar
Thomas Zander committed
336
}
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351

void KoShapeManager::renderSingleShape(KoShape *shape, QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext)
{
    KisQPainterStateSaver saver(&painter);

    // apply shape clipping
    KoClipPath::applyClipping(shape, painter, converter);

    // apply transformation
    painter.setTransform(shape->absoluteTransformation(&converter) * painter.transform());

    // paint the shape
    paintShape(shape, painter, converter, paintContext);
}

352
void KoShapeManager::paintShape(KoShape *shape, QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext)
353
{
354 355 356 357
    qreal transparency = shape->transparency(true);
    if (transparency > 0.0) {
        painter.setOpacity(1.0-transparency);
    }
358

359
    if (shape->shadow()) {
360
        painter.save();
361
        shape->shadow()->paint(shape, painter, converter);
362 363
        painter.restore();
    }
Thomas Zander's avatar
Thomas Zander committed
364
    if (!shape->filterEffectStack() || shape->filterEffectStack()->isEmpty()) {
365 366 367 368 369 370 371 372 373 374

        QScopedPointer<KoClipMaskPainter> clipMaskPainter;
        QPainter *shapePainter = &painter;

        KoClipMask *clipMask = shape->clipMask();
        if (clipMask) {
            clipMaskPainter.reset(new KoClipMaskPainter(&painter, shape->boundingRect()));
            shapePainter = clipMaskPainter->shapePainter();
        }

375 376 377 378 379 380 381
        /**
         * We expect the shape to save/restore the painter's state itself. Such design was not
         * not always here, so we need a period of sanity checks to ensure all the shapes are
         * ported correctly.
         */
        const QTransform sanityCheckTransformSaved = shapePainter->transform();

382
        shape->paint(*shapePainter, converter, paintContext);
383
        shape->paintStroke(*shapePainter, converter, paintContext);
384 385 386 387

        KIS_SAFE_ASSERT_RECOVER(shapePainter->transform() == sanityCheckTransformSaved) {
            shapePainter->setTransform(sanityCheckTransformSaved);
        }
388 389 390 391

        if (clipMask) {
            shape->clipMask()->drawMask(clipMaskPainter->maskPainter(), shape);
            clipMaskPainter->renderOnGlobalPainter();
392
        }
393

394
    } else {
395 396
        // TODO: clipping mask is not implemented for this case!

Thorsten Zachmann's avatar
Thorsten Zachmann committed
397
        // There are filter effects, then we need to prerender the shape on an image, to filter it
398
        QRectF shapeBound(QPointF(), shape->size());
399
        // First step, compute the rectangle used for the image
400 401
        QRectF clipRegion = shape->filterEffectStack()->clipRectForBoundingRect(shapeBound);
        // convert clip region to view coordinates
402
        QRectF zoomedClipRegion = converter.documentToView(clipRegion);
403
        // determine the offset of the clipping rect from the shapes origin
404
        QPointF clippingOffset = zoomedClipRegion.topLeft();
405

406
        // Initialize the buffer image
407
        QImage sourceGraphic(zoomedClipRegion.size().toSize(), QImage::Format_ARGB32_Premultiplied);
Jan Hambrecht's avatar
Jan Hambrecht committed
408
        sourceGraphic.fill(qRgba(0,0,0,0));
409

410 411 412 413 414 415 416 417 418 419 420 421 422
        QHash<QString, QImage> imageBuffers;

        QSet<QString> requiredStdInputs = shape->filterEffectStack()->requiredStandarsInputs();

        if (requiredStdInputs.contains("SourceGraphic") || requiredStdInputs.contains("SourceAlpha")) {
            // Init the buffer painter
            QPainter imagePainter(&sourceGraphic);
            imagePainter.translate(-1.0f*clippingOffset);
            imagePainter.setPen(Qt::NoPen);
            imagePainter.setBrush(Qt::NoBrush);
            imagePainter.setRenderHint(QPainter::Antialiasing, painter.testRenderHint(QPainter::Antialiasing));

            // Paint the shape on the image
Thomas Zander's avatar
Thomas Zander committed
423
            KoShapeGroup *group = dynamic_cast<KoShapeGroup*>(shape);
424 425 426
            if (group) {
                // the childrens matrix contains the groups matrix as well
                // so we have to compensate for that before painting the children
427
                imagePainter.setTransform(group->absoluteTransformation(&converter).inverted(), true);
428
                Private::paintGroup(group, imagePainter, converter, paintContext);
429
            } else {
430
                imagePainter.save();
431
                shape->paint(imagePainter, converter, paintContext);
432
                shape->paintStroke(imagePainter, converter, paintContext);
433
                imagePainter.restore();
434
                imagePainter.end();
435
            }
436 437 438 439 440 441
        }
        if (requiredStdInputs.contains("SourceAlpha")) {
            QImage sourceAlpha = sourceGraphic;
            sourceAlpha.fill(qRgba(0,0,0,255));
            sourceAlpha.setAlphaChannel(sourceGraphic.alphaChannel());
            imageBuffers.insert("SourceAlpha", sourceAlpha);
442
        }
443 444 445 446 447
        if (requiredStdInputs.contains("FillPaint")) {
            QImage fillPaint = sourceGraphic;
            if (shape->background()) {
                QPainter fillPainter(&fillPaint);
                QPainterPath fillPath;
448
                fillPath.addRect(fillPaint.rect().adjusted(-1,-1,1,1));
449
                shape->background()->paint(fillPainter, converter, paintContext, fillPath);
450 451 452 453 454
            } else {
                fillPaint.fill(qRgba(0,0,0,0));
            }
            imageBuffers.insert("FillPaint", fillPaint);
        }
455

Jan Hambrecht's avatar
Jan Hambrecht committed
456 457
        imageBuffers.insert("SourceGraphic", sourceGraphic);
        imageBuffers.insert(QString(), sourceGraphic);
458

459
        KoFilterEffectRenderContext renderContext(converter);
460
        renderContext.setShapeBoundingBox(shapeBound);
461

462
        QImage result;
463
        QList<KoFilterEffect*> filterEffects = shape->filterEffectStack()->filterEffects();
464
        // Filter
Thomas Zander's avatar
Thomas Zander committed
465
        foreach (KoFilterEffect *filterEffect, filterEffects) {
466
            QRectF filterRegion = filterEffect->filterRectForBoundingRect(shapeBound);
467
            filterRegion = converter.documentToView(filterRegion);
468
            QRect subRegion = filterRegion.translated(-clippingOffset).toRect();
469
            // set current filter region
470 471
            renderContext.setFilterRegion(subRegion & sourceGraphic.rect());

472
            if (filterEffect->maximalInputCount() <= 1) {
473 474
                QList<QString> inputs = filterEffect->inputs();
                QString input = inputs.count() ? inputs.first() : QString();
475
                // get input image from image buffers and apply the filter effect
476 477
                QImage image = imageBuffers.value(input);
                if (!image.isNull()) {
478
                    result = filterEffect->processImage(imageBuffers.value(input), renderContext);
479
                }
480 481
            } else {
                QList<QImage> inputImages;
482
                Q_FOREACH (const QString &input, filterEffect->inputs()) {
483 484
                    QImage image = imageBuffers.value(input);
                    if (!image.isNull())
485
                        inputImages.append(imageBuffers.value(input));
486 487
                }
                // apply the filter effect
488 489
                if (filterEffect->inputs().count() == inputImages.count())
                    result = filterEffect->processImages(inputImages, renderContext);
490
            }
491 492
            // store result of effect
            imageBuffers.insert(filterEffect->output(), result);
493 494
        }

Thomas Zander's avatar
Thomas Zander committed
495
        KoFilterEffect *lastEffect = filterEffects.last();
496

497
        // Paint the result
498
        painter.save();
499
        painter.drawImage(clippingOffset, imageBuffers.value(lastEffect->output()));
500 501 502 503
        painter.restore();
    }
}

Thomas Zander's avatar
Thomas Zander committed
504
KoShape *KoShapeManager::shapeAt(const QPointF &position, KoFlake::ShapeSelection selection, bool omitHiddenShapes)
Thomas Zander's avatar
Thomas Zander committed
505
{
506 507
    QMutexLocker l(&d->mutex);

508
    d->updateTree();
509
    QList<KoShape*> sortedShapes(d->tree.contains(position));
510
    std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex);
Thomas Zander's avatar
Thomas Zander committed
511
    KoShape *firstUnselectedShape = 0;
512
    for (int count = sortedShapes.count() - 1; count >= 0; count--) {
Thomas Zander's avatar
Thomas Zander committed
513
        KoShape *shape = sortedShapes.at(count);
514
        if (omitHiddenShapes && ! shape->isVisible())
515
            continue;
516
        if (! shape->hitTest(position))
517 518
            continue;

519 520
        switch (selection) {
        case KoFlake::ShapeOnTop:
521 522
            if (shape->isSelectable())
                return shape;
523
            break;
524 525
        case KoFlake::Selected:
            if (d->selection->isSelected(shape))
526
                return shape;
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
            break;
        case KoFlake::Unselected:
            if (! d->selection->isSelected(shape))
                return shape;
            break;
        case KoFlake::NextUnselected:
            // we want an unselected shape
            if (d->selection->isSelected(shape))
                continue;
            // memorize the first unselected shape
            if (! firstUnselectedShape)
                firstUnselectedShape = shape;
            // check if the shape above is selected
            if (count + 1 < sortedShapes.count() && d->selection->isSelected(sortedShapes.at(count + 1)))
                return shape;
            break;
Thomas Zander's avatar
Thomas Zander committed
543 544
        }
    }
545
    // if we want the next unselected below a selected but there was none selected,
546
    // return the first found unselected shape
547
    if (selection == KoFlake::NextUnselected && firstUnselectedShape)
548 549
        return firstUnselectedShape;

550
    if (d->selection->hitTest(position))
551
        return d->selection;
Thomas Zander's avatar
Thomas Zander committed
552 553 554

    return 0; // missed everything
}
Laurent Montel's avatar
Laurent Montel committed
555

556
QList<KoShape *> KoShapeManager::shapesAt(const QRectF &rect, bool omitHiddenShapes, bool containedMode)
557
{
558 559
    QMutexLocker l(&d->mutex);

560
    d->updateTree();
561 562 563
    QList<KoShape*> shapes(containedMode ? d->tree.contained(rect) : d->tree.intersects(rect));

    for (int count = shapes.count() - 1; count >= 0; count--) {
564

565
        KoShape *shape = shapes.at(count);
566

567
        if (omitHiddenShapes && !shape->isVisible()) {
568
            shapes.removeAt(count);
Thomas Zander's avatar
Thomas Zander committed
569
        } else {
570
            const QPainterPath outline = shape->absoluteTransformation(0).map(shape->outline());
571 572 573 574 575 576 577 578 579 580 581 582

            if (!containedMode && !outline.intersects(rect) && !outline.contains(rect)) {
                shapes.removeAt(count);

            } else if (containedMode) {

                QPainterPath containingPath;
                containingPath.addRect(rect);

                if (!containingPath.contains(outline)) {
                    shapes.removeAt(count);
                }
583
            }
584
        }
585
    }
586 587

    return shapes;
588 589
}

590
void KoShapeManager::update(const QRectF &rect, const KoShape *shape, bool selectionHandles)
591
{
592 593
    // TODO: do we need locking here?

594 595 596
    d->canvas->updateCanvas(rect);
    if (selectionHandles && d->selection->isSelected(shape)) {
        if (d->canvas->toolProxy())
597
            d->canvas->toolProxy()->repaintDecorations();
598 599 600
    }
}

Thomas Zander's avatar
Thomas Zander committed
601
void KoShapeManager::notifyShapeChanged(KoShape *shape)
602
{
603 604
    QMutexLocker l(&d->mutex);

605
    Q_ASSERT(shape);
606
    if (d->aggregate4update.contains(shape)) {
607
        return;
608
    }
609

610
    d->aggregate4update.insert(shape);
611
    d->shapeIndexesBeforeUpdate.insert(shape, shape->zIndex());
612

613
    KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
614
    if (container) {
615
        Q_FOREACH (KoShape *child, container->shapes())
616
            notifyShapeChanged(child);
617
    }
618 619
}

620
QList<KoShape*> KoShapeManager::shapes() const
621
{
622 623
    QMutexLocker l(&d->mutex);

624 625 626
    return d->shapes;
}

627 628
QList<KoShape*> KoShapeManager::topLevelShapes() const
{
629 630
    QMutexLocker l(&d->mutex);

631 632
    QList<KoShape*> shapes;
    // get all toplevel shapes
633
    Q_FOREACH (KoShape *shape, d->shapes) {
634
        if (!shape->parent() || dynamic_cast<KoShapeLayer*>(shape->parent())) {
635 636 637 638 639 640
            shapes.append(shape);
        }
    }
    return shapes;
}

Thomas Zander's avatar
Thomas Zander committed
641
KoSelection *KoShapeManager::selection() const
642
{
643 644 645
    return d->selection;
}

Boudewijn Rempt's avatar
Boudewijn Rempt committed
646 647 648 649 650
KoCanvasBase *KoShapeManager::canvas()
{
    return d->canvas;
}

651 652
//have to include this because of Q_PRIVATE_SLOT
#include "moc_KoShapeManager.cpp"