00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "KoShapeManager.h"
00024 #include "KoSelection.h"
00025 #include "KoToolManager.h"
00026 #include "KoPointerEvent.h"
00027 #include "KoShape.h"
00028 #include "KoCanvasBase.h"
00029 #include "KoShapeContainer.h"
00030 #include "KoShapeBorderModel.h"
00031 #include "KoShapeGroup.h"
00032 #include "KoToolProxy.h"
00033 #include "KoShapeManagerPaintingStrategy.h"
00034 #include "KoShapeShadow.h"
00035 #include "KoShapeLayer.h"
00036 #include "KoFilterEffect.h"
00037 #include "KoFilterEffectStack.h"
00038 #include "KoFilterEffectRenderContext.h"
00039
00040 #include <KoRTree.h>
00041
00042 #include <QPainter>
00043 #include <kdebug.h>
00044
00045 class KoShapeManager::Private
00046 {
00047 public:
00048 Private(KoShapeManager *shapeManager, KoCanvasBase *c)
00049 : selection(new KoSelection()),
00050 canvas(c),
00051 tree(4, 2),
00052 strategy(new KoShapeManagerPaintingStrategy(shapeManager)),
00053 q(shapeManager)
00054 {
00055 }
00056
00057 ~Private() {
00058 delete selection;
00059 delete strategy;
00060 }
00061
00066 void updateTree();
00067
00068 class DetectCollision
00069 {
00070 public:
00071 DetectCollision() {}
00072 void detect(KoRTree<KoShape *> &tree, KoShape *s, int prevZIndex) {
00073 foreach(KoShape *shape, tree.intersects(s->boundingRect())) {
00074 bool isChild = false;
00075 KoShapeContainer *parent = s->parent();
00076 while (parent && !isChild) {
00077 if (parent == shape)
00078 isChild = true;
00079 parent = parent->parent();
00080 }
00081 if (isChild)
00082 continue;
00083 if (s->zIndex() <= shape->zIndex() && prevZIndex <= shape->zIndex())
00084
00085 continue;
00086 if (shape->collisionDetection() && !shapesWithCollisionDetection.contains(shape))
00087 shapesWithCollisionDetection.append(shape);
00088 }
00089 }
00090
00091 void fireSignals() {
00092 foreach(KoShape *shape, shapesWithCollisionDetection)
00093 shape->shapeChanged(KoShape::CollisionDetected);
00094 }
00095
00096 private:
00097 QList<KoShape*> shapesWithCollisionDetection;
00098 };
00099
00100 QList<KoShape *> shapes;
00101 QList<KoShape *> additionalShapes;
00102 KoSelection *selection;
00103 KoCanvasBase *canvas;
00104 KoRTree<KoShape *> tree;
00105 QSet<KoShape *> aggregate4update;
00106 QHash<KoShape*, int> shapeIndexesBeforeUpdate;
00107 KoShapeManagerPaintingStrategy *strategy;
00108 KoShapeManager *q;
00109 };
00110
00111 void KoShapeManager::Private::updateTree()
00112 {
00113
00114 DetectCollision detector;
00115 bool selectionModified = false;
00116 foreach(KoShape *shape, aggregate4update) {
00117 if (shapeIndexesBeforeUpdate.contains(shape))
00118 detector.detect(tree, shape, shapeIndexesBeforeUpdate[shape]);
00119 selectionModified = selectionModified || selection->isSelected(shape);
00120 }
00121
00122 foreach(KoShape * shape, aggregate4update) {
00123 tree.remove(shape);
00124 QRectF br(shape->boundingRect());
00125 strategy->adapt(shape, br);
00126 tree.insert(br, shape);
00127 }
00128
00129
00130 foreach(KoShape *shape, aggregate4update)
00131 detector.detect(tree, shape, shapeIndexesBeforeUpdate[shape]);
00132 aggregate4update.clear();
00133 shapeIndexesBeforeUpdate.clear();
00134
00135 detector.fireSignals();
00136 if (selectionModified) {
00137 selection->updateSizeAndPosition();
00138 emit q->selectionContentChanged();
00139 }
00140 }
00141
00142 KoShapeManager::KoShapeManager(KoCanvasBase *canvas, const QList<KoShape *> &shapes)
00143 : d(new Private(this, canvas))
00144 {
00145 Q_ASSERT(d->canvas);
00146 connect(d->selection, SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged()));
00147 setShapes(shapes);
00148 }
00149
00150 KoShapeManager::KoShapeManager(KoCanvasBase *canvas)
00151 : d(new Private(this, canvas))
00152 {
00153 Q_ASSERT(d->canvas);
00154 connect(d->selection, SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged()));
00155 }
00156
00157 KoShapeManager::~KoShapeManager()
00158 {
00159 foreach(KoShape *shape, d->shapes) {
00160 shape->removeShapeManager(this);
00161 }
00162 foreach(KoShape *shape, d->additionalShapes) {
00163 shape->removeShapeManager(this);
00164 }
00165 delete d;
00166 }
00167
00168
00169 void KoShapeManager::setShapes(const QList<KoShape *> &shapes, Repaint repaint)
00170 {
00171
00172 d->selection->deselectAll();
00173 foreach(KoShape *shape, d->shapes) {
00174 shape->removeShapeManager(this);
00175 }
00176 d->aggregate4update.clear();
00177 d->tree.clear();
00178 d->shapes.clear();
00179 foreach(KoShape *shape, shapes) {
00180 add(shape, repaint);
00181 }
00182 }
00183
00184 void KoShapeManager::add(KoShape *shape, Repaint repaint)
00185 {
00186 if (d->shapes.contains(shape))
00187 return;
00188 shape->addShapeManager(this);
00189 d->shapes.append(shape);
00190 if (! dynamic_cast<KoShapeGroup*>(shape) && ! dynamic_cast<KoShapeLayer*>(shape)) {
00191 QRectF br(shape->boundingRect());
00192 d->tree.insert(br, shape);
00193 }
00194 if (repaint == PaintShapeOnAdd) {
00195 shape->update();
00196 }
00197
00198
00199 KoShapeContainer* container = dynamic_cast<KoShapeContainer*>(shape);
00200
00201 if (container) {
00202 foreach(KoShape* containerShape, container->childShapes()) {
00203 add(containerShape, repaint);
00204 }
00205 }
00206
00207 Private::DetectCollision detector;
00208 detector.detect(d->tree, shape, shape->zIndex());
00209 detector.fireSignals();
00210 }
00211
00212 void KoShapeManager::addAdditional(KoShape *shape)
00213 {
00214 if ( shape ) {
00215 if (d->additionalShapes.contains(shape)) {
00216 return;
00217 }
00218 shape->addShapeManager(this);
00219 d->additionalShapes.append(shape);
00220 }
00221 }
00222
00223 void KoShapeManager::remove(KoShape *shape)
00224 {
00225 Private::DetectCollision detector;
00226 detector.detect(d->tree, shape, shape->zIndex());
00227 detector.fireSignals();
00228
00229 shape->update();
00230 shape->removeShapeManager(this);
00231 d->selection->deselect(shape);
00232 d->aggregate4update.remove(shape);
00233 d->tree.remove(shape);
00234 d->shapes.removeAll(shape);
00235
00236
00237 KoShapeContainer* container = dynamic_cast<KoShapeContainer*>(shape);
00238
00239 if (container) {
00240 foreach(KoShape* containerShape, container->childShapes()) {
00241 remove(containerShape);
00242 }
00243 }
00244 }
00245
00246 void KoShapeManager::removeAdditional(KoShape *shape)
00247 {
00248 if ( shape ) {
00249 shape->removeShapeManager(this);
00250 d->additionalShapes.removeAll(shape);
00251 }
00252 }
00253
00254 void KoShapeManager::paint(QPainter &painter, const KoViewConverter &converter, bool forPrint)
00255 {
00256 d->updateTree();
00257 painter.setPen(Qt::NoPen);
00258 painter.setBrush(Qt::NoBrush);
00259
00260 QList<KoShape*> unsortedShapes;
00261 if (painter.hasClipping()) {
00262 QRectF rect = converter.viewToDocument(painter.clipRegion().boundingRect());
00263 unsortedShapes = d->tree.intersects(rect);
00264 } else {
00265 unsortedShapes = shapes();
00266 kWarning() << "KoShapeManager::paint Painting with a painter that has no clipping will lead to too much being painted!";
00267 }
00268
00269
00270 QList<KoShape*> sortedShapes;
00271 foreach(KoShape * shape, unsortedShapes) {
00272 if (shape->isVisible(true))
00273 sortedShapes.append(shape);
00274 }
00275
00276 qSort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex);
00277
00278 foreach(KoShape * shape, sortedShapes) {
00279 if (shape->parent() != 0 && shape->parent()->childClipped(shape))
00280 continue;
00281
00282 d->strategy->paint(shape, painter, converter, forPrint);
00283 }
00284
00285 #ifdef KOFFICE_RTREE_DEBUG
00286
00287 qreal zx = 0;
00288 qreal zy = 0;
00289 converter.zoom(&zx, &zy);
00290 painter.save();
00291 painter.scale(zx, zy);
00292 d->tree.paint(painter);
00293 painter.restore();
00294 #endif
00295
00296 if (! forPrint)
00297 d->selection->paint(painter, converter);
00298 }
00299
00300 void KoShapeManager::paintShape(KoShape * shape, QPainter &painter, const KoViewConverter &converter, bool forPrint)
00301 {
00302 if (shape->shadow()) {
00303 painter.save();
00304 shape->shadow()->paint(shape, painter, converter);
00305 painter.restore();
00306 }
00307 if(!shape->filterEffectStack() || shape->filterEffectStack()->filterEffects().isEmpty()) {
00308 painter.save();
00309 shape->paint(painter, converter);
00310 painter.restore();
00311 if (shape->border()) {
00312 painter.save();
00313 shape->border()->paintBorder(shape, painter, converter);
00314 painter.restore();
00315 }
00316 } else {
00317
00318 QRectF shapeBound(QPointF(), shape->size());
00319
00320 QRectF clipRegion = shape->filterEffectStack()->clipRectForBoundingRect(shapeBound);
00321
00322 QRectF zoomedClipRegion = converter.documentToView(clipRegion);
00323
00324 QPointF clippingOffset = zoomedClipRegion.topLeft();
00325
00326
00327 QImage sourceGraphic(zoomedClipRegion.size().toSize(), QImage::Format_ARGB32_Premultiplied);
00328 sourceGraphic.fill(qRgba(0,0,0,0));
00329
00330
00331 QPainter imagePainter(&sourceGraphic);
00332 imagePainter.translate(-1.0f*clippingOffset);
00333 imagePainter.setPen(Qt::NoPen);
00334 imagePainter.setBrush(Qt::NoBrush);
00335 imagePainter.setRenderHint(QPainter::Antialiasing, painter.testRenderHint(QPainter::Antialiasing));
00336
00337
00338 imagePainter.save();
00339 shape->paint(imagePainter, converter);
00340 imagePainter.restore();
00341 if (shape->border()) {
00342 imagePainter.save();
00343 shape->border()->paintBorder(shape, imagePainter, converter);
00344 imagePainter.restore();
00345 }
00346 imagePainter.end();
00347
00348 QImage sourceAlpha = sourceGraphic;
00349 sourceAlpha.fill(qRgba(0,0,0,255));
00350 sourceAlpha.setAlphaChannel(sourceGraphic.alphaChannel());
00351
00352 QHash<QString, QImage> imageBuffers;
00353 imageBuffers.insert("SourceGraphic", sourceGraphic);
00354 imageBuffers.insert(QString(), sourceGraphic);
00355 imageBuffers.insert("SourceAlpha", sourceAlpha);
00356
00357 QMatrix coordTransform = QMatrix().scale(shapeBound.width(), shapeBound.height());
00358 KoFilterEffectRenderContext renderContext(converter);
00359 renderContext.setCoordinateTransformation(coordTransform);
00360
00361 QImage result;
00362 QList<KoFilterEffect*> filterEffects = shape->filterEffectStack()->filterEffects();
00363
00364 foreach(KoFilterEffect* filterEffect, filterEffects) {
00365 QRectF filterRegion = filterEffect->filterRectForBoundingRect(clipRegion);
00366 filterRegion = converter.documentToView(filterRegion);
00367 QRect subRegion = filterRegion.translated(-clippingOffset).toRect();
00368
00369 renderContext.setFilterRegion(subRegion & sourceGraphic.rect());
00370
00371 if (filterEffect->maximalInputCount() == 1) {
00372 QList<QString> inputs = filterEffect->inputs();
00373 QString input = inputs.count() ? inputs.first() : QString();
00374
00375 QImage image = imageBuffers.value(input);
00376 if (!image.isNull()) {
00377 result = filterEffect->processImage(imageBuffers.value(input), renderContext);
00378 }
00379 } else {
00380 QList<QImage> inputImages;
00381 foreach(const QString &input, filterEffect->inputs()) {
00382 QImage image = imageBuffers.value(input);
00383 if (!image.isNull())
00384 inputImages.append(imageBuffers.value(input));
00385 }
00386
00387 if (filterEffect->inputs().count() == inputImages.count())
00388 result = filterEffect->processImages(inputImages, renderContext);
00389 }
00390
00391 imageBuffers.insert(filterEffect->output(), result);
00392 }
00393
00394 KoFilterEffect * lastEffect = filterEffects.last();
00395
00396
00397 painter.save();
00398 painter.drawImage(clippingOffset, imageBuffers.value(lastEffect->output()));
00399 painter.restore();
00400 }
00401 if (! forPrint) {
00402 painter.save();
00403 painter.setRenderHint(QPainter::Antialiasing, false);
00404 shape->paintDecorations(painter, converter, d->canvas);
00405 painter.restore();
00406 }
00407 }
00408
00409
00410 KoShape * KoShapeManager::shapeAt(const QPointF &position, KoFlake::ShapeSelection selection, bool omitHiddenShapes)
00411 {
00412 d->updateTree();
00413 QList<KoShape*> sortedShapes(d->tree.contains(position));
00414 qSort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex);
00415 KoShape *firstUnselectedShape = 0;
00416 for (int count = sortedShapes.count() - 1; count >= 0; count--) {
00417 KoShape *shape = sortedShapes.at(count);
00418 if (omitHiddenShapes && ! shape->isVisible(true))
00419 continue;
00420 if (! shape->hitTest(position))
00421 continue;
00422
00423 switch (selection) {
00424 case KoFlake::ShapeOnTop:
00425 if (shape->isSelectable())
00426 return shape;
00427 case KoFlake::Selected:
00428 if (d->selection->isSelected(shape))
00429 return shape;
00430 break;
00431 case KoFlake::Unselected:
00432 if (! d->selection->isSelected(shape))
00433 return shape;
00434 break;
00435 case KoFlake::NextUnselected:
00436
00437 if (d->selection->isSelected(shape))
00438 continue;
00439
00440 if (! firstUnselectedShape)
00441 firstUnselectedShape = shape;
00442
00443 if (count + 1 < sortedShapes.count() && d->selection->isSelected(sortedShapes.at(count + 1)))
00444 return shape;
00445 break;
00446 }
00447 }
00448
00449
00450 if (selection == KoFlake::NextUnselected && firstUnselectedShape)
00451 return firstUnselectedShape;
00452
00453 if (d->selection->hitTest(position))
00454 return d->selection;
00455
00456 return 0;
00457 }
00458
00459 QList<KoShape *> KoShapeManager::shapesAt(const QRectF &rect, bool omitHiddenShapes)
00460 {
00461 d->updateTree();
00462
00463 QList<KoShape*> intersectedShapes(d->tree.intersects(rect));
00464 for (int count = intersectedShapes.count() - 1; count >= 0; count--) {
00465 KoShape *shape = intersectedShapes.at(count);
00466 if ( omitHiddenShapes && ! shape->isVisible(true)) {
00467 intersectedShapes.removeAt(count);
00468 }
00469 else {
00470 const QPainterPath outline = shape->absoluteTransformation(0).map(shape->outline());
00471 if( ! outline.intersects( rect ) && ! outline.contains( rect ) ) {
00472 intersectedShapes.removeAt(count);
00473 }
00474 }
00475 }
00476 return intersectedShapes;
00477 }
00478
00479 void KoShapeManager::update(QRectF &rect, const KoShape *shape, bool selectionHandles)
00480 {
00481 d->canvas->updateCanvas(rect);
00482 if (selectionHandles && d->selection->isSelected(shape)) {
00483 if (d->canvas->toolProxy())
00484 d->canvas->toolProxy()->repaintDecorations();
00485 }
00486 }
00487
00488 void KoShapeManager::notifyShapeChanged(KoShape * shape)
00489 {
00490 Q_ASSERT(shape);
00491 if (d->aggregate4update.contains(shape) || d->additionalShapes.contains(shape)) {
00492 return;
00493 }
00494 d->aggregate4update.insert(shape);
00495 d->shapeIndexesBeforeUpdate.insert(shape, shape->zIndex());
00496
00497 KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
00498 if (container) {
00499 foreach(KoShape *child, container->childShapes())
00500 d->aggregate4update.insert(child);
00501 }
00502 }
00503
00504 QList<KoShape*> KoShapeManager::shapes() const
00505 {
00506 return d->shapes;
00507 }
00508
00509 QList<KoShape*> KoShapeManager::topLevelShapes() const
00510 {
00511 QList<KoShape*> shapes;
00512
00513 foreach(KoShape *shape, d->shapes) {
00514 if (shape->parent() == 0) {
00515 shapes.append(shape);
00516 }
00517 }
00518 return shapes;
00519 }
00520
00521 KoSelection * KoShapeManager::selection() const
00522 {
00523 return d->selection;
00524 }
00525
00526 void KoShapeManager::suggestChangeTool(KoPointerEvent *event)
00527 {
00528 QList<KoShape*> shapes;
00529
00530 KoShape *clicked = shapeAt(event->point);
00531 if (clicked) {
00532 if (! selection()->isSelected(clicked)) {
00533 selection()->deselectAll();
00534 selection()->select(clicked);
00535 }
00536 shapes.append(clicked);
00537 }
00538 KoToolManager::instance()->switchToolRequested(
00539 KoToolManager::instance()->preferredToolForSelection(shapes));
00540 }
00541
00542 void KoShapeManager::setPaintingStrategy(KoShapeManagerPaintingStrategy * strategy)
00543 {
00544 delete d->strategy;
00545 d->strategy = strategy;
00546 }
00547
00548 #include "KoShapeManager.moc"