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