KGantt

kganttsummaryhandlingproxymodel.cpp
1 /*
2  * SPDX-FileCopyrightText: 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
3  *
4  * This file is part of the KGantt library.
5  *
6  * SPDX-License-Identifier: GPL-2.0-or-later
7  */
8 
9 #include "kganttsummaryhandlingproxymodel.h"
10 #include "kganttsummaryhandlingproxymodel_p.h"
11 
12 #include <QDebug>
13 
14 #include <cassert>
15 
16 using namespace KGantt;
17 
18 
19 
20 typedef ForwardingProxyModel BASE;
21 
22 bool SummaryHandlingProxyModel::Private::cacheLookup( const QModelIndex& idx,
23  QPair<QDateTime,QDateTime>* result ) const
24 {
25  //qDebug() << "cacheLookup("<<idx<<"), cache has " << cached_summary_items.count() << "items";
27  cached_summary_items.constFind( idx );
28  if ( it != cached_summary_items.constEnd() ) {
29  *result = *it;
30  return true;
31  } else {
32  return false;
33  }
34 }
35 
36 void SummaryHandlingProxyModel::Private::insertInCache( const SummaryHandlingProxyModel* model,
37  const QModelIndex& sourceIdx ) const
38 {
39  QAbstractItemModel* sourceModel = model->sourceModel();
40  const QModelIndex& mainIdx = sourceIdx;
41  QDateTime st;
42  QDateTime et;
43 
44  for ( int r = 0; r < sourceModel->rowCount( mainIdx ); ++r ) {
45  QModelIndex pdIdx = model->mapFromSource( sourceModel->index( r, 0, mainIdx ) );
46  /* The probably results in recursive calls here */
47  QVariant tmpsv = model->data( pdIdx, StartTimeRole );
48  QVariant tmpev = model->data( pdIdx, EndTimeRole );
49  if ( !tmpsv.canConvert( QVariant::DateTime ) ||
50  !tmpev.canConvert( QVariant::DateTime ) ) {
51  qDebug() << "Skipping item " << sourceIdx << " because it doesn't contain QDateTime";
52  continue;
53  }
54 
55  // check for valid datetimes
56  if ( tmpsv.type() == QVariant::DateTime && !tmpsv.value<QDateTime>().isValid()) continue;
57  if ( tmpev.type() == QVariant::DateTime && !tmpev.value<QDateTime>().isValid()) continue;
58 
59  // We need to test for empty strings to
60  // avoid a stupid Qt warning
61  if ( tmpsv.type() == QVariant::String && tmpsv.value<QString>().isEmpty()) continue;
62  if ( tmpev.type() == QVariant::String && tmpev.value<QString>().isEmpty()) continue;
63  QDateTime tmpst = tmpsv.toDateTime();
64  QDateTime tmpet = tmpev.toDateTime();
65  if ( st.isNull() || st > tmpst ) st = tmpst;
66  if ( et.isNull() || et < tmpet ) et = tmpet;
67  }
68  QVariant tmpssv = sourceModel->data( mainIdx, StartTimeRole );
69  QVariant tmpsev = sourceModel->data( mainIdx, EndTimeRole );
70  if ( tmpssv.canConvert( QVariant::DateTime )
71  && !( tmpssv.canConvert( QVariant::String ) && tmpssv.toString().isEmpty() )
72  && tmpssv.toDateTime() != st )
73  sourceModel->setData( mainIdx, st, StartTimeRole );
74  if ( tmpsev.canConvert( QVariant::DateTime )
75  && !( tmpsev.canConvert( QVariant::String ) && tmpsev.toString().isEmpty() )
76  && tmpsev.toDateTime() != et )
77  sourceModel->setData( mainIdx, et, EndTimeRole );
78  cached_summary_items[sourceIdx]=qMakePair( st, et );
79 }
80 
81 void SummaryHandlingProxyModel::Private::removeFromCache( const QModelIndex& idx ) const
82 {
83  cached_summary_items.remove( idx );
84 }
85 
86 void SummaryHandlingProxyModel::Private::clearCache() const
87 {
88  cached_summary_items.clear();
89 }
90 
91 
93  : BASE( parent ), _d( new Private )
94 {
95  init();
96 }
97 
98 #define d d_func()
99 SummaryHandlingProxyModel::~SummaryHandlingProxyModel()
100 {
101  delete _d;
102 }
103 
104 void SummaryHandlingProxyModel::init()
105 {
106 }
107 
108 
110 {
111  BASE::setSourceModel( model );
112  d->clearCache();
113 }
114 
115 void SummaryHandlingProxyModel::sourceModelReset()
116 {
117  d->clearCache();
118  BASE::sourceModelReset();
119 }
120 
121 void SummaryHandlingProxyModel::sourceLayoutChanged()
122 {
123  d->clearCache();
124  BASE::sourceLayoutChanged();
125 }
126 
127 void SummaryHandlingProxyModel::sourceDataChanged( const QModelIndex& from, const QModelIndex& to )
128 {
129  QAbstractItemModel* model = sourceModel();
130  QModelIndex parentIdx = from;
131  do {
132  const QModelIndex& dataIdx = parentIdx;
133  if ( model->data( dataIdx, ItemTypeRole )==TypeSummary ) {
134  //qDebug() << "removing " << parentIdx << "from cache";
135  d->removeFromCache( dataIdx );
136  QModelIndex proxyDataIdx = mapFromSource( dataIdx );
137  Q_EMIT dataChanged( proxyDataIdx, proxyDataIdx );
138  }
139  } while ( ( parentIdx=model->parent( parentIdx ) ) != QModelIndex() );
140 
141  BASE::sourceDataChanged( from, to );
142 }
143 
144 void SummaryHandlingProxyModel::sourceColumnsAboutToBeInserted( const QModelIndex& parentIdx,
145  int start,
146  int end )
147 {
148  BASE::sourceColumnsAboutToBeInserted( parentIdx, start, end );
149  d->clearCache();
150 }
151 
152 void SummaryHandlingProxyModel::sourceColumnsAboutToBeRemoved( const QModelIndex& parentIdx,
153  int start,
154  int end )
155 {
156  BASE::sourceColumnsAboutToBeRemoved( parentIdx, start, end );
157  d->clearCache();
158 }
159 
160 void SummaryHandlingProxyModel::sourceRowsAboutToBeInserted( const QModelIndex & parentIdx, int start, int end )
161 {
162  BASE::sourceRowsAboutToBeInserted( parentIdx, start, end );
163  d->clearCache();
164 }
165 
166 void SummaryHandlingProxyModel::sourceRowsAboutToBeRemoved( const QModelIndex & parentIdx, int start, int end )
167 {
168  BASE::sourceRowsAboutToBeRemoved( parentIdx, start, end );
169  d->clearCache();
170 }
171 
172 
174 {
175  const QModelIndex sidx = mapToSource( idx );
176  const QAbstractItemModel* model = sourceModel();
177  Qt::ItemFlags f = model->flags( sidx );
178  if ( d->isSummary(sidx) ) {
179  f &= ~Qt::ItemIsEditable;
180  }
181  return f;
182 }
183 
184 
185 QVariant SummaryHandlingProxyModel::data( const QModelIndex& proxyIndex, int role) const
186 {
187  //qDebug() << "SummaryHandlingProxyModel::data("<<proxyIndex<<role<<")";
188  const QModelIndex sidx = mapToSource( proxyIndex );
189  const QAbstractItemModel* model = sourceModel();
190  if ( d->isSummary(sidx) && ( role==StartTimeRole || role==EndTimeRole )) {
191  //qDebug() << "requested summary";
193  if ( d->cacheLookup( sidx, &result ) ) {
194  //qDebug() << "SummaryHandlingProxyModel::data(): Looking up summary for " << proxyIndex << role;
195  switch ( role ) {
196  case StartTimeRole: return result.first;
197  case EndTimeRole: return result.second;
198  default: /* fall thru */;
199  }
200  } else {
201  d->insertInCache( this, sidx );
202  return data( proxyIndex, role ); /* TODO: Optimize */
203  }
204  }
205  return model->data( sidx, role );
206 }
207 
208 
209 bool SummaryHandlingProxyModel::setData( const QModelIndex& index, const QVariant& value, int role )
210 {
211  QAbstractItemModel* model = sourceModel();
212  if ( role==StartTimeRole || role==EndTimeRole ) {
213  QModelIndex parentIdx = mapToSource( index );
214  do {
215  if ( d->isSummary(parentIdx) ) {
216  //qDebug() << "removing " << parentIdx << "from cache";
217  d->removeFromCache( parentIdx );
218  QModelIndex proxyParentIdx = mapFromSource( parentIdx );
219  Q_EMIT dataChanged( proxyParentIdx, proxyParentIdx );
220  }
221  } while ( ( parentIdx=model->parent( parentIdx ) ) != QModelIndex() );
222  }
223  return BASE::setData( index, value, role );
224 }
225 
226 #undef d
227 
228 #ifndef KDAB_NO_UNIT_TESTS
229 
230 #include "unittest/test.h"
231 
232 #include <QStandardItemModel>
233 
234 static std::ostream& operator<<( std::ostream& os, const QDateTime& dt )
235 {
236 #ifdef QT_NO_STL
237  os << dt.toString().toLatin1().constData();
238 #else
239  os << dt.toString().toStdString();
240 #endif
241  return os;
242 }
243 
244 KDAB_SCOPED_UNITTEST_SIMPLE( KGantt, SummaryHandlingProxyModel, "test" ) {
247 
248  model.setSourceModel( &sourceModel );
249 
250  QStandardItem* topitem = new QStandardItem( QString::fromLatin1( "Summary" ) );
251  topitem->setData( KGantt::TypeSummary, KGantt::ItemTypeRole );
252  sourceModel.appendRow( topitem );
253 
254  QStandardItem* task1 = new QStandardItem( QString::fromLatin1( "Task1" ) );
255  task1->setData( KGantt::TypeTask, KGantt::ItemTypeRole );
256  QStandardItem* task2 = new QStandardItem( QString::fromLatin1( "Task2" ) );
257  task2->setData( KGantt::TypeTask, KGantt::ItemTypeRole );
258  topitem->appendRow( task1 );
259  topitem->appendRow( task2 );
260 
261 
263  QDateTime enddt = startdt.addDays( 1 );
264 
265 
266  task1->setData( startdt, KGantt::StartTimeRole );
267  task1->setData( enddt, KGantt::EndTimeRole );
268  task2->setData( startdt, KGantt::StartTimeRole );
269  task2->setData( enddt, KGantt::EndTimeRole );
270 
271  const QModelIndex topidx = model.index( 0, 0, QModelIndex() );
272 
273  assertEqual( model.data( topidx, KGantt::ItemTypeRole ).toInt(), KGantt::TypeSummary );
274  assertEqual( model.data( model.index( 0, 0, topidx ), KGantt::ItemTypeRole ).toInt(), KGantt::TypeTask );
275 
276  QDateTime task1startdt = model.data( model.index( 0, 0, topidx ), KGantt::StartTimeRole ).toDateTime();
277  assertEqual( task1startdt, startdt );
278 
279  QDateTime summarystartdt = model.data( topidx, KGantt::StartTimeRole ).toDateTime();
280  assertEqual( summarystartdt, startdt );
281  assertTrue( model.flags( model.index( 0, 0, topidx ) ) & Qt::ItemIsEditable );
282  assertFalse( model.flags( topidx ) & Qt::ItemIsEditable );
283 }
284 
285 #endif /* KDAB_NO_UNIT_TESTS */
286 
287 #include "moc_kganttsummaryhandlingproxymodel.cpp"
bool canConvert(int targetTypeId) const const
std::string toStdString() const const
QString toString(Qt::DateFormat format) const const
The item type.
Definition: kganttglobal.h:217
virtual int rowCount(const QModelIndex &parent) const const =0
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const =0
QDateTime toDateTime() const const
Start time (or other start value) for a gantt item.
Definition: kganttglobal.h:214
T value() const const
QHash::const_iterator constFind(const Key &key) const const
virtual void setData(const QVariant &value, int role)
QHash::const_iterator constEnd() const const
int toInt(bool *ok) const const
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector< int > &roles)
void appendRow(const QList< QStandardItem * > &items)
QVariant data(const QModelIndex &proxyIndex, int role=Qt::DisplayRole) const override
bool isEmpty() const const
End time (or other end value) for a gantt item.
Definition: kganttglobal.h:215
const char * constData() const const
virtual void setSourceModel(QAbstractItemModel *sourceModel)
virtual QVariant data(const QModelIndex &index, int role) const const =0
Global namespace.
virtual QModelIndex parent(const QModelIndex &index) const const =0
QCA_EXPORT void init()
QDataStream & operator<<(QDataStream &out, const KDateTime::Spec &spec)
QAbstractItemModel * sourceModel() const const
QDateTime currentDateTime()
QByteArray toLatin1() const const
bool isNull() const const
bool isValid(QStringView ifopt)
virtual bool setData(const QModelIndex &index, const QVariant &value, int role)
QString fromLatin1(const char *str, int size)
virtual Qt::ItemFlags flags(const QModelIndex &index) const const
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
QVariant::Type type() const const
QDateTime addDays(qint64 ndays) const const
Qt::ItemFlags flags(const QModelIndex &idx) const override
void setSourceModel(QAbstractItemModel *model) override
QString toString() const const
void appendRow(const QList< QStandardItem * > &items)
Q_EMITQ_EMIT
Proxy model that supports summary gantt items.
virtual bool setData(const QModelIndex &index, const QVariant &value, int role) override
typedef ItemFlags
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sun Dec 5 2021 22:32:42 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.