1 /* 2 * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. 3 * 4 * This file is part of the KD Chart library. 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program. If not, see <https://www.gnu.org/licenses/>. 18 */ 19 20 #ifndef KCHARTMODELDATACACHE_H 21 #define KCHARTMODELDATACACHE_H 22 23 #include <limits> 24 25 #include <QObject> 26 #include <QModelIndex> 27 #include <QVector> 28 29 #include "kchart_export.h" 30 31 QT_BEGIN_NAMESPACE 32 class QAbstractItemModel; 33 QT_END_NAMESPACE 34 35 namespace KChart 36 { 37 namespace ModelDataCachePrivate 38 { 39 class ModelSignalMapper 40 { 41 protected: ModelSignalMapper()42 ModelSignalMapper() {} 43 public: ~ModelSignalMapper()44 virtual ~ModelSignalMapper() {} 45 virtual void resetModel() = 0; 46 virtual void columnsInserted( const QModelIndex&, int, int ) = 0; 47 virtual void columnsRemoved( const QModelIndex&, int, int ) = 0; 48 virtual void dataChanged( const QModelIndex&, const QModelIndex& ) = 0; 49 virtual void layoutChanged() = 0; 50 virtual void modelReset() = 0; 51 virtual void rowsInserted( const QModelIndex&, int, int ) = 0; 52 virtual void rowsRemoved( const QModelIndex&, int, int ) = 0; 53 }; 54 55 // this class maps slots to a non-QObject instantiating ModelSignalMapper 56 class KCHART_EXPORT ModelSignalMapperConnector : public QObject 57 { 58 Q_OBJECT 59 public: 60 explicit ModelSignalMapperConnector( ModelSignalMapper& mapper ); 61 ~ModelSignalMapperConnector(); 62 63 void connectSignals( QAbstractItemModel* model ); 64 void disconnectSignals( QAbstractItemModel* model ); 65 66 protected Q_SLOTS: 67 void resetModel(); 68 void columnsInserted( const QModelIndex&, int, int ); 69 void columnsRemoved( const QModelIndex&, int, int ); 70 void dataChanged( const QModelIndex&, const QModelIndex& ); 71 void layoutChanged(); 72 void modelReset(); 73 void rowsInserted( const QModelIndex&, int, int ); 74 void rowsRemoved( const QModelIndex&, int, int ); 75 76 private: 77 ModelSignalMapper& m_mapper; 78 }; 79 80 template< class T> nan()81 T nan() 82 { 83 return T(); 84 } 85 86 template<> 87 inline qreal nan< qreal >() 88 { 89 return std::numeric_limits< qreal >::quiet_NaN(); 90 } 91 } 92 93 template< class T, int ROLE > 94 class ModelDataCache : public ModelDataCachePrivate::ModelSignalMapper 95 { 96 public: ModelDataCache()97 ModelDataCache() 98 : m_model( nullptr ), 99 m_connector( *this ) 100 { 101 } 102 ~ModelDataCache()103 virtual ~ModelDataCache() 104 { 105 } 106 data(const QModelIndex & index)107 T data( const QModelIndex& index ) const 108 { 109 if ( !index.isValid() || index.parent() != m_rootIndex || index.row() >= m_model->rowCount(m_rootIndex) || index.column() >= m_model->columnCount(m_rootIndex) ) 110 return ModelDataCachePrivate::nan< T >(); 111 112 if ( index.row() >= m_data.count() ) 113 { 114 qWarning( "KChart didn't receive signal rowsInserted, resetModel or layoutChanged, " 115 "but an index with a row outside of the known bounds." ); 116 117 // apparently, data were added behind our back (w/o signals) 118 const_cast< ModelDataCache< T, ROLE >* >( this )->rowsInserted( m_rootIndex, 119 m_data.count(), 120 m_model->rowCount( m_rootIndex ) - 1 ); 121 Q_ASSERT( index.row() < m_data.count() ); 122 } 123 124 if ( index.column() >= m_data.first().count() ) 125 { 126 qWarning( "KChart didn't got signal columnsInserted, resetModel or layoutChanged, " 127 "but an index with a column outside of the known bounds." ); 128 129 // apparently, data were added behind our back (w/o signals) 130 const_cast< ModelDataCache< T, ROLE >* >( this )->columnsInserted( m_rootIndex, 131 m_data.first().count(), 132 m_model->columnCount( m_rootIndex ) - 1 ); 133 Q_ASSERT( index.column() < m_data.first().count() ); 134 } 135 136 return data( index.row(), index.column() ); 137 } 138 data(int row,int column)139 T data( int row, int column ) const 140 { 141 if ( row < 0 || column < 0 ) 142 return ModelDataCachePrivate::nan< T >(); 143 144 Q_ASSERT( row < m_model->rowCount(m_rootIndex) ); 145 Q_ASSERT( column < m_model->columnCount(m_rootIndex) ); 146 147 Q_ASSERT( row < m_data.count() ); 148 Q_ASSERT( column < m_data.first().count() ); 149 150 if ( isCached( row, column ) ) 151 return m_data.at( row ).at( column ); 152 153 return fetchFromModel( row, column, ROLE ); 154 } 155 setModel(QAbstractItemModel * model)156 void setModel( QAbstractItemModel* model ) 157 { 158 if ( m_model != nullptr ) 159 m_connector.disconnectSignals( m_model ); 160 161 m_model = model; 162 163 if ( m_model != nullptr ) 164 m_connector.connectSignals( m_model ); 165 166 modelReset(); 167 } 168 model()169 QAbstractItemModel* model() const 170 { 171 return m_model; 172 } 173 setRootIndex(const QModelIndex & rootIndex)174 void setRootIndex( const QModelIndex& rootIndex ) 175 { 176 Q_ASSERT( rootIndex.model() == m_model || !rootIndex.isValid() ); 177 m_rootIndex = rootIndex; 178 modelReset(); 179 } 180 rootIndex()181 QModelIndex rootIndex() const 182 { 183 return m_rootIndex; 184 } 185 186 protected: isCached(int row,int column)187 bool isCached( int row, int column ) const 188 { 189 return m_cacheValid.at( row ).at( column ); 190 } 191 fetchFromModel(int row,int column,int role)192 T fetchFromModel( int row, int column, int role ) const 193 { 194 Q_ASSERT( m_model != nullptr ); 195 196 const QModelIndex index = m_model->index( row, column, m_rootIndex ); 197 const QVariant data = index.data( role ); 198 const T value = data.isNull() ? ModelDataCachePrivate::nan< T >() 199 : ( data.value< T >() ); 200 201 m_data[ row ][ column ] = value; 202 m_cacheValid[ row ][ column ] = true; 203 204 return value; 205 } 206 columnsInserted(const QModelIndex & parent,int start,int end)207 void columnsInserted( const QModelIndex& parent, int start, int end ) override 208 { 209 Q_ASSERT( m_model != nullptr ); 210 Q_ASSERT( parent.model() == m_model || !parent.isValid() ); 211 212 if ( parent != m_rootIndex ) 213 return; 214 215 Q_ASSERT( start <= end ); 216 Q_ASSERT( start <= m_model->columnCount(m_rootIndex) ); 217 218 const int rowCount = m_data.count(); 219 for ( int row = 0; row < rowCount; ++row ) 220 { 221 m_data[ row ].insert( start, end - start + 1, T() ); 222 m_cacheValid[ row ].insert( start, end - start + 1, false ); 223 Q_ASSERT( m_data.at( row ).count() == m_model->columnCount( m_rootIndex ) ); 224 Q_ASSERT( m_cacheValid.at( row ).count() == m_model->columnCount( m_rootIndex ) ); 225 } 226 } 227 columnsRemoved(const QModelIndex & parent,int start,int end)228 void columnsRemoved( const QModelIndex& parent, int start, int end ) override 229 { 230 Q_ASSERT( m_model != nullptr ); 231 Q_ASSERT( parent.model() == m_model || !parent.isValid() ); 232 233 if ( parent != m_rootIndex ) 234 return; 235 236 Q_ASSERT( start <= end ); 237 238 const int rowCount = m_data.count(); 239 for ( int row = 0; row < rowCount; ++row ) 240 { 241 m_data[ row ].remove( start, end - start + 1 ); 242 m_cacheValid[ row ].remove( start, end - start + 1 ); 243 Q_ASSERT( m_data.at( row ).count() == m_model->columnCount( m_rootIndex ) ); 244 Q_ASSERT( m_cacheValid.at( row ).count() == m_model->columnCount( m_rootIndex ) ); 245 } 246 } 247 dataChanged(const QModelIndex & topLeft,const QModelIndex & bottomRight)248 void dataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight ) override 249 { 250 if ( !m_model ) 251 return; 252 Q_ASSERT( m_model != nullptr ); 253 Q_ASSERT( topLeft.parent() == bottomRight.parent() ); 254 255 if ( !topLeft.isValid() || !bottomRight.isValid() || topLeft.parent() != m_rootIndex ) 256 return; 257 258 Q_ASSERT( topLeft.model() == m_model && bottomRight.model() == m_model ); 259 260 const int minRow = qMax( 0, topLeft.row() ); 261 const int maxRow = bottomRight.row(); 262 const int minCol = qMax( 0, topLeft.column() ); 263 const int maxCol = bottomRight.column(); 264 265 Q_ASSERT( minRow <= maxRow ); 266 Q_ASSERT( minCol <= maxCol ); 267 Q_ASSERT( maxRow < m_model->rowCount( m_rootIndex ) ); 268 Q_ASSERT( maxCol < m_model->columnCount( m_rootIndex ) ); 269 270 for ( int row = minRow; row <= maxRow; ++row ) 271 { 272 for ( int col = minCol; col <= maxCol; ++col ) 273 { 274 m_cacheValid[ row ][ col ] = false; 275 Q_ASSERT( !isCached( row, col ) ); 276 } 277 } 278 } 279 layoutChanged()280 void layoutChanged() override 281 { 282 modelReset(); 283 } 284 modelReset()285 void modelReset() override 286 { 287 m_data.clear(); 288 m_cacheValid.clear(); 289 290 if ( m_model == nullptr ) 291 return; 292 293 m_data.fill( QVector< T >( m_model->columnCount( m_rootIndex ) ), m_model->rowCount( m_rootIndex ) ); 294 m_cacheValid.fill( QVector< bool >( m_model->columnCount( m_rootIndex ), false ), m_model->rowCount( m_rootIndex ) ); 295 296 Q_ASSERT( m_data.count() == m_model->rowCount( m_rootIndex ) ); 297 Q_ASSERT( m_cacheValid.count() == m_model->rowCount( m_rootIndex ) ); 298 } 299 rowsInserted(const QModelIndex & parent,int start,int end)300 void rowsInserted( const QModelIndex& parent, int start, int end ) override 301 { 302 Q_ASSERT( m_model != nullptr ); 303 Q_ASSERT( parent.model() == m_model || !parent.isValid() ); 304 305 if ( parent != m_rootIndex || start >= m_model->rowCount(m_rootIndex) ) 306 return; 307 308 Q_ASSERT( start <= end ); 309 Q_ASSERT( end - start + 1 <= m_model->rowCount(m_rootIndex) ); 310 311 m_data.insert( start, end - start + 1, QVector< T >( m_model->columnCount( m_rootIndex ) ) ); 312 m_cacheValid.insert( start, end - start + 1, QVector< bool >( m_model->columnCount( m_rootIndex ), false ) ); 313 314 Q_ASSERT( m_data.count() == m_model->rowCount( m_rootIndex ) ); 315 Q_ASSERT( m_cacheValid.count() == m_model->rowCount( m_rootIndex ) ); 316 } 317 rowsRemoved(const QModelIndex & parent,int start,int end)318 void rowsRemoved( const QModelIndex& parent, int start, int end ) override 319 { 320 Q_ASSERT( m_model != nullptr ); 321 Q_ASSERT( parent.model() == m_model || !parent.isValid() ); 322 323 if ( parent != m_rootIndex || start >= m_data.count() ) 324 return; 325 326 Q_ASSERT( start <= end ); 327 328 m_data.remove( start, end - start + 1 ); 329 m_cacheValid.remove( start, end - start + 1 ); 330 331 Q_ASSERT( m_data.count() == m_model->rowCount( m_rootIndex ) ); 332 Q_ASSERT( m_cacheValid.count() == m_model->rowCount( m_rootIndex ) ); 333 } 334 resetModel()335 void resetModel() override 336 { 337 // no need to disconnect, this is a response to SIGNAL( destroyed() ) 338 m_model = nullptr; 339 modelReset(); 340 } 341 342 private: 343 QAbstractItemModel* m_model; 344 QModelIndex m_rootIndex; 345 ModelDataCachePrivate::ModelSignalMapperConnector m_connector; 346 mutable QVector< QVector< T > > m_data; 347 mutable QVector< QVector< bool > > m_cacheValid; 348 }; 349 } 350 351 #endif 352