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