KD Chart API Documentation  3.1
kdganttsummaryhandlingproxymodel.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** This file is part of the KD Chart library.
4 **
5 ** SPDX-FileCopyrightText: 2001 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
6 **
7 ** SPDX-License-Identifier: MIT
8 **
9 ****************************************************************************/
10 
12 #include "kdganttsummaryhandlingproxymodel_p.h"
13 
14 #include <QDebug>
15 
16 #include <cassert>
17 
18 using namespace KDGantt;
19 
37 
38 bool SummaryHandlingProxyModel::Private::cacheLookup(const QModelIndex &idx,
39  QPair<QDateTime, QDateTime> *result) const
40 {
41  // qDebug() << "cacheLookup("<<idx<<"), cache has " << cached_summary_items.count() << "items";
42  QHash<QModelIndex, QPair<QDateTime, QDateTime>>::const_iterator it =
43  cached_summary_items.constFind(idx);
44  if (it != cached_summary_items.constEnd()) {
45  *result = *it;
46  return true;
47  } else {
48  return false;
49  }
50 }
51 
52 void SummaryHandlingProxyModel::Private::insertInCache(const SummaryHandlingProxyModel *model,
53  const QModelIndex &sourceIdx) const
54 {
55  QAbstractItemModel *sourceModel = model->sourceModel();
56  const QModelIndex &mainIdx = sourceIdx;
57  QDateTime st;
58  QDateTime et;
59 
60  for (int r = 0; r < sourceModel->rowCount(mainIdx); ++r) {
61  QModelIndex pdIdx = model->mapFromSource(sourceModel->index(r, 0, mainIdx));
62  /* The probably results in recursive calls here */
63  QVariant tmpsv = model->data(pdIdx, StartTimeRole);
64  QVariant tmpev = model->data(pdIdx, EndTimeRole);
65  if (!tmpsv.canConvert(QVariant::DateTime) || !tmpev.canConvert(QVariant::DateTime)) {
66  qDebug() << "Skipping item " << sourceIdx << " because it doesn't contain QDateTime";
67  continue;
68  }
69 
70  // check for valid datetimes
71  if (tmpsv.type() == QVariant::DateTime && !tmpsv.value<QDateTime>().isValid())
72  continue;
73  if (tmpev.type() == QVariant::DateTime && !tmpev.value<QDateTime>().isValid())
74  continue;
75 
76  // We need to test for empty strings to
77  // avoid a stupid Qt warning
78  if (tmpsv.type() == QVariant::String && tmpsv.value<QString>().isEmpty())
79  continue;
80  if (tmpev.type() == QVariant::String && tmpev.value<QString>().isEmpty())
81  continue;
82  QDateTime tmpst = tmpsv.toDateTime();
83  QDateTime tmpet = tmpev.toDateTime();
84  if (st.isNull() || st > tmpst)
85  st = tmpst;
86  if (et.isNull() || et < tmpet)
87  et = tmpet;
88  }
89  QVariant tmpssv = sourceModel->data(mainIdx, StartTimeRole);
90  QVariant tmpsev = sourceModel->data(mainIdx, EndTimeRole);
91  if (tmpssv.canConvert(QVariant::DateTime)
92  && !(tmpssv.canConvert(QVariant::String) && tmpssv.toString().isEmpty())
93  && tmpssv.toDateTime() != st)
94  sourceModel->setData(mainIdx, st, StartTimeRole);
95  if (tmpsev.canConvert(QVariant::DateTime)
96  && !(tmpsev.canConvert(QVariant::String) && tmpsev.toString().isEmpty())
97  && tmpsev.toDateTime() != et)
98  sourceModel->setData(mainIdx, et, EndTimeRole);
99  cached_summary_items[sourceIdx] = qMakePair(st, et);
100 }
101 
102 void SummaryHandlingProxyModel::Private::removeFromCache(const QModelIndex &idx) const
103 {
104  cached_summary_items.remove(idx);
105 }
106 
107 void SummaryHandlingProxyModel::Private::clearCache() const
108 {
109  cached_summary_items.clear();
110 }
111 
116  : BASE(parent)
117  , _d(new Private)
118 {
119  init();
120 }
121 
122 #define d d_func()
124 {
125  delete _d;
126 }
127 
128 void SummaryHandlingProxyModel::init()
129 {
130 }
131 
132 namespace {
133 
134 // Think this is ugly? Well, it's not from me, it comes from QProxyModel
135 struct KDPrivateModelIndex
136 {
137  int r, c;
138  void *p;
139  const QAbstractItemModel *m;
140 };
141 }
142 
147 void SummaryHandlingProxyModel::setSourceModel(QAbstractItemModel *model)
148 {
149  BASE::setSourceModel(model);
150  d->clearCache();
151 }
152 
154 {
155  d->clearCache();
156  BASE::sourceModelReset();
157 }
158 
160 {
161  d->clearCache();
162  BASE::sourceLayoutChanged();
163 }
164 
165 void SummaryHandlingProxyModel::sourceDataChanged(const QModelIndex &from, const QModelIndex &to)
166 {
167  QAbstractItemModel *model = sourceModel();
168  QModelIndex parentIdx = from;
169  do {
170  const QModelIndex &dataIdx = parentIdx;
171  if (model->data(dataIdx, ItemTypeRole) == TypeSummary) {
172  // qDebug() << "removing " << parentIdx << "from cache";
173  d->removeFromCache(dataIdx);
174  QModelIndex proxyDataIdx = mapFromSource(dataIdx);
175  Q_EMIT dataChanged(proxyDataIdx, proxyDataIdx);
176  }
177  } while ((parentIdx = model->parent(parentIdx)) != QModelIndex());
178 
179  BASE::sourceDataChanged(from, to);
180 }
181 
183  int start,
184  int end)
185 {
186  BASE::sourceColumnsAboutToBeInserted(parentIdx, start, end);
187  d->clearCache();
188 }
189 
191  int start,
192  int end)
193 {
194  BASE::sourceColumnsAboutToBeRemoved(parentIdx, start, end);
195  d->clearCache();
196 }
197 
198 void SummaryHandlingProxyModel::sourceRowsAboutToBeInserted(const QModelIndex &parentIdx, int start, int end)
199 {
200  BASE::sourceRowsAboutToBeInserted(parentIdx, start, end);
201  d->clearCache();
202 }
203 
204 void SummaryHandlingProxyModel::sourceRowsAboutToBeRemoved(const QModelIndex &parentIdx, int start, int end)
205 {
206  BASE::sourceRowsAboutToBeRemoved(parentIdx, start, end);
207  d->clearCache();
208 }
209 
211 Qt::ItemFlags SummaryHandlingProxyModel::flags(const QModelIndex &idx) const
212 {
213  const QModelIndex sidx = mapToSource(idx);
214  const QAbstractItemModel *model = sourceModel();
215  Qt::ItemFlags f = model->flags(sidx);
216  if (d->isSummary(sidx)) {
217  f &= ~Qt::ItemIsEditable;
218  }
219  return f;
220 }
221 
223 QVariant SummaryHandlingProxyModel::data(const QModelIndex &proxyIndex, int role) const
224 {
225  // qDebug() << "SummaryHandlingProxyModel::data("<<proxyIndex<<role<<")";
226  const QModelIndex sidx = mapToSource(proxyIndex);
227  const QAbstractItemModel *model = sourceModel();
228  if (d->isSummary(sidx) && (role == StartTimeRole || role == EndTimeRole)) {
229  // qDebug() << "requested summary";
230  QPair<QDateTime, QDateTime> result;
231  if (d->cacheLookup(sidx, &result)) {
232  // qDebug() << "SummaryHandlingProxyModel::data(): Looking up summary for " << proxyIndex << role;
233  switch (role) {
234  case StartTimeRole:
235  return result.first;
236  case EndTimeRole:
237  return result.second;
238  default: /* fall thru */;
239  }
240  } else {
241  d->insertInCache(this, sidx);
242  return data(proxyIndex, role); /* TODO: Optimize */
243  }
244  }
245  return model->data(sidx, role);
246 }
247 
249 bool SummaryHandlingProxyModel::setData(const QModelIndex &index, const QVariant &value, int role)
250 {
251  QAbstractItemModel *model = sourceModel();
252  if (role == StartTimeRole || role == EndTimeRole) {
253  QModelIndex parentIdx = mapToSource(index);
254  do {
255  if (d->isSummary(parentIdx)) {
256  // qDebug() << "removing " << parentIdx << "from cache";
257  d->removeFromCache(parentIdx);
258  QModelIndex proxyParentIdx = mapFromSource(parentIdx);
259  Q_EMIT dataChanged(proxyParentIdx, proxyParentIdx);
260  }
261  } while ((parentIdx = model->parent(parentIdx)) != QModelIndex());
262  }
263  return BASE::setData(index, value, role);
264 }
265 
266 #undef d
267 
268 #ifndef KDAB_NO_UNIT_TESTS
269 
270 #include "unittest/test.h"
271 
272 #include <QStandardItemModel>
273 
274 static std::ostream &operator<<(std::ostream &os, const QDateTime &dt)
275 {
276 #ifdef QT_NO_STL
277  os << dt.toString().toLatin1().constData();
278 #else
279  os << dt.toString().toStdString();
280 #endif
281  return os;
282 }
283 
285 {
287  QStandardItemModel sourceModel;
288 
289  model.setSourceModel(&sourceModel);
290 
291  auto *topitem = new QStandardItem(QString::fromLatin1("Summary"));
292  topitem->setData(KDGantt::TypeSummary, KDGantt::ItemTypeRole);
293  sourceModel.appendRow(topitem);
294 
295  auto *task1 = new QStandardItem(QString::fromLatin1("Task1"));
296  task1->setData(KDGantt::TypeTask, KDGantt::ItemTypeRole);
297  auto *task2 = new QStandardItem(QString::fromLatin1("Task2"));
298  task2->setData(KDGantt::TypeTask, KDGantt::ItemTypeRole);
299  topitem->appendRow(task1);
300  topitem->appendRow(task2);
301 
302  QDateTime startdt = QDateTime::currentDateTime();
303  QDateTime enddt = startdt.addDays(1);
304 
305  task1->setData(startdt, KDGantt::StartTimeRole);
306  task1->setData(enddt, KDGantt::EndTimeRole);
307  task2->setData(startdt, KDGantt::StartTimeRole);
308  task2->setData(enddt, KDGantt::EndTimeRole);
309 
310  const QModelIndex topidx = model.index(0, 0, QModelIndex());
311 
312  assertEqual(model.data(topidx, KDGantt::ItemTypeRole).toInt(), KDGantt::TypeSummary);
313  assertEqual(model.data(model.index(0, 0, topidx), KDGantt::ItemTypeRole).toInt(), KDGantt::TypeTask);
314 
315  QDateTime task1startdt = model.data(model.index(0, 0, topidx), KDGantt::StartTimeRole).toDateTime();
316  assertEqual(task1startdt, startdt);
317 
318  QDateTime summarystartdt = model.data(topidx, KDGantt::StartTimeRole).toDateTime();
319  assertEqual(summarystartdt, startdt);
320  assertTrue(model.flags(model.index(0, 0, topidx)) & Qt::ItemIsEditable);
321  assertFalse(model.flags(topidx) & Qt::ItemIsEditable);
322 }
323 
324 #endif /* KDAB_NO_UNIT_TESTS */
325 
326 #include "moc_kdganttsummaryhandlingproxymodel.cpp"
QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override
Converts indexes in the source model to indexes in the proxy model.
QModelIndex mapToSource(const QModelIndex &proxyIndex) const override
Converts indexes in the proxy model to indexes in the source model.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
Proxy model that supports summary gantt items.
Qt::ItemFlags flags(const QModelIndex &idx) const override
void sourceDataChanged(const QModelIndex &from, const QModelIndex &to) override
Called when the data in an existing item in the source model changes.
void sourceModelReset() override
Called when the source model is reset.
void sourceRowsAboutToBeInserted(const QModelIndex &idx, int start, int end) override
Called just before rows are inserted into the source model.
void sourceRowsAboutToBeRemoved(const QModelIndex &, int start, int end) override
Called just before rows are removed from the source model.
void sourceColumnsAboutToBeInserted(const QModelIndex &idx, int start, int end) override
Called just before columns are inserted into the source model.
void setSourceModel(QAbstractItemModel *model) override
Sets the model to be used as the source model for this proxy.
SummaryHandlingProxyModel(QObject *parent=nullptr)
Constructor.
void sourceLayoutChanged() override
Called when the layout of the source model has changed.
QVariant data(const QModelIndex &proxyIndex, int role=Qt::DisplayRole) const override
void sourceColumnsAboutToBeRemoved(const QModelIndex &idx, int start, int end) override
Called just before columns are removed from the source model.
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
QAbstractProxyModel BASE
KDAB_SCOPED_UNITTEST_SIMPLE(KDGantt, SummaryHandlingProxyModel, "test")
ForwardingProxyModel BASE
static std::ostream & operator<<(std::ostream &os, const QDateTime &dt)

© 2001 Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/
https://www.kdab.com/development-resources/qt-tools/kd-chart/
Generated by doxygen 1.9.1