File indexing completed on 2024-05-12 04:20:36

0001 /*
0002  * SPDX-FileCopyrightText: 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
0003  *
0004  * This file is part of the KD Chart library.
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #ifndef KCHARTMODELDATACACHE_H
0010 #define KCHARTMODELDATACACHE_H
0011 
0012 #include <limits>
0013 
0014 #include <QObject>
0015 #include <QModelIndex>
0016 #include <QVector>
0017 
0018 #include "kchart_export.h"
0019 
0020 QT_BEGIN_NAMESPACE
0021 class QAbstractItemModel;
0022 QT_END_NAMESPACE
0023 
0024 namespace KChart
0025 {
0026     namespace ModelDataCachePrivate
0027     {
0028         class ModelSignalMapper
0029         {
0030         protected:
0031             ModelSignalMapper() {}
0032         public:
0033             virtual ~ModelSignalMapper() {}
0034             virtual void resetModel() = 0;
0035             virtual void columnsInserted( const QModelIndex&, int, int ) = 0;
0036             virtual void columnsRemoved( const QModelIndex&, int, int ) = 0;
0037             virtual void dataChanged( const QModelIndex&, const QModelIndex& ) = 0;
0038             virtual void layoutChanged() = 0;
0039             virtual void modelReset() = 0;
0040             virtual void rowsInserted( const QModelIndex&, int, int ) = 0;
0041             virtual void rowsRemoved( const QModelIndex&, int, int ) = 0;
0042         };
0043 
0044         // this class maps slots to a non-QObject instantiating ModelSignalMapper
0045         class KCHART_EXPORT ModelSignalMapperConnector : public QObject
0046         {
0047             Q_OBJECT
0048         public:
0049             explicit ModelSignalMapperConnector( ModelSignalMapper& mapper );
0050             ~ModelSignalMapperConnector() override;
0051 
0052             void connectSignals( QAbstractItemModel* model );
0053             void disconnectSignals( QAbstractItemModel* model );
0054 
0055         protected Q_SLOTS:
0056             void resetModel();
0057             void columnsInserted( const QModelIndex&, int, int );
0058             void columnsRemoved( const QModelIndex&, int, int );
0059             void dataChanged( const QModelIndex&, const QModelIndex& );
0060             void layoutChanged();
0061             void modelReset();
0062             void rowsInserted( const QModelIndex&, int, int );
0063             void rowsRemoved( const QModelIndex&, int, int );
0064 
0065         private:
0066             ModelSignalMapper& m_mapper;
0067         };
0068 
0069         template< class T>
0070         T nan()
0071         {
0072             return T();
0073         }
0074 
0075         template<>
0076         inline qreal nan< qreal >()
0077         {
0078             return std::numeric_limits< qreal >::quiet_NaN();
0079         }
0080     }
0081 
0082     template< class T, int ROLE >
0083     class ModelDataCache : public ModelDataCachePrivate::ModelSignalMapper
0084     {
0085     public:
0086         ModelDataCache()
0087             : m_model( nullptr ),
0088               m_connector( *this )
0089         {
0090         }
0091 
0092         ~ModelDataCache() override
0093         {
0094         }
0095 
0096         T data( const QModelIndex& index ) const
0097         {
0098             if ( !index.isValid() || index.parent() != m_rootIndex || index.row() >= m_model->rowCount(m_rootIndex) || index.column() >= m_model->columnCount(m_rootIndex) )
0099                 return ModelDataCachePrivate::nan< T >();
0100 
0101             if ( index.row() >= m_data.count() )
0102             {
0103                 qWarning( "KChart didn't receive signal rowsInserted, resetModel or layoutChanged, "
0104                           "but an index with a row outside of the known bounds." );
0105 
0106                 // apparently, data were added behind our back (w/o signals)
0107                 const_cast< ModelDataCache< T, ROLE >* >( this )->rowsInserted( m_rootIndex,
0108                                                                                 m_data.count(),
0109                                                                                 m_model->rowCount( m_rootIndex ) - 1 );
0110                 Q_ASSERT( index.row() < m_data.count() );
0111             }
0112 
0113             if ( index.column() >= m_data.first().count() )
0114             {
0115                 qWarning( "KChart didn't got signal columnsInserted, resetModel or layoutChanged, "
0116                           "but an index with a column outside of the known bounds." );
0117 
0118                 // apparently, data were added behind our back (w/o signals)
0119                 const_cast< ModelDataCache< T, ROLE >* >( this )->columnsInserted( m_rootIndex,
0120                                                                                    m_data.first().count(),
0121                                                                                    m_model->columnCount( m_rootIndex ) - 1 );
0122                 Q_ASSERT( index.column() < m_data.first().count() );
0123             }
0124 
0125             return data( index.row(), index.column() );
0126         }
0127 
0128         T data( int row, int column ) const
0129         {
0130             if ( row < 0 || column < 0 )
0131                 return ModelDataCachePrivate::nan< T >();
0132 
0133             Q_ASSERT( row < m_model->rowCount(m_rootIndex) );
0134             Q_ASSERT( column < m_model->columnCount(m_rootIndex) );
0135 
0136             Q_ASSERT( row < m_data.count() );
0137             Q_ASSERT( column < m_data.first().count() );
0138 
0139             if ( isCached( row, column ) )
0140                 return m_data.at( row ).at( column );
0141 
0142             return fetchFromModel( row, column, ROLE );
0143         }
0144 
0145         void setModel( QAbstractItemModel* model )
0146         {
0147             if ( m_model != nullptr )
0148                 m_connector.disconnectSignals( m_model );
0149 
0150             m_model = model;
0151 
0152             if ( m_model != nullptr )
0153                 m_connector.connectSignals( m_model );
0154 
0155             modelReset();
0156         }
0157 
0158         QAbstractItemModel* model() const
0159         {
0160             return m_model;
0161         }
0162 
0163         void setRootIndex( const QModelIndex& rootIndex )
0164         {
0165             Q_ASSERT( rootIndex.model() == m_model || !rootIndex.isValid() );
0166             m_rootIndex = rootIndex;
0167             modelReset();
0168         }
0169 
0170         QModelIndex rootIndex() const
0171         {
0172             return m_rootIndex;
0173         }
0174 
0175     protected:
0176         bool isCached( int row, int column ) const
0177         {
0178             return m_cacheValid.at( row ).at( column );
0179         }
0180 
0181         T fetchFromModel( int row, int column, int role ) const
0182         {
0183             Q_ASSERT( m_model != nullptr );
0184 
0185             const QModelIndex index = m_model->index( row, column, m_rootIndex );
0186             const QVariant data = index.data( role );
0187             const T value = data.isNull() ? ModelDataCachePrivate::nan< T >()
0188                                           : ( data.value< T >() );
0189 
0190             m_data[ row ][ column ] = value;
0191             m_cacheValid[ row ][ column ] = true;
0192 
0193             return value;
0194         }
0195 
0196         void columnsInserted( const QModelIndex& parent, int start, int end ) override
0197         {
0198             Q_ASSERT( m_model != nullptr );
0199             Q_ASSERT( parent.model() == m_model || !parent.isValid() );
0200 
0201             if ( parent != m_rootIndex )
0202                 return;
0203 
0204             Q_ASSERT( start <= end );
0205             Q_ASSERT( start <= m_model->columnCount(m_rootIndex) );
0206 
0207             const int rowCount = m_data.count();
0208             for ( int row = 0; row < rowCount; ++row )
0209             {
0210                 m_data[ row ].insert( start, end - start + 1, T() );
0211                 m_cacheValid[ row ].insert( start, end - start + 1, false );
0212                 Q_ASSERT( m_data.at( row ).count() == m_model->columnCount( m_rootIndex ) );
0213                 Q_ASSERT( m_cacheValid.at( row ).count() == m_model->columnCount( m_rootIndex ) );
0214             }
0215         }
0216 
0217         void columnsRemoved( const QModelIndex& parent, int start, int end ) override
0218         {
0219             Q_ASSERT( m_model != nullptr );
0220             Q_ASSERT( parent.model() == m_model || !parent.isValid() );
0221 
0222             if ( parent != m_rootIndex )
0223                 return;
0224 
0225             Q_ASSERT( start <= end );
0226 
0227             const int rowCount = m_data.count();
0228             for ( int row = 0; row < rowCount; ++row )
0229             {
0230                 m_data[ row ].remove( start, end - start + 1 );
0231                 m_cacheValid[ row ].remove( start, end - start + 1 );
0232                 Q_ASSERT( m_data.at( row ).count() == m_model->columnCount( m_rootIndex ) );
0233                 Q_ASSERT( m_cacheValid.at( row ).count() == m_model->columnCount( m_rootIndex ) );
0234             }
0235         }
0236 
0237         void dataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight ) override
0238         {
0239             if ( !m_model )
0240                 return;
0241             Q_ASSERT( m_model != nullptr );
0242             Q_ASSERT( topLeft.parent() == bottomRight.parent() );
0243 
0244             if ( !topLeft.isValid() || !bottomRight.isValid() || topLeft.parent() != m_rootIndex )
0245                 return;
0246 
0247             Q_ASSERT( topLeft.model() == m_model && bottomRight.model() == m_model );
0248 
0249             const int minRow = qMax( 0, topLeft.row() );
0250             const int maxRow = bottomRight.row();
0251             const int minCol = qMax( 0, topLeft.column() );
0252             const int maxCol = bottomRight.column();
0253 
0254             Q_ASSERT( minRow <= maxRow );
0255             Q_ASSERT( minCol <= maxCol );
0256             Q_ASSERT( maxRow < m_model->rowCount( m_rootIndex ) );
0257             Q_ASSERT( maxCol < m_model->columnCount( m_rootIndex ) );
0258 
0259             for ( int row = minRow; row <= maxRow; ++row )
0260             {
0261                 for ( int col = minCol; col <= maxCol; ++col )
0262                 {
0263                     m_cacheValid[ row ][ col ] = false;
0264                     Q_ASSERT( !isCached( row, col ) );
0265                 }
0266             }
0267         }
0268 
0269         void layoutChanged() override
0270         {
0271             modelReset();
0272         }
0273 
0274         void modelReset() override
0275         {
0276             m_data.clear();
0277             m_cacheValid.clear();
0278 
0279             if ( m_model == nullptr )
0280                 return;
0281 
0282             m_data.fill( QVector< T >( m_model->columnCount( m_rootIndex ) ), m_model->rowCount( m_rootIndex ) );
0283             m_cacheValid.fill( QVector< bool >( m_model->columnCount( m_rootIndex ), false ), m_model->rowCount( m_rootIndex ) );
0284 
0285             Q_ASSERT( m_data.count() == m_model->rowCount( m_rootIndex ) );
0286             Q_ASSERT( m_cacheValid.count() == m_model->rowCount( m_rootIndex ) );
0287         }
0288 
0289         void rowsInserted( const QModelIndex& parent, int start, int end ) override
0290         {
0291             Q_ASSERT( m_model != nullptr );
0292             Q_ASSERT( parent.model() == m_model || !parent.isValid() );
0293 
0294             if ( parent != m_rootIndex || start >= m_model->rowCount(m_rootIndex) )
0295                 return;
0296 
0297             Q_ASSERT( start <= end );
0298             Q_ASSERT( end - start + 1 <= m_model->rowCount(m_rootIndex) );
0299 
0300             m_data.insert( start, end - start + 1, QVector< T >( m_model->columnCount( m_rootIndex ) ) );
0301             m_cacheValid.insert( start, end - start + 1, QVector< bool >( m_model->columnCount( m_rootIndex ), false ) );
0302 
0303             Q_ASSERT( m_data.count() == m_model->rowCount( m_rootIndex ) );
0304             Q_ASSERT( m_cacheValid.count() == m_model->rowCount( m_rootIndex ) );
0305         }
0306 
0307         void rowsRemoved( const QModelIndex& parent, int start, int end ) override
0308         {
0309             Q_ASSERT( m_model != nullptr );
0310             Q_ASSERT( parent.model() == m_model || !parent.isValid() );
0311 
0312             if ( parent != m_rootIndex || start >= m_data.count() )
0313                 return;
0314 
0315             Q_ASSERT( start <= end );
0316 
0317             m_data.remove( start, end - start + 1 );
0318             m_cacheValid.remove( start, end - start + 1 );
0319 
0320             Q_ASSERT( m_data.count() == m_model->rowCount( m_rootIndex ) );
0321             Q_ASSERT( m_cacheValid.count() == m_model->rowCount( m_rootIndex ) );
0322         }
0323 
0324         void resetModel() override
0325         {
0326             // no need to disconnect, this is a response to SIGNAL( destroyed() )
0327             m_model = nullptr;
0328             modelReset();
0329         }
0330 
0331     private:
0332         QAbstractItemModel* m_model;
0333         QModelIndex m_rootIndex;
0334         ModelDataCachePrivate::ModelSignalMapperConnector m_connector;
0335         mutable QVector< QVector< T > > m_data;
0336         mutable QVector< QVector< bool > > m_cacheValid;
0337     };
0338 }
0339 
0340 #endif