KD Chart 2  [rev.2.6]
kdganttsummaryhandlingproxymodel.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 ** Copyright (C) 2001-2019 Klaralvdalens Datakonsult AB. All rights reserved.
3 **
4 ** This file is part of the KD Chart library.
5 **
6 ** Licensees holding valid commercial KD Chart licenses may use this file in
7 ** accordance with the KD Chart Commercial License Agreement provided with
8 ** the Software.
9 **
10 **
11 ** This file may be distributed and/or modified under the terms of the
12 ** GNU General Public License version 2 and version 3 as published by the
13 ** Free Software Foundation and appearing in the file LICENSE.GPL.txt included.
14 **
15 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
16 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
17 **
18 ** Contact info@kdab.com if any conditions of this licensing are not
19 ** clear to you.
20 **
21 **********************************************************************/
22 
24 #include "kdganttsummaryhandlingproxymodel_p.h"
25 
26 #include <QDebug>
27 
28 #include <cassert>
29 
30 using namespace KDGantt;
31 
49 
50 bool SummaryHandlingProxyModel::Private::cacheLookup( const QModelIndex& idx,
51  QPair<QDateTime,QDateTime>* result ) const
52 {
53  //qDebug() << "cacheLookup("<<idx<<"), cache has " << cached_summary_items.count() << "items";
54  QHash<QModelIndex,QPair<QDateTime,QDateTime> >::const_iterator it =
55  cached_summary_items.constFind( idx );
56  if ( it != cached_summary_items.constEnd() ) {
57  *result = *it;
58  return true;
59  } else {
60  return false;
61  }
62 }
63 
64 void SummaryHandlingProxyModel::Private::insertInCache( const SummaryHandlingProxyModel* model,
65  const QModelIndex& sourceIdx ) const
66 {
67  QAbstractItemModel* sourceModel = model->sourceModel();
68  const QModelIndex& mainIdx = sourceIdx;
69  QDateTime st;
70  QDateTime et;
71 
72  for ( int r = 0; r < sourceModel->rowCount( mainIdx ); ++r ) {
73  QModelIndex pdIdx = model->mapFromSource( sourceModel->index( r, 0, mainIdx ) );
74  /* The probably results in recursive calls here */
75  QVariant tmpsv = model->data( pdIdx, StartTimeRole );
76  QVariant tmpev = model->data( pdIdx, EndTimeRole );
77  if ( !tmpsv.canConvert( QVariant::DateTime ) ||
78  !tmpev.canConvert( QVariant::DateTime ) ) {
79  qDebug() << "Skipping item " << sourceIdx << " because it doesn't contain QDateTime";
80  continue;
81  }
82 
83  // check for valid datetimes
84  if ( tmpsv.type() == QVariant::DateTime && !tmpsv.value<QDateTime>().isValid()) continue;
85  if ( tmpev.type() == QVariant::DateTime && !tmpev.value<QDateTime>().isValid()) continue;
86 
87  // We need to test for empty strings to
88  // avoid a stupid Qt warning
89  if ( tmpsv.type() == QVariant::String && tmpsv.value<QString>().isEmpty()) continue;
90  if ( tmpev.type() == QVariant::String && tmpev.value<QString>().isEmpty()) continue;
91  QDateTime tmpst = tmpsv.toDateTime();
92  QDateTime tmpet = tmpev.toDateTime();
93  if ( st.isNull() || st > tmpst ) st = tmpst;
94  if ( et.isNull() || et < tmpet ) et = tmpet;
95  }
96  QVariant tmpssv = sourceModel->data( mainIdx, StartTimeRole );
97  QVariant tmpsev = sourceModel->data( mainIdx, EndTimeRole );
98  if ( tmpssv.canConvert( QVariant::DateTime )
99  && !( tmpssv.canConvert( QVariant::String ) && tmpssv.toString().isEmpty() )
100  && tmpssv.toDateTime() != st )
101  sourceModel->setData( mainIdx, st, StartTimeRole );
102  if ( tmpsev.canConvert( QVariant::DateTime )
103  && !( tmpsev.canConvert( QVariant::String ) && tmpsev.toString().isEmpty() )
104  && tmpsev.toDateTime() != et )
105  sourceModel->setData( mainIdx, et, EndTimeRole );
106  cached_summary_items[sourceIdx]=qMakePair( st, et );
107 }
108 
109 void SummaryHandlingProxyModel::Private::removeFromCache( const QModelIndex& idx ) const
110 {
111  cached_summary_items.remove( idx );
112 }
113 
114 void SummaryHandlingProxyModel::Private::clearCache() const
115 {
116  cached_summary_items.clear();
117 }
118 
123  : BASE( parent ), _d( new Private )
124 {
125  init();
126 }
127 
128 #define d d_func()
130 {
131  delete _d;
132 }
133 
134 void SummaryHandlingProxyModel::init()
135 {
136 }
137 
138 namespace {
139 
140  // Think this is ugly? Well, it's not from me, it comes from QProxyModel
141  struct KDPrivateModelIndex {
142  int r, c;
143  void *p;
144  const QAbstractItemModel *m;
145  };
146 }
147 
152 void SummaryHandlingProxyModel::setSourceModel( QAbstractItemModel* model )
153 {
154  BASE::setSourceModel( model );
155  d->clearCache();
156 }
157 
159 {
160  d->clearCache();
161  BASE::sourceModelReset();
162 }
163 
165 {
166  d->clearCache();
167  BASE::sourceLayoutChanged();
168 }
169 
170 void SummaryHandlingProxyModel::sourceDataChanged( const QModelIndex& from, const QModelIndex& to )
171 {
172  QAbstractItemModel* model = sourceModel();
173  QModelIndex parentIdx = from;
174  do {
175  const QModelIndex& dataIdx = parentIdx;
176  if ( model->data( dataIdx, ItemTypeRole )==TypeSummary ) {
177  //qDebug() << "removing " << parentIdx << "from cache";
178  d->removeFromCache( dataIdx );
179  QModelIndex proxyDataIdx = mapFromSource( dataIdx );
180  emit dataChanged( proxyDataIdx, proxyDataIdx );
181  }
182  } while ( ( parentIdx=model->parent( parentIdx ) ) != QModelIndex() );
183 
184  BASE::sourceDataChanged( from, to );
185 }
186 
188  int start,
189  int end )
190 {
191  BASE::sourceColumnsAboutToBeInserted( parentIdx, start, end );
192  d->clearCache();
193 }
194 
196  int start,
197  int end )
198 {
199  BASE::sourceColumnsAboutToBeRemoved( parentIdx, start, end );
200  d->clearCache();
201 }
202 
203 void SummaryHandlingProxyModel::sourceRowsAboutToBeInserted( const QModelIndex & parentIdx, int start, int end )
204 {
205  BASE::sourceRowsAboutToBeInserted( parentIdx, start, end );
206  d->clearCache();
207 }
208 
209 void SummaryHandlingProxyModel::sourceRowsAboutToBeRemoved( const QModelIndex & parentIdx, int start, int end )
210 {
211  BASE::sourceRowsAboutToBeRemoved( parentIdx, start, end );
212  d->clearCache();
213 }
214 
216 Qt::ItemFlags SummaryHandlingProxyModel::flags( const QModelIndex& idx ) const
217 {
218  const QModelIndex sidx = mapToSource( idx );
219  const QAbstractItemModel* model = sourceModel();
220  Qt::ItemFlags f = model->flags( sidx );
221  if ( d->isSummary(sidx) ) {
222  f &= ~Qt::ItemIsEditable;
223  }
224  return f;
225 }
226 
228 QVariant SummaryHandlingProxyModel::data( const QModelIndex& proxyIndex, int role) const
229 {
230  //qDebug() << "SummaryHandlingProxyModel::data("<<proxyIndex<<role<<")";
231  const QModelIndex sidx = mapToSource( proxyIndex );
232  const QAbstractItemModel* model = sourceModel();
233  if ( d->isSummary(sidx) && ( role==StartTimeRole || role==EndTimeRole )) {
234  //qDebug() << "requested summary";
236  if ( d->cacheLookup( sidx, &result ) ) {
237  //qDebug() << "SummaryHandlingProxyModel::data(): Looking up summary for " << proxyIndex << role;
238  switch ( role ) {
239  case StartTimeRole: return result.first;
240  case EndTimeRole: return result.second;
241  default: /* fall thru */;
242  }
243  } else {
244  d->insertInCache( this, sidx );
245  return data( proxyIndex, role ); /* TODO: Optimize */
246  }
247  }
248  return model->data( sidx, role );
249 }
250 
252 bool SummaryHandlingProxyModel::setData( const QModelIndex& index, const QVariant& value, int role )
253 {
254  QAbstractItemModel* model = sourceModel();
255  if ( role==StartTimeRole || role==EndTimeRole ) {
256  QModelIndex parentIdx = mapToSource( index );
257  do {
258  if ( d->isSummary(parentIdx) ) {
259  //qDebug() << "removing " << parentIdx << "from cache";
260  d->removeFromCache( parentIdx );
261  QModelIndex proxyParentIdx = mapFromSource( parentIdx );
262  emit dataChanged( proxyParentIdx, proxyParentIdx );
263  }
264  } while ( ( parentIdx=model->parent( parentIdx ) ) != QModelIndex() );
265  }
266  return BASE::setData( index, value, role );
267 }
268 
269 #undef d
270 
271 #ifndef KDAB_NO_UNIT_TESTS
272 
273 #include "unittest/test.h"
274 
275 #include <QStandardItemModel>
276 
277 static std::ostream& operator<<( std::ostream& os, const QDateTime& dt )
278 {
279 #ifdef QT_NO_STL
280  os << dt.toString().toLatin1().constData();
281 #else
282  os << dt.toString().toStdString();
283 #endif
284  return os;
285 }
286 
289  QStandardItemModel sourceModel;
290 
291  model.setSourceModel( &sourceModel );
292 
293  QStandardItem* topitem = new QStandardItem( QString::fromLatin1( "Summary" ) );
294  topitem->setData( KDGantt::TypeSummary, KDGantt::ItemTypeRole );
295  sourceModel.appendRow( topitem );
296 
297  QStandardItem* task1 = new QStandardItem( QString::fromLatin1( "Task1" ) );
298  task1->setData( KDGantt::TypeTask, KDGantt::ItemTypeRole );
299  QStandardItem* task2 = new QStandardItem( QString::fromLatin1( "Task2" ) );
300  task2->setData( KDGantt::TypeTask, KDGantt::ItemTypeRole );
301  topitem->appendRow( task1 );
302  topitem->appendRow( task2 );
303 
304 
305  QDateTime startdt = QDateTime::currentDateTime();
306  QDateTime enddt = startdt.addDays( 1 );
307 
308 
309  task1->setData( startdt, KDGantt::StartTimeRole );
310  task1->setData( enddt, KDGantt::EndTimeRole );
311  task2->setData( startdt, KDGantt::StartTimeRole );
312  task2->setData( enddt, KDGantt::EndTimeRole );
313 
314  const QModelIndex topidx = model.index( 0, 0, QModelIndex() );
315 
316  assertEqual( model.data( topidx, KDGantt::ItemTypeRole ).toInt(), KDGantt::TypeSummary );
317  assertEqual( model.data( model.index( 0, 0, topidx ), KDGantt::ItemTypeRole ).toInt(), KDGantt::TypeTask );
318 
319  QDateTime task1startdt = model.data( model.index( 0, 0, topidx ), KDGantt::StartTimeRole ).toDateTime();
320  assertEqual( task1startdt, startdt );
321 
322  QDateTime summarystartdt = model.data( topidx, KDGantt::StartTimeRole ).toDateTime();
323  assertEqual( summarystartdt, startdt );
324  assertTrue( model.flags( model.index( 0, 0, topidx ) ) & Qt::ItemIsEditable );
325  assertFalse( model.flags( topidx ) & Qt::ItemIsEditable );
326 }
327 
328 #endif /* KDAB_NO_UNIT_TESTS */
329 
330 #include "moc_kdganttsummaryhandlingproxymodel.cpp"
Proxy model that supports summary gantt items.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const
void sourceDataChanged(const QModelIndex &from, const QModelIndex &to)
QModelIndex mapToSource(const QModelIndex &proxyIndex) const
void sourceColumnsAboutToBeInserted(const QModelIndex &idx, int start, int end)
void sourceColumnsAboutToBeRemoved(const QModelIndex &idx, int start, int end)
QModelIndex mapFromSource(const QModelIndex &sourceIndex) const
Class only listed here to document inheritance of some KDChart classes.
ForwardingProxyModel BASE
static std::ostream & operator<<(std::ostream &os, const QDateTime &dt)
void sourceRowsAboutToBeInserted(const QModelIndex &idx, int start, int end)
Qt::ItemFlags flags(const QModelIndex &idx) const
KDAB_SCOPED_UNITTEST_SIMPLE(KDGantt, SummaryHandlingProxyModel,"test")
Class only listed here to document inheritance of some KDChart classes.
#define assertFalse(x)
Definition: test.h:42
void sourceRowsAboutToBeRemoved(const QModelIndex &, int start, int end)
QVariant data(const QModelIndex &proxyIndex, int role=Qt::DisplayRole) const
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole)
#define assertTrue(x)
Definition: test.h:41
#define assertEqual(x, y)
Definition: test.h:43

Klarälvdalens Datakonsult AB (KDAB)
Qt-related services and products
https://www.kdab.com/
https://www.kdab.com/products/kd-chart/