File indexing completed on 2024-05-12 15:54:18

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