Commit 467c4429 authored by Thorsten Zachmann's avatar Thorsten Zachmann

o Add a KoRTree into the KoShapeManager for fast finding of the objects.

svn path=/trunk/koffice/; revision=556067
parent bdd754f4
......@@ -9,7 +9,6 @@ set(flake_SRCS
KoLineBorder.cpp
KoPathShape.cpp
KoRectangleShape.cpp
KoRepaintManager.cpp
KoSelection.cpp
KoShape.cpp
KoShapeContainer.cpp
......
/* This file is part of the KDE project
* Copyright (C) 2006 Thomas Zander <zander@kde.org>
*
* 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 "KoRepaintManager.h"
#include "KoCanvasBase.h"
#include "KoShape.h"
#include "KoSelection.h"
#include "KoTool.h"
#include <QRectF>
KoRepaintManager::KoRepaintManager(KoCanvasBase *canvas, KoSelection *selection)
: m_canvas (canvas)
, m_selection(selection)
, m_active (true)
, m_locked(false)
, m_otherManagers()
, m_refCount(0)
{
}
void KoRepaintManager::addChainedManager(KoRepaintManager *manager) {
if(manager == this)
return;
if(! m_otherManagers.contains(manager)) {
m_otherManagers.append(manager);
manager->addUser();
}
}
void KoRepaintManager::dismantle() {
m_canvas = 0;
m_active = false;
}
void KoRepaintManager::repaint(QRectF &rect, const KoShape *shape, bool selectionHandles) {
if(!m_active || m_locked) // lock out cyclic links
return;
m_canvas->updateCanvas(rect);
if(selectionHandles && m_selection->isSelected(shape)) {
if(m_canvas->tool())
m_canvas->tool()->repaintDecorations();
}
m_locked = true;
foreach(KoRepaintManager *manager, m_otherManagers) {
if(! manager->isActive()) {
manager->removeUser();
m_otherManagers.removeAll(manager);
if(manager->refCount() == 0)
delete manager;
}
else
manager->repaint(rect, shape, selectionHandles);
}
m_locked = false;
}
/* This file is part of the KDE project
* Copyright (C) 2006 Thomas Zander <zander@kde.org>
*
* 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.
*/
#ifndef KOREPAINTMANAGER_H
#define KOREPAINTMANAGER_H
#include <QList>
class KoCanvasBase;
class KoSelection;
class KoShape;
class QRectF;
/**
* This class gets repaint requests and will sent it to all registered canvas objects.
* Each KoShape has a link to a repaint manager so any repaint requests on the shape
* will be forwarded here.
* There will be one repaintManager per canvas so in order to repaint the shape that may
* be visible on more then one canvas there is a concept of chained repaintManagers.
* The KoRepaintManager is refcounted and deletes itself.
*/
class KoRepaintManager {
public:
/**
* Constructor.
* @param canvas the canvas this repaintManager is going to forward any requests to.
* @param selection the selection object that manages the selections for the canvas
*/
KoRepaintManager(KoCanvasBase *canvas, KoSelection *selection);
/**
* attach another repaint manager to this one so repaints from our children will
* have effect on the other managers canvas as well.
* @param manager the other manager.
*/
void addChainedManager(KoRepaintManager *manager);
/**
* Since this object is refcounted you do not delete it but you dismantle it.
*/
void dismantle(); // to deactivate this repaintManager for good.
/**
* For reference counting purposes, add a user to register that user has a reference.
*/
void addUser() { m_refCount++; }
/**
* For reference counting purposes, remove a user to register that user no
* longer has a reference.
*/
void removeUser() { m_refCount--; }
/**
* return the amount of references.
* @return the amount of references.
*/
int refCount() { return m_refCount; }
/**
* Request a repaint to be queued.
* The repaint will be restricted to the parameters rectangle, which is expected to be
* in points (the internal coordinates system of KoShape) and it is expected to be
* normalized and based in the global coordinates, not any local coordinates.
* <p>This method will return immediately and only request a repaint. Successive calls
* will be merged into an appropriate repaint action.
* @param rect the rectangle (in pt) to queue for repaint.
* @param shape the shape that is going to be redrawn; only needed when selectionHandles=true
* @param selectionHandles if true; find out if the shape is selected and repaint its
* selection handles at the same time.
*/
void repaint(QRectF &rect, const KoShape *shape = 0, bool selectionHandles=false);
/**
* Return if this manager is still actively supporting clients for a canvas.
*/
bool isActive() { return m_active; }
private:
KoCanvasBase *m_canvas;
KoSelection *m_selection;
bool m_active, m_locked;
QList<KoRepaintManager*> m_otherManagers;
int m_refCount;
};
#endif
......@@ -84,6 +84,9 @@ public:
virtual QRectF boundingRect() const;
protected:
virtual void updateTree() {}
signals:
/// emitted when the selection is changed
void selectionChanged();
......
......@@ -21,11 +21,11 @@
#include "KoShape.h"
#include "KoShapeContainer.h"
#include "KoRepaintManager.h"
#include "KoSelection.h"
#include "KoPointerEvent.h"
#include "KoInsets.h"
#include "KoShapeBorderModel.h"
#include "KoShapeManager.h"
#include "KoShapeUserData.h"
#include <QPainter>
......@@ -48,7 +48,6 @@ KoShape::KoShape()
, m_visible( true )
, m_locked( false )
, m_keepAspect( false )
, m_repaintManager(0)
, m_userData(0)
{
recalcMatrix();
......@@ -151,6 +150,7 @@ void KoShape::recalcMatrix()
{
m_matrix = transformationMatrix(0);
m_invMatrix = m_matrix.inverted();
updateTree();
}
QMatrix KoShape::transformationMatrix(KoViewConverter *converter) const {
......@@ -210,36 +210,32 @@ int KoShape::zIndex() const {
return m_zIndex;
}
void KoShape::setRepaintManager(KoRepaintManager *manager) {
Q_ASSERT(manager);
if(m_repaintManager) {
// swap repaint Manager (Since that will release old ones easier)
m_repaintManager->removeUser();
manager->addChainedManager(m_repaintManager);
}
m_repaintManager = manager;
manager->addUser();
}
void KoShape::repaint() const {
if(m_repaintManager == 0)
return;
QRectF rect(QPointF(0, 0), m_size);
if(m_border) {
KoInsets *insets = new KoInsets(0, 0, 0, 0);
m_border->borderInsets(this, *insets);
rect.adjust(-insets->left, -insets->top, insets->right, insets->bottom);
delete insets;
if ( !m_shapeManagers.empty() )
{
foreach( KoShapeManager * manager, m_shapeManagers )
{
QRectF rect(QPointF(0, 0), m_size);
if(m_border) {
KoInsets insets(0, 0, 0, 0);
m_border->borderInsets(this, insets);
rect.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
}
rect = m_matrix.mapRect(rect);
manager->repaint( rect, this, true );
}
}
rect = m_matrix.mapRect(rect);
m_repaintManager->repaint(rect, this, true);
}
void KoShape::repaint(QRectF &shape) const {
if(m_repaintManager == 0 || !isVisible())
return;
QRectF rect(m_matrix.mapRect(shape));
m_repaintManager->repaint(rect);
if ( !m_shapeManagers.empty() && isVisible() )
{
foreach( KoShapeManager * manager, m_shapeManagers )
{
QRectF rect(m_matrix.mapRect(shape));
manager->repaint(rect);
}
}
}
void KoShape::repaint(double x, double y, double width, double height) const {
......@@ -312,6 +308,14 @@ void KoShape::moveBy(double distanceX, double distanceY) {
setAbsolutePosition(QPointF(p.x() + distanceX, p.y() + distanceY));
}
void KoShape::updateTree()
{
foreach( KoShapeManager * manager, m_shapeManagers )
{
manager->updateTree( this );
}
}
void KoShape::setUserData(KoShapeUserData *userData) {
if(m_userData)
delete m_userData;
......@@ -334,3 +338,4 @@ void KoShape::applyConversion(QPainter &painter, const KoViewConverter &converte
converter.zoom(&zoomX, &zoomY);
painter.scale(zoomX, zoomY);
}
......@@ -24,6 +24,7 @@
#include <QMatrix>
#include <QVector>
#include <QSet>
#include <QBrush>
#include "KoViewConverter.h"
......@@ -37,8 +38,8 @@ class QVariant;
class KoSelection;
class KoPointerEvent;
class KoShapeContainer;
class KoRepaintManager;
class KoShapeBorderModel;
class KoShapeManager;
class KoShapeUserData;
/**
......@@ -486,6 +487,11 @@ protected:
*/
virtual void copySettings(const KoShape *shape);
/**
* Update the position of the shape in the tree of the KoShapeManager.
*/
virtual void updateTree();
/// Used by shapeChanged() to select which change was made
enum ChangeType {
PositionChanged, ///< used after a setPosition()
......@@ -523,11 +529,13 @@ private:
bool m_visible, m_locked, m_keepAspect;
KoRepaintManager *m_repaintManager;
QSet<KoShapeManager *> m_shapeManagers;
private:
friend class KoShapeManager;
void setRepaintManager(KoRepaintManager *manager);
void addShapeManager( KoShapeManager * manager ) { m_shapeManagers.insert( manager ); }
void removeShapeManager( KoShapeManager * manager ) { m_shapeManagers.remove( manager ); }
KoShapeUserData *m_userData;
};
......
......@@ -23,62 +23,85 @@
#include "KoSelection.h"
#include "KoShape.h"
#include "KoCanvasBase.h"
#include "KoRepaintManager.h"
#include "KoShapeContainer.h"
#include "KoShapeBorderModel.h"
#include "KoRepaintManager.moc"
#include "KoShapeGroup.h"
#include "KoTool.h"
#include <QPainter>
KoShapeManager::KoShapeManager( KoCanvasBase *canvas, const QList<KoShape *> &shapes )
: m_selection( new KoSelection() )
, m_canvas( canvas )
, m_tree( 4, 2 )
{
connect( m_selection, SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged()) );
m_repaintManager = new KoRepaintManager(canvas, m_selection);
setShapes(shapes);
m_selection->setRepaintManager(m_repaintManager);
m_selection->addShapeManager( this );
}
KoShapeManager::KoShapeManager(KoCanvasBase *canvas)
: m_shapes()
, m_selection( new KoSelection() )
, m_canvas( canvas )
, m_tree( 4, 2 )
{
connect( m_selection, SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged()) );
m_repaintManager = new KoRepaintManager(canvas, m_selection);
m_selection->setRepaintManager(m_repaintManager);
m_selection->addShapeManager( this );
}
KoShapeManager::~KoShapeManager()
{
foreach(KoShape *shape, m_shapes)
shape->removeShapeManager( this );
delete m_selection;
m_repaintManager->dismantle();
}
void KoShapeManager::setShapes( const QList<KoShape *> &shapes )
{
foreach(KoShape *shape, m_shapes)
{
m_aggregate4update.remove( shape );
m_tree.remove( shape );
shape->removeShapeManager( this );
}
m_shapes = shapes;
foreach(KoShape *shape, m_shapes)
shape->setRepaintManager(m_repaintManager);
{
add( shape );
}
}
void KoShapeManager::add(KoShape *shape) {
shape->setRepaintManager(m_repaintManager);
void KoShapeManager::add( KoShape *shape )
{
shape->addShapeManager( this );
m_shapes.append(shape);
if( ! dynamic_cast<KoShapeGroup*>( shape ))
{
QRectF br( shape->boundingRect() );
m_tree.insert( shape, br );
}
}
void KoShapeManager::remove(KoShape *shape) {
void KoShapeManager::remove( KoShape *shape )
{
shape->removeShapeManager( this );
m_aggregate4update.remove( shape );
m_tree.remove( shape );
m_shapes.removeAll(shape);
}
void KoShapeManager::paint( QPainter &painter, KoViewConverter &converter, bool forPrint)
{
updateTree();
QPen pen(Qt::NoPen); // painters by default have a black stroke, lets turn that off.
painter.setPen(pen);
QList<KoShape*> sorterdShapes(m_shapes);
QList<KoShape*> sorterdShapes( m_tree.intersects( converter.viewToDocument( painter.clipRegion().boundingRect() ) ) );
qSort(sorterdShapes.begin(), sorterdShapes.end(), KoShape::compareShapeZIndex);
const QRegion clipRegion = painter.clipRegion();
foreach ( KoShape * shape, sorterdShapes ) {
if(! shape->isVisible())
continue;
......@@ -112,13 +135,25 @@ void KoShapeManager::paint( QPainter &painter, KoViewConverter &converter, bool
painter.restore(); // for the matrix
}
#if 0
// paint tree
double zx = 0;
double zy = 0;
converter.zoom( &zx, &zy );
painter.save();
painter.scale( zx, zy );
m_tree.paint( painter );
painter.restore();
#endif
if(! forPrint)
m_selection->paint( painter, converter );
}
KoShape * KoShapeManager::shapeAt( const QPointF &position )
{
QList<KoShape*> sorterdShapes(m_shapes);
updateTree();
QList<KoShape*> sorterdShapes( m_tree.contains( position ) );
qSort(sorterdShapes.begin(), sorterdShapes.end(), KoShape::compareShapeZIndex);
for(int count = sorterdShapes.count()-1; count >= 0; count--) {
if ( sorterdShapes.at(count)->hitTest( position ) )
......@@ -133,4 +168,38 @@ KoShape * KoShapeManager::shapeAt( const QPointF &position )
return 0; // missed everything
}
QList<KoShape *> KoShapeManager::shapesAt( const QRectF &rect )
{
updateTree();
//TODO check if object is really in the rect and not
// only the bounding rect of the object.
return m_tree.intersects( rect );
}
void KoShapeManager::repaint( QRectF &rect, const KoShape *shape, bool selectionHandles )
{
m_canvas->updateCanvas( rect );
if ( selectionHandles && m_selection->isSelected( shape ) )
{
if ( m_canvas->tool() )
m_canvas->tool()->repaintDecorations();
}
}
void KoShapeManager::updateTree( KoShape * shape )
{
m_aggregate4update.insert( shape );
}
void KoShapeManager::updateTree()
{
foreach ( KoShape * shape, m_aggregate4update )
{
m_tree.remove( shape );
QRectF br( shape->boundingRect() );
m_tree.insert( shape, br );
}
m_aggregate4update.clear();
}
#include "KoShapeManager.moc"
......@@ -23,17 +23,19 @@
#include <QList>
#include <QObject>
#include <QSet>
#include <koffice_export.h>
#include <KoRTree.h>
class KoShape;
class KoSelection;
class KoRepaintManager;
class KoViewConverter;
class KoCanvasBase;
class QPainter;
class QPointF;
class QRectF;
/**
* The shape manager hold a list of all shape which are in scope.
......@@ -85,16 +87,52 @@ public:
* Paint all shapes and their selection handles etc.
* @param painter the painter to paint to.
* @param forPrint if true, make sure only actual content is drawn and no decorations.
* @param converter to convert between internal and view coordinates.
* @param converter to convert between document and view coordinates.
*/
virtual void paint( QPainter &painter, KoViewConverter &converter, bool forPrint );
/**
* Returns the shape located at a specific point in the document.
* @param position the position in the normal coordinate system.
* @param position the position in the document coordinate system.
*/
KoShape * shapeAt( const QPointF &position );
/**
* Returns the shapes which intersects the specific rect in the document.
* @param rect the rectangle in the document coordinate system.
*/
QList<KoShape *> shapesAt( const QRectF &rect );
/**
* Request a repaint to be queued.
* The repaint will be restricted to the parameters rectangle, which is expected to be
* in points (the document coordinates system of KoShape) and it is expected to be
* normalized and based in the global coordinates, not any local coordinates.
* <p>This method will return immediately and only request a repaint. Successive calls
* will be merged into an appropriate repaint action.
* @param rect the rectangle (in pt) to queue for repaint.
* @param shape the shape that is going to be redrawn; only needed when selectionHandles=true
* @param selectionHandles if true; find out if the shape is selected and repaint its
* selection handles at the same time.
*/
void repaint( QRectF &rect, const KoShape *shape = 0, bool selectionHandles = false );
/**
* Update the tree for finding the shapes.
* This will remove the shape form the tree and will reinsert it again.
* The update to the tree will be posponed until it is needed so that successive calles
* will be merged into one.
* @param shape the shape to updated its position in the tree.
*/
void updateTree( KoShape * shape );
protected:
/**
* Update the tree when there are shapes in m_aggregate4update. This is done so not all
* updates to the tree are done when they are asked for but when they are needed.
*/
void updateTree();
signals:
/// emitted when the selection is changed
void selectionChanged();
......@@ -102,7 +140,9 @@ signals:
private:
QList<KoShape *> m_shapes;
KoSelection * m_selection;
KoRepaintManager *m_repaintManager;
KoCanvasBase * m_canvas;
KoRTree<KoShape> m_tree;
QSet<KoShape *> m_aggregate4update;
};
#endif
......
......@@ -87,13 +87,10 @@ void KoShapeRubberSelectStrategy::handleMouseMove(const QPointF &point, Qt::Keyb
void KoShapeRubberSelectStrategy::finishInteraction()
{
KoSelection * selection = m_canvas->shapeManager()->selection();
const QList<KoShape *> &shapes = m_canvas->shapeManager()->shapes();
foreach ( KoShape * object, shapes )
QList<KoShape *> shapes( m_canvas->shapeManager()->shapesAt( m_selectRect ) );
foreach ( KoShape * shape, shapes )
{
if ( object->boundingRect().intersects( m_selectRect ) )
{
selection->select( object );
}
selection->select( shape );
}
m_parent->repaintDecorations();
m_canvas->updateCanvas(m_selectRect.normalized());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment