KissCount/lib/libkdchart/src/KDChartModelDataCache_p.h

342 lines
12 KiB
C
Raw Normal View History

2012-01-28 15:54:17 +01:00
/****************************************************************************
** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved.
**
** This file is part of the KD Chart library.
**
** Licensees holding valid commercial KD Chart licenses may use this file in
** accordance with the KD Chart Commercial License Agreement provided with
** the Software.
**
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 and version 3 as published by the
** Free Software Foundation and appearing in the file LICENSE.GPL.txt included.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** Contact info@kdab.com if any conditions of this licensing are not
** clear to you.
**
**********************************************************************/
#ifndef KDCHARTMODELDATACACHE_H
#define KDCHARTMODELDATACACHE_H
#include <limits>
#include <QObject>
#include <QModelIndex>
#include <QVector>
#include "kdchart_export.h"
class QAbstractItemModel;
namespace KDChart
{
namespace ModelDataCachePrivate
{
class KDCHART_EXPORT ModelSignalMapper
{
protected:
ModelSignalMapper() {}
public:
virtual ~ModelSignalMapper() {}
virtual void resetModel() = 0;
virtual void columnsInserted( const QModelIndex&, int, int ) = 0;
virtual void columnsRemoved( const QModelIndex&, int, int ) = 0;
virtual void dataChanged( const QModelIndex&, const QModelIndex& ) = 0;
virtual void layoutChanged() = 0;
virtual void modelReset() = 0;
virtual void rowsInserted( const QModelIndex&, int, int ) = 0;
virtual void rowsRemoved( const QModelIndex&, int, int ) = 0;
};
// this class maps slots to a non-QObject instantiating ModelSignalMapper
class KDCHART_EXPORT ModelSignalMapperConnector : public QObject
{
Q_OBJECT
public:
explicit ModelSignalMapperConnector( ModelSignalMapper& mapper );
~ModelSignalMapperConnector();
void connectSignals( QAbstractItemModel* model );
void disconnectSignals( QAbstractItemModel* model );
protected Q_SLOTS:
void resetModel();
void columnsInserted( const QModelIndex&, int, int );
void columnsRemoved( const QModelIndex&, int, int );
void dataChanged( const QModelIndex&, const QModelIndex& );
void layoutChanged();
void modelReset();
void rowsInserted( const QModelIndex&, int, int );
void rowsRemoved( const QModelIndex&, int, int );
private:
ModelSignalMapper& m_mapper;
};
template< class T>
T nan()
{
return T();
}
template<>
inline double nan< double >()
{
return std::numeric_limits< double >::quiet_NaN();
}
}
template< class T, int ROLE = Qt::DisplayRole >
class ModelDataCache : public ModelDataCachePrivate::ModelSignalMapper
{
public:
ModelDataCache()
: m_model( 0 ),
m_connector( *this )
{
}
virtual ~ModelDataCache()
{
}
T data( const QModelIndex& index ) const
{
if( !index.isValid() || index.parent() != m_rootIndex )
return ModelDataCachePrivate::nan< T >();
if( index.row() >= m_data.count() )
{
qWarning( "KDChart didn't got signal rowsInserted, resetModel or layoutChanged, "
"but an index with a row outside of the known bounds." );
// apparently, data were added behind our back (w/o signals)
const_cast< ModelDataCache< T, ROLE >* >( this )->rowsInserted( m_rootIndex,
m_data.count(),
qMax(m_data.count(), m_model->rowCount( m_rootIndex ) - 1) );
Q_ASSERT( index.row() < m_data.count() );
}
if( index.column() >= m_data.first().count() )
{
qWarning( "KDChart didn't got signal columnsInserted, resetModel or layoutChanged, "
"but an index with a column outside of the known bounds." );
// apparently, data were added behind our back (w/o signals)
const_cast< ModelDataCache< T, ROLE >* >( this )->columnsInserted( m_rootIndex,
m_data.first().count(),
qMax(m_data.first().count(), m_model->columnCount( m_rootIndex ) - 1) );
Q_ASSERT( index.column() < m_data.first().count() );
}
return data( index.row(), index.column() );
}
T data( int row, int column ) const
{
if( row < 0 || column < 0 )
return ModelDataCachePrivate::nan< T >();
Q_ASSERT( row < m_data.count() );
Q_ASSERT( column < m_data.first().count() );
if( isCached( row, column ) )
return m_data.at( row ).at( column );
return fetchFromModel( row, column, ROLE );
}
void setModel( QAbstractItemModel* model )
{
if( m_model != 0 )
m_connector.disconnectSignals( m_model );
m_model = model;
if( m_model != 0 )
m_connector.connectSignals( m_model );
modelReset();
}
QAbstractItemModel* model() const
{
return m_model;
}
void setRootIndex( const QModelIndex& rootIndex )
{
Q_ASSERT( rootIndex.model() == m_model || !rootIndex.isValid() );
m_rootIndex = rootIndex;
modelReset();
}
QModelIndex rootIndex() const
{
return m_rootIndex;
}
protected:
bool isCached( int row, int column ) const
{
return m_cacheValid.at( row ).at( column );
}
T fetchFromModel( int row, int column, int role ) const
{
Q_ASSERT( m_model != 0 );
const QModelIndex index = m_model->index( row, column, m_rootIndex );
const QVariant data = index.data( role );
const T value = data.isNull() ? ModelDataCachePrivate::nan< T >()
: qVariantValue< T >( data );
m_data[ row ][ column ] = value;
m_cacheValid[ row ][ column ] = true;
return value;
}
protected:
void columnsInserted( const QModelIndex& parent, int start, int end )
{
Q_ASSERT( m_model != 0 );
if( parent != m_rootIndex )
return;
Q_ASSERT( start <= end );
const int rowCount = m_data.count();
for( int row = 0; row < rowCount; ++row )
{
m_data[ row ].insert( start, end - start + 1, T() );
m_cacheValid[ row ].insert( start, end - start + 1, false );
Q_ASSERT( m_data.at( row ).count() == m_model->columnCount( m_rootIndex ) );
Q_ASSERT( m_cacheValid.at( row ).count() == m_model->columnCount( m_rootIndex ) );
}
}
void columnsRemoved( const QModelIndex& parent, int start, int end )
{
Q_ASSERT( m_model != 0 );
if( parent != m_rootIndex )
return;
Q_ASSERT( start <= end );
const int rowCount = m_data.count();
for( int row = 0; row < rowCount; ++row )
{
m_data[ row ].remove( start, end - start + 1 );
m_cacheValid[ row ].remove( start, end - start + 1 );
Q_ASSERT( m_data.at( row ).count() == m_model->columnCount( m_rootIndex ) );
Q_ASSERT( m_cacheValid.at( row ).count() == m_model->columnCount( m_rootIndex ) );
}
}
void dataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight )
{
Q_ASSERT( m_model != 0 );
Q_ASSERT( topLeft.parent() == bottomRight.parent() );
Q_ASSERT( topLeft.model() == m_model && bottomRight.model() == m_model );
if( !topLeft.isValid() || !bottomRight.isValid() || topLeft.parent() != m_rootIndex )
return;
const int minRow = qMax( 0, topLeft.row() );
const int maxRow = bottomRight.row();
const int minCol = qMax( 0, topLeft.column() );
const int maxCol = bottomRight.column();
Q_ASSERT( minRow <= maxRow );
Q_ASSERT( minCol <= maxCol );
Q_ASSERT( maxRow < m_model->rowCount( m_rootIndex ) );
Q_ASSERT( maxCol < m_model->columnCount( m_rootIndex ) );
for( int row = minRow; row <= maxRow; ++row )
{
for( int col = minCol; col <= maxCol; ++col )
{
m_cacheValid[ row ][ col ] = false;
Q_ASSERT( !isCached( row, col ) );
}
}
}
void layoutChanged()
{
modelReset();
}
void modelReset()
{
m_data.clear();
m_cacheValid.clear();
if( m_model == 0 )
return;
m_data.fill( QVector< T >( m_model->columnCount( m_rootIndex ) ), m_model->rowCount( m_rootIndex ) );
m_cacheValid.fill( QVector< bool >( m_model->columnCount( m_rootIndex ), false ), m_model->rowCount( m_rootIndex ) );
Q_ASSERT( m_data.count() == m_model->rowCount( m_rootIndex ) );
Q_ASSERT( m_cacheValid.count() == m_model->rowCount( m_rootIndex ) );
}
void rowsInserted( const QModelIndex& parent, int start, int end )
{
Q_ASSERT( m_model != 0 );
if( parent != m_rootIndex )
return;
Q_ASSERT( start >= 0 && start <= m_data.size() );
Q_ASSERT( start <= end );
m_data.insert( start, end - start + 1, QVector< T >( m_model->columnCount( m_rootIndex ) ) );
m_cacheValid.insert( start, end - start + 1, QVector< bool >( m_model->columnCount( m_rootIndex ), false ) );
Q_ASSERT( m_data.count() == m_model->rowCount( m_rootIndex ) );
Q_ASSERT( m_cacheValid.count() == m_model->rowCount( m_rootIndex ) );
}
void rowsRemoved( const QModelIndex& parent, int start, int end )
{
Q_ASSERT( m_model != 0 );
if( parent != m_rootIndex )
return;
Q_ASSERT( start <= end );
m_data.remove( start, end - start + 1 );
m_cacheValid.remove( start, end - start + 1 );
Q_ASSERT( m_data.count() == m_model->rowCount( m_rootIndex ) );
Q_ASSERT( m_cacheValid.count() == m_model->rowCount( m_rootIndex ) );
}
void resetModel()
{
// no need to disconnect, this is a response to SIGNAL( destroyed() )
m_model = 0;
modelReset();
}
private:
QAbstractItemModel* m_model;
QModelIndex m_rootIndex;
ModelDataCachePrivate::ModelSignalMapperConnector m_connector;
mutable QVector< QVector< T > > m_data;
mutable QVector< QVector< bool > > m_cacheValid;
};
}
#endif