Warning, file /office/calligra/libs/flake/KoShapeManager.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* This file is part of the KDE project
0002 
0003    Copyright (C) 2006-2008 Thorsten Zachmann <zachmann@kde.org>
0004    Copyright (C) 2006-2010 Thomas Zander <zander@kde.org>
0005    Copyright (C) 2009-2010 Jan Hambrecht <jaham@gmx.net>
0006 
0007    This library is free software; you can redistribute it and/or
0008    modify it under the terms of the GNU Library General Public
0009    License as published by the Free Software Foundation; either
0010    version 2 of the License, or (at your option) any later version.
0011 
0012    This library is distributed in the hope that it will be useful,
0013    but WITHOUT ANY WARRANTY; without even the implied warranty of
0014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015    Library General Public License for more details.
0016 
0017    You should have received a copy of the GNU Library General Public License
0018    along with this library; see the file COPYING.LIB.  If not, write to
0019    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0020  * Boston, MA 02110-1301, USA.
0021 */
0022 
0023 #include "KoShapeManager.h"
0024 #include "KoShapeManager_p.h"
0025 #include "KoSelection.h"
0026 #include "KoToolManager.h"
0027 #include "KoPointerEvent.h"
0028 #include "KoShape.h"
0029 #include "KoShape_p.h"
0030 #include "KoCanvasBase.h"
0031 #include "KoShapeContainer.h"
0032 #include "KoShapeStrokeModel.h"
0033 #include "KoShapeGroup.h"
0034 #include "KoToolProxy.h"
0035 #include "KoShapeManagerPaintingStrategy.h"
0036 #include "KoShapeShadow.h"
0037 #include "KoShapeLayer.h"
0038 #include "KoFilterEffect.h"
0039 #include "KoFilterEffectStack.h"
0040 #include "KoFilterEffectRenderContext.h"
0041 #include "KoShapeBackground.h"
0042 #include <KoRTree.h>
0043 #include "KoClipPath.h"
0044 #include "KoShapePaintingContext.h"
0045 
0046 #include <QPainter>
0047 #include <QPainterPath>
0048 #include <QTimer>
0049 #include <FlakeDebug.h>
0050 
0051 #include <algorithm>
0052 
0053 void KoShapeManager::Private::updateTree()
0054 {
0055     // for detecting collisions between shapes.
0056     DetectCollision detector;
0057     bool selectionModified = false;
0058     bool anyModified = false;
0059     foreach(KoShape *shape, aggregate4update) {
0060         if (shapeIndexesBeforeUpdate.contains(shape))
0061             detector.detect(tree, shape, shapeIndexesBeforeUpdate[shape]);
0062         selectionModified = selectionModified || selection->isSelected(shape);
0063         anyModified = true;
0064     }
0065 
0066     foreach (KoShape *shape, aggregate4update) {
0067         tree.remove(shape);
0068         QRectF br(shape->boundingRect());
0069         strategy->adapt(shape, br);
0070         tree.insert(br, shape);
0071     }
0072 
0073     // do it again to see which shapes we intersect with _after_ moving.
0074     foreach (KoShape *shape, aggregate4update)
0075         detector.detect(tree, shape, shapeIndexesBeforeUpdate[shape]);
0076     aggregate4update.clear();
0077     shapeIndexesBeforeUpdate.clear();
0078 
0079     detector.fireSignals();
0080     if (selectionModified) {
0081         selection->updateSizeAndPosition();
0082         emit q->selectionContentChanged();
0083     }
0084     if (anyModified) {
0085         emit q->contentChanged();
0086     }
0087 }
0088 
0089 void KoShapeManager::Private::paintGroup(KoShapeGroup *group, QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext)
0090 {
0091     QList<KoShape*> shapes = group->shapes();
0092     std::sort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex);
0093     foreach(KoShape *child, shapes) {
0094         // we paint recursively here, so we do not have to check recursively for visibility
0095         if (!child->isVisible())
0096             continue;
0097         KoShapeGroup *childGroup = dynamic_cast<KoShapeGroup*>(child);
0098         if (childGroup) {
0099             paintGroup(childGroup, painter, converter, paintContext);
0100         } else {
0101             painter.save();
0102             strategy->paint(child, painter, converter, paintContext);
0103             painter.restore();
0104         }
0105     }
0106 }
0107 
0108 KoShapeManager::KoShapeManager(KoCanvasBase *canvas, const QList<KoShape *> &shapes)
0109         : d(new Private(this, canvas))
0110 {
0111     Q_ASSERT(d->canvas); // not optional.
0112     connect(d->selection, SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged()));
0113     setShapes(shapes);
0114 }
0115 
0116 KoShapeManager::KoShapeManager(KoCanvasBase *canvas)
0117         : d(new Private(this, canvas))
0118 {
0119     Q_ASSERT(d->canvas); // not optional.
0120     connect(d->selection, SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged()));
0121 }
0122 
0123 KoShapeManager::~KoShapeManager()
0124 {
0125     foreach(KoShape *shape, d->shapes) {
0126         shape->priv()->removeShapeManager(this);
0127     }
0128     foreach(KoShape *shape, d->additionalShapes) {
0129         shape->priv()->removeShapeManager(this);
0130     }
0131     delete d;
0132 }
0133 
0134 
0135 void KoShapeManager::setShapes(const QList<KoShape *> &shapes, Repaint repaint)
0136 {
0137     //clear selection
0138     d->selection->deselectAll();
0139     foreach(KoShape *shape, d->shapes) {
0140         shape->priv()->removeShapeManager(this);
0141     }
0142     d->aggregate4update.clear();
0143     d->tree.clear();
0144     d->shapes.clear();
0145     foreach(KoShape *shape, shapes) {
0146         addShape(shape, repaint);
0147     }
0148 }
0149 
0150 void KoShapeManager::addShape(KoShape *shape, Repaint repaint)
0151 {
0152     if (d->shapes.contains(shape))
0153         return;
0154     shape->priv()->addShapeManager(this);
0155     d->shapes.append(shape);
0156     if (! dynamic_cast<KoShapeGroup*>(shape) && ! dynamic_cast<KoShapeLayer*>(shape)) {
0157         QRectF br(shape->boundingRect());
0158         d->tree.insert(br, shape);
0159     }
0160     if (repaint == PaintShapeOnAdd) {
0161         shape->update();
0162     }
0163 
0164     // add the children of a KoShapeContainer
0165     KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
0166 
0167     if (container) {
0168         foreach (KoShape *containerShape, container->shapes()) {
0169             addShape(containerShape, repaint);
0170         }
0171     }
0172 
0173     Private::DetectCollision detector;
0174     detector.detect(d->tree, shape, shape->zIndex());
0175     detector.fireSignals();
0176 }
0177 
0178 void KoShapeManager::addAdditional(KoShape *shape)
0179 {
0180     if (shape) {
0181         if (d->additionalShapes.contains(shape)) {
0182             return;
0183         }
0184         shape->priv()->addShapeManager(this);
0185         d->additionalShapes.append(shape);
0186     }
0187 }
0188 
0189 void KoShapeManager::remove(KoShape *shape)
0190 {
0191     Private::DetectCollision detector;
0192     detector.detect(d->tree, shape, shape->zIndex());
0193     detector.fireSignals();
0194 
0195     shape->update();
0196     shape->priv()->removeShapeManager(this);
0197     d->selection->deselect(shape);
0198     d->aggregate4update.remove(shape);
0199     d->tree.remove(shape);
0200     d->shapes.removeAll(shape);
0201 
0202     // remove the children of a KoShapeContainer
0203     KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
0204     if (container) {
0205         foreach (KoShape *containerShape, container->shapes()) {
0206             remove(containerShape);
0207         }
0208     }
0209 
0210     // This signal is used in the annotation shape.
0211     // FIXME: Is this really what we want?  (and shouldn't it be called shapeDeleted()?)
0212     emit shapeRemoved(shape);
0213 }
0214 
0215 void KoShapeManager::removeAdditional(KoShape *shape)
0216 {
0217     if (shape) {
0218         shape->priv()->removeShapeManager(this);
0219         d->additionalShapes.removeAll(shape);
0220     }
0221 }
0222 
0223 void KoShapeManager::paint(QPainter &painter, const KoViewConverter &converter, bool forPrint)
0224 {
0225     d->updateTree();
0226     painter.setPen(Qt::NoPen);  // painters by default have a black stroke, lets turn that off.
0227     painter.setBrush(Qt::NoBrush);
0228 
0229     QList<KoShape*> unsortedShapes;
0230     if (painter.hasClipping()) {
0231         QRectF rect = converter.viewToDocument(painter.clipRegion().boundingRect());
0232         unsortedShapes = d->tree.intersects(rect);
0233     } else {
0234         unsortedShapes = shapes();
0235         warnFlake << "KoShapeManager::paint  Painting with a painter that has no clipping will lead to too much being painted!";
0236     }
0237 
0238     // filter all hidden shapes from the list
0239     // also filter shapes with a parent which has filter effects applied
0240     QList<KoShape*> sortedShapes;
0241     foreach (KoShape *shape, unsortedShapes) {
0242         if (!shape->isVisible(true))
0243             continue;
0244         bool addShapeToList = true;
0245         // check if one of the shapes ancestors have filter effects
0246         KoShapeContainer *parent = shape->parent();
0247         while (parent) {
0248             // parent must be part of the shape manager to be taken into account
0249             if (!d->shapes.contains(parent))
0250                 break;
0251             if (parent->filterEffectStack() && !parent->filterEffectStack()->isEmpty()) {
0252                 addShapeToList = false;
0253                 break;
0254             }
0255             parent = parent->parent();
0256         }
0257         if (addShapeToList) {
0258             sortedShapes.append(shape);
0259         } else if (parent) {
0260             sortedShapes.append(parent);
0261         }
0262     }
0263 
0264     std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex);
0265 
0266     foreach (KoShape *shape, sortedShapes) {
0267         if (shape->parent() != 0 && shape->parent()->isClipped(shape))
0268             continue;
0269 
0270         painter.save();
0271 
0272         // apply shape clipping
0273         KoClipPath::applyClipping(shape, painter, converter);
0274 
0275         // let the painting strategy paint the shape
0276         KoShapePaintingContext paintContext(d->canvas, forPrint); //FIXME
0277         d->strategy->paint(shape, painter, converter, paintContext);
0278 
0279         painter.restore();
0280     }
0281 
0282 #ifdef CALLIGRA_RTREE_DEBUG
0283     // paint tree
0284     qreal zx = 0;
0285     qreal zy = 0;
0286     converter.zoom(&zx, &zy);
0287     painter.save();
0288     painter.scale(zx, zy);
0289     d->tree.paint(painter);
0290     painter.restore();
0291 #endif
0292 
0293     if (! forPrint) {
0294         KoShapePaintingContext paintContext(d->canvas, forPrint); //FIXME
0295         d->selection->paint(painter, converter, paintContext);
0296     }
0297 }
0298 
0299 void KoShapeManager::paintShape(KoShape *shape, QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext)
0300 {
0301     qreal transparency = shape->transparency(true);
0302     if (transparency > 0.0) {
0303         painter.setOpacity(1.0-transparency);
0304     }
0305 
0306     if (shape->shadow()) {
0307         painter.save();
0308         shape->shadow()->paint(shape, painter, converter);
0309         painter.restore();
0310     }
0311     if (!shape->filterEffectStack() || shape->filterEffectStack()->isEmpty()) {
0312         painter.save();
0313         shape->paint(painter, converter, paintContext);
0314         painter.restore();
0315         if (shape->stroke()) {
0316             painter.save();
0317             shape->stroke()->paint(shape, painter, converter);
0318             painter.restore();
0319         }
0320     } else {
0321         // There are filter effects, then we need to prerender the shape on an image, to filter it
0322         QRectF shapeBound(QPointF(), shape->size());
0323         // First step, compute the rectangle used for the image
0324         QRectF clipRegion = shape->filterEffectStack()->clipRectForBoundingRect(shapeBound);
0325         // convert clip region to view coordinates
0326         QRectF zoomedClipRegion = converter.documentToView(clipRegion);
0327         // determine the offset of the clipping rect from the shapes origin
0328         QPointF clippingOffset = zoomedClipRegion.topLeft();
0329 
0330         // Initialize the buffer image
0331         QImage sourceGraphic(zoomedClipRegion.size().toSize(), QImage::Format_ARGB32_Premultiplied);
0332         sourceGraphic.fill(qRgba(0,0,0,0));
0333 
0334         QHash<QString, QImage> imageBuffers;
0335 
0336         QSet<QString> requiredStdInputs = shape->filterEffectStack()->requiredStandarsInputs();
0337 
0338         if (requiredStdInputs.contains("SourceGraphic") || requiredStdInputs.contains("SourceAlpha")) {
0339             // Init the buffer painter
0340             QPainter imagePainter(&sourceGraphic);
0341             imagePainter.translate(-1.0f*clippingOffset);
0342             imagePainter.setPen(Qt::NoPen);
0343             imagePainter.setBrush(Qt::NoBrush);
0344             imagePainter.setRenderHint(QPainter::Antialiasing, painter.testRenderHint(QPainter::Antialiasing));
0345 
0346             // Paint the shape on the image
0347             KoShapeGroup *group = dynamic_cast<KoShapeGroup*>(shape);
0348             if (group) {
0349                 // the childrens matrix contains the groups matrix as well
0350                 // so we have to compensate for that before painting the children
0351                 imagePainter.setTransform(group->absoluteTransformation(&converter).inverted(), true);
0352                 d->paintGroup(group, imagePainter, converter, paintContext);
0353             } else {
0354                 imagePainter.save();
0355                 shape->paint(imagePainter, converter, paintContext);
0356                 imagePainter.restore();
0357                 if (shape->stroke()) {
0358                     imagePainter.save();
0359                     shape->stroke()->paint(shape, imagePainter, converter);
0360                     imagePainter.restore();
0361                 }
0362                 imagePainter.end();
0363             }
0364         }
0365         if (requiredStdInputs.contains("SourceAlpha")) {
0366             QImage sourceAlpha = sourceGraphic;
0367             sourceAlpha.fill(qRgba(0,0,0,255));
0368             sourceAlpha.setAlphaChannel(sourceGraphic.alphaChannel());
0369             imageBuffers.insert("SourceAlpha", sourceAlpha);
0370         }
0371         if (requiredStdInputs.contains("FillPaint")) {
0372             QImage fillPaint = sourceGraphic;
0373             if (shape->background()) {
0374                 QPainter fillPainter(&fillPaint);
0375                 QPainterPath fillPath;
0376                 fillPath.addRect(fillPaint.rect().adjusted(-1,-1,1,1));
0377                 shape->background()->paint(fillPainter, converter, paintContext, fillPath);
0378             } else {
0379                 fillPaint.fill(qRgba(0,0,0,0));
0380             }
0381             imageBuffers.insert("FillPaint", fillPaint);
0382         }
0383 
0384         imageBuffers.insert("SourceGraphic", sourceGraphic);
0385         imageBuffers.insert(QString(), sourceGraphic);
0386 
0387         KoFilterEffectRenderContext renderContext(converter);
0388         renderContext.setShapeBoundingBox(shapeBound);
0389 
0390         QImage result;
0391         QList<KoFilterEffect*> filterEffects = shape->filterEffectStack()->filterEffects();
0392         // Filter
0393         foreach (KoFilterEffect *filterEffect, filterEffects) {
0394             QRectF filterRegion = filterEffect->filterRectForBoundingRect(shapeBound);
0395             filterRegion = converter.documentToView(filterRegion);
0396             QRect subRegion = filterRegion.translated(-clippingOffset).toRect();
0397             // set current filter region
0398             renderContext.setFilterRegion(subRegion & sourceGraphic.rect());
0399 
0400             if (filterEffect->maximalInputCount() <= 1) {
0401                 QList<QString> inputs = filterEffect->inputs();
0402                 QString input = inputs.count() ? inputs.first() : QString();
0403                 // get input image from image buffers and apply the filter effect
0404                 QImage image = imageBuffers.value(input);
0405                 if (!image.isNull()) {
0406                     result = filterEffect->processImage(imageBuffers.value(input), renderContext);
0407                 }
0408             } else {
0409                 QVector<QImage> inputImages;
0410                 foreach(const QString &input, filterEffect->inputs()) {
0411                     QImage image = imageBuffers.value(input);
0412                     if (!image.isNull())
0413                         inputImages.append(imageBuffers.value(input));
0414                 }
0415                 // apply the filter effect
0416                 if (filterEffect->inputs().count() == inputImages.count())
0417                     result = filterEffect->processImages(inputImages, renderContext);
0418             }
0419             // store result of effect
0420             imageBuffers.insert(filterEffect->output(), result);
0421         }
0422 
0423         KoFilterEffect *lastEffect = filterEffects.last();
0424 
0425         // Paint the result
0426         painter.save();
0427         painter.drawImage(clippingOffset, imageBuffers.value(lastEffect->output()));
0428         painter.restore();
0429     }
0430 }
0431 
0432 KoShape *KoShapeManager::shapeAt(const QPointF &position, KoFlake::ShapeSelection selection, bool omitHiddenShapes)
0433 {
0434     d->updateTree();
0435     QList<KoShape*> sortedShapes(d->tree.contains(position));
0436     std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex);
0437     KoShape *firstUnselectedShape = 0;
0438     for (int count = sortedShapes.count() - 1; count >= 0; count--) {
0439         KoShape *shape = sortedShapes.at(count);
0440         if (omitHiddenShapes && ! shape->isVisible(true))
0441             continue;
0442         if (! shape->hitTest(position))
0443             continue;
0444 
0445         switch (selection) {
0446         case KoFlake::ShapeOnTop:
0447             if (shape->isSelectable())
0448                 return shape;
0449         case KoFlake::Selected:
0450             if (d->selection->isSelected(shape))
0451                 return shape;
0452             break;
0453         case KoFlake::Unselected:
0454             if (! d->selection->isSelected(shape))
0455                 return shape;
0456             break;
0457         case KoFlake::NextUnselected:
0458             // we want an unselected shape
0459             if (d->selection->isSelected(shape))
0460                 continue;
0461             // memorize the first unselected shape
0462             if (! firstUnselectedShape)
0463                 firstUnselectedShape = shape;
0464             // check if the shape above is selected
0465             if (count + 1 < sortedShapes.count() && d->selection->isSelected(sortedShapes.at(count + 1)))
0466                 return shape;
0467             break;
0468         }
0469     }
0470     // if we want the next unselected below a selected but there was none selected,
0471     // return the first found unselected shape
0472     if (selection == KoFlake::NextUnselected && firstUnselectedShape)
0473         return firstUnselectedShape;
0474 
0475     if (d->selection->hitTest(position))
0476         return d->selection;
0477 
0478     return 0; // missed everything
0479 }
0480 
0481 QList<KoShape *> KoShapeManager::shapesAt(const QRectF &rect, bool omitHiddenShapes)
0482 { 
0483     d->updateTree();
0484     QList<KoShape*> intersectedShapes(d->tree.intersects(rect));
0485     
0486     for (int count = intersectedShapes.count() - 1; count >= 0; count--) {
0487      
0488         KoShape *shape = intersectedShapes.at(count);
0489        
0490         if (omitHiddenShapes && ! shape->isVisible(true)) {
0491             intersectedShapes.removeAt(count);
0492         } else {
0493             const QPainterPath outline = shape->absoluteTransformation(0).map(shape->outline());
0494             if (! outline.intersects(rect) && ! outline.contains(rect)) { 
0495                 intersectedShapes.removeAt(count);
0496             }
0497         }
0498     }
0499     return intersectedShapes;
0500 }
0501 
0502 void KoShapeManager::update(QRectF &rect, const KoShape *shape, bool selectionHandles)
0503 {
0504     d->canvas->updateCanvas(rect);
0505     if (selectionHandles && d->selection->isSelected(shape)) {
0506         if (d->canvas->toolProxy())
0507             d->canvas->toolProxy()->repaintDecorations();
0508     }
0509 }
0510 
0511 void KoShapeManager::notifyShapeChanged(KoShape *shape)
0512 {
0513     Q_ASSERT(shape);
0514     if (d->aggregate4update.contains(shape) || d->additionalShapes.contains(shape)) {
0515         return;
0516     }
0517     const bool wasEmpty = d->aggregate4update.isEmpty();
0518     d->aggregate4update.insert(shape);
0519     d->shapeIndexesBeforeUpdate.insert(shape, shape->zIndex());
0520 
0521     KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
0522     if (container) {
0523         foreach(KoShape *child, container->shapes())
0524             notifyShapeChanged(child);
0525     }
0526     if (wasEmpty && !d->aggregate4update.isEmpty())
0527         QTimer::singleShot(100, this, SLOT(updateTree()));
0528     emit shapeChanged(shape);
0529 }
0530 
0531 QList<KoShape*> KoShapeManager::shapes() const
0532 {
0533     return d->shapes;
0534 }
0535 
0536 QList<KoShape*> KoShapeManager::topLevelShapes() const
0537 {
0538     QList<KoShape*> shapes;
0539     // get all toplevel shapes
0540     foreach(KoShape *shape, d->shapes) {
0541         if (shape->parent() == 0) {
0542             shapes.append(shape);
0543         }
0544     }
0545     return shapes;
0546 }
0547 
0548 KoSelection *KoShapeManager::selection() const
0549 {
0550     return d->selection;
0551 }
0552 
0553 void KoShapeManager::suggestChangeTool(KoPointerEvent *event)
0554 {
0555     QList<KoShape*> shapes;
0556 
0557     KoShape *clicked = shapeAt(event->point);
0558     if (clicked) {
0559         if (! selection()->isSelected(clicked)) {
0560             selection()->deselectAll();
0561             selection()->select(clicked);
0562         }
0563         shapes.append(clicked);
0564     }
0565 
0566     QList<KoShape*> shapes2;
0567     foreach (KoShape *shape, shapes) {
0568         QSet<KoShape*> delegates = shape->toolDelegates();
0569         if (delegates.isEmpty()) {
0570             shapes2.append(shape);
0571         } else {
0572             foreach (KoShape *delegatedShape, delegates) {
0573                 shapes2.append(delegatedShape);
0574             }
0575         }
0576     }
0577     KoToolManager::instance()->switchToolRequested(
0578         KoToolManager::instance()->preferredToolForSelection(shapes2));
0579 }
0580 
0581 void KoShapeManager::setPaintingStrategy(KoShapeManagerPaintingStrategy *strategy)
0582 {
0583     delete d->strategy;
0584     d->strategy = strategy;
0585 }
0586 
0587 KoCanvasBase *KoShapeManager::canvas()
0588 {
0589     return d->canvas;
0590 }
0591 
0592 //have to include this because of Q_PRIVATE_SLOT
0593 #include "moc_KoShapeManager.cpp"