KItemModels

kdescendantsproxymodel.cpp
1 /*
2  SPDX-FileCopyrightText: 2009 Stephen Kelly <[email protected]>
3  SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
4  SPDX-FileContributor: Stephen Kelly <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 
9 #include "kdescendantsproxymodel.h"
10 
11 #include <QStringList>
12 
13 #include "kbihash_p.h"
14 
15 typedef KHash2Map<QPersistentModelIndex, int> Mapping;
16 
17 class KDescendantsProxyModelPrivate
18 {
19  KDescendantsProxyModelPrivate(KDescendantsProxyModel *qq)
20  : q_ptr(qq),
21  m_rowCount(0),
22  m_ignoreNextLayoutAboutToBeChanged(false),
23  m_ignoreNextLayoutChanged(false),
24  m_relayouting(false),
25  m_displayAncestorData(false),
26  m_ancestorSeparator(QStringLiteral(" / "))
27  {
28  }
29 
30  Q_DECLARE_PUBLIC(KDescendantsProxyModel)
31  KDescendantsProxyModel *const q_ptr;
32 
33  mutable QVector<QPersistentModelIndex> m_pendingParents;
34 
35  void scheduleProcessPendingParents() const;
36  void processPendingParents();
37 
38  void synchronousMappingRefresh();
39 
40  void updateInternalIndexes(int start, int offset);
41 
42  void resetInternalData();
43 
44  void sourceRowsAboutToBeInserted(const QModelIndex &, int, int);
45  void sourceRowsInserted(const QModelIndex &, int, int);
46  void sourceRowsAboutToBeRemoved(const QModelIndex &, int, int);
47  void sourceRowsRemoved(const QModelIndex &, int, int);
48  void sourceRowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int);
49  void sourceRowsMoved(const QModelIndex &, int, int, const QModelIndex &, int);
50  void sourceModelAboutToBeReset();
51  void sourceModelReset();
52  void sourceLayoutAboutToBeChanged();
53  void sourceLayoutChanged();
54  void sourceDataChanged(const QModelIndex &, const QModelIndex &);
55  void sourceModelDestroyed();
56 
57  Mapping m_mapping;
58  int m_rowCount;
59  QPair<int, int> m_removePair;
60  QPair<int, int> m_insertPair;
61 
62  bool m_ignoreNextLayoutAboutToBeChanged;
63  bool m_ignoreNextLayoutChanged;
64  bool m_relayouting;
65 
66  bool m_displayAncestorData;
67  QString m_ancestorSeparator;
68 
69  QList<QPersistentModelIndex> m_layoutChangePersistentIndexes;
70  QModelIndexList m_proxyIndexes;
71 };
72 
73 void KDescendantsProxyModelPrivate::resetInternalData()
74 {
75  m_rowCount = 0;
76  m_mapping.clear();
77  m_layoutChangePersistentIndexes.clear();
78  m_proxyIndexes.clear();
79 }
80 
81 void KDescendantsProxyModelPrivate::synchronousMappingRefresh()
82 {
83  m_rowCount = 0;
84  m_mapping.clear();
85  m_pendingParents.clear();
86 
87  m_pendingParents.append(QModelIndex());
88 
89  m_relayouting = true;
90  while (!m_pendingParents.isEmpty()) {
91  processPendingParents();
92  }
93  m_relayouting = false;
94 }
95 
96 void KDescendantsProxyModelPrivate::scheduleProcessPendingParents() const
97 {
98  const_cast<KDescendantsProxyModelPrivate *>(this)->processPendingParents();
99 }
100 
101 void KDescendantsProxyModelPrivate::processPendingParents()
102 {
104  const QVector<QPersistentModelIndex>::iterator begin = m_pendingParents.begin();
106 
107  const QVector<QPersistentModelIndex>::iterator end = m_pendingParents.end();
108 
109  QVector<QPersistentModelIndex> newPendingParents;
110 
111  while (it != end && it != m_pendingParents.end()) {
112  const QModelIndex sourceParent = *it;
113  if (!sourceParent.isValid() && m_rowCount > 0) {
114  // It was removed from the source model before it was inserted.
115  it = m_pendingParents.erase(it);
116  continue;
117  }
118  const int rowCount = q->sourceModel()->rowCount(sourceParent);
119 
120  Q_ASSERT(rowCount > 0);
121  const QPersistentModelIndex sourceIndex = q->sourceModel()->index(rowCount - 1, 0, sourceParent);
122 
123  Q_ASSERT(sourceIndex.isValid());
124 
125  const QModelIndex proxyParent = q->mapFromSource(sourceParent);
126 
127  Q_ASSERT(sourceParent.isValid() == proxyParent.isValid());
128  const int proxyEndRow = proxyParent.row() + rowCount;
129  const int proxyStartRow = proxyEndRow - rowCount + 1;
130 
131  if (!m_relayouting) {
132  q->beginInsertRows(QModelIndex(), proxyStartRow, proxyEndRow);
133  }
134 
135  updateInternalIndexes(proxyStartRow, rowCount);
136  m_mapping.insert(sourceIndex, proxyEndRow);
137  it = m_pendingParents.erase(it);
138  m_rowCount += rowCount;
139 
140  if (!m_relayouting) {
141  q->endInsertRows();
142  }
143 
144  for (int sourceRow = 0; sourceRow < rowCount; ++sourceRow) {
145  static const int column = 0;
146  const QModelIndex child = q->sourceModel()->index(sourceRow, column, sourceParent);
147  Q_ASSERT(child.isValid());
148 
149  if (q->sourceModel()->hasChildren(child)) {
150  Q_ASSERT(q->sourceModel()->rowCount(child) > 0);
151  newPendingParents.append(child);
152  }
153  }
154  }
155  m_pendingParents += newPendingParents;
156  if (!m_pendingParents.isEmpty()) {
157  processPendingParents();
158  }
159 // scheduleProcessPendingParents();
160 }
161 
162 void KDescendantsProxyModelPrivate::updateInternalIndexes(int start, int offset)
163 {
164  // TODO: Make KHash2Map support key updates and do this backwards.
166  {
167  Mapping::right_iterator it = m_mapping.rightLowerBound(start);
168  const Mapping::right_iterator end = m_mapping.rightEnd();
169 
170  while (it != end) {
171  updates.insert(it.key() + offset, *it);
172  ++it;
173  }
174  }
175 
176  {
179 
180  for (; it != end; ++it) {
181  m_mapping.insert(it.value(), it.key());
182  }
183  }
184 
185 }
186 
188  : QAbstractProxyModel(parent), d_ptr(new KDescendantsProxyModelPrivate(this))
189 {
190 }
191 
193 {
194  delete d_ptr;
195 }
196 
197 #if KITEMMODELS_BUILD_DEPRECATED_SINCE(4, 8)
198 void KDescendantsProxyModel::setRootIndex(const QModelIndex &index)
199 {
200  Q_UNUSED(index)
201 }
202 #endif
203 
204 QModelIndexList KDescendantsProxyModel::match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const
205 {
206  return QAbstractProxyModel::match(start, role, value, hits, flags);
207 }
208 
209 namespace {
210  // we only work on DisplayRole for now
211  static const QVector<int> changedRoles = {Qt::DisplayRole};
212 }
213 
215 {
217  bool displayChanged = (display != d->m_displayAncestorData);
218  d->m_displayAncestorData = display;
219  if (displayChanged) {
220  Q_EMIT displayAncestorDataChanged();
221  // send out big hammer. Everything needs to be updated.
222  Q_EMIT dataChanged(index(0,0),index(rowCount()-1,columnCount()-1), changedRoles);
223  }
224 }
225 
227 {
228  Q_D(const KDescendantsProxyModel);
229  return d->m_displayAncestorData;
230 }
231 
233 {
235  bool separatorChanged = (separator != d->m_ancestorSeparator);
236  d->m_ancestorSeparator = separator;
237  if (separatorChanged) {
238  Q_EMIT ancestorSeparatorChanged();
239  if (d->m_displayAncestorData) {
240  // send out big hammer. Everything needs to be updated.
241  Q_EMIT dataChanged(index(0,0),index(rowCount()-1,columnCount()-1), changedRoles);
242  }
243  }
244 }
245 
247 {
248  Q_D(const KDescendantsProxyModel);
249  return d->m_ancestorSeparator;
250 }
251 
253 {
255 
256  beginResetModel();
257 
258  static const char *const modelSignals[] = {
259  SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
260  SIGNAL(rowsInserted(QModelIndex,int,int)),
261  SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
262  SIGNAL(rowsRemoved(QModelIndex,int,int)),
263  SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
264  SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
265  SIGNAL(modelAboutToBeReset()),
266  SIGNAL(modelReset()),
267  SIGNAL(dataChanged(QModelIndex,QModelIndex)),
268  SIGNAL(layoutAboutToBeChanged()),
269  SIGNAL(layoutChanged()),
270  SIGNAL(destroyed())
271  };
272  static const char *const proxySlots[] = {
273  SLOT(sourceRowsAboutToBeInserted(QModelIndex,int,int)),
274  SLOT(sourceRowsInserted(QModelIndex,int,int)),
275  SLOT(sourceRowsAboutToBeRemoved(QModelIndex,int,int)),
276  SLOT(sourceRowsRemoved(QModelIndex,int,int)),
277  SLOT(sourceRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
278  SLOT(sourceRowsMoved(QModelIndex,int,int,QModelIndex,int)),
279  SLOT(sourceModelAboutToBeReset()),
280  SLOT(sourceModelReset()),
281  SLOT(sourceDataChanged(QModelIndex,QModelIndex)),
282  SLOT(sourceLayoutAboutToBeChanged()),
283  SLOT(sourceLayoutChanged()),
284  SLOT(sourceModelDestroyed())
285  };
286 
287  if (sourceModel()) {
288  for (int i = 0; i < int(sizeof modelSignals / sizeof * modelSignals); ++i) {
289  disconnect(sourceModel(), modelSignals[i], this, proxySlots[i]);
290  }
291  }
292 
294 
295  if (_sourceModel) {
296  for (int i = 0; i < int(sizeof modelSignals / sizeof * modelSignals); ++i) {
297  connect(_sourceModel, modelSignals[i], this, proxySlots[i]);
298  }
299  }
300 
302  if (_sourceModel && _sourceModel->hasChildren()) {
303  d->synchronousMappingRefresh();
304  }
305 
306  endResetModel();
307  Q_EMIT sourceModelChanged();
308 }
309 
310 QModelIndex KDescendantsProxyModel::parent(const QModelIndex &index) const
311 {
312  Q_UNUSED(index)
313  return QModelIndex();
314 }
315 
316 bool KDescendantsProxyModel::hasChildren(const QModelIndex &parent) const
317 {
318  Q_D(const KDescendantsProxyModel);
319  return !(d->m_mapping.isEmpty() || parent.isValid());
320 }
321 
322 int KDescendantsProxyModel::rowCount(const QModelIndex &parent) const
323 {
324  Q_D(const KDescendantsProxyModel);
325  if (d->m_pendingParents.contains(parent) || parent.isValid() || !sourceModel()) {
326  return 0;
327  }
328 
329  if (d->m_mapping.isEmpty() && sourceModel()->hasChildren()) {
330  Q_ASSERT(sourceModel()->rowCount() > 0);
331  const_cast<KDescendantsProxyModelPrivate *>(d)->synchronousMappingRefresh();
332  }
333  return d->m_rowCount;
334 }
335 
336 QModelIndex KDescendantsProxyModel::index(int row, int column, const QModelIndex &parent) const
337 {
338  if (parent.isValid()) {
339  return QModelIndex();
340  }
341 
342  if (!hasIndex(row, column, parent)) {
343  return QModelIndex();
344  }
345 
346  return createIndex(row, column);
347 }
348 
349 QModelIndex KDescendantsProxyModel::mapToSource(const QModelIndex &proxyIndex) const
350 {
351  Q_D(const KDescendantsProxyModel);
352  if (d->m_mapping.isEmpty() || !proxyIndex.isValid() || !sourceModel()) {
353  return QModelIndex();
354  }
355 
356  const Mapping::right_const_iterator result = d->m_mapping.rightLowerBound(proxyIndex.row());
357  Q_ASSERT(result != d->m_mapping.rightEnd());
358 
359  const int proxyLastRow = result.key();
360  const QModelIndex sourceLastChild = result.value();
361  Q_ASSERT(sourceLastChild.isValid());
362 
363  // proxyLastRow is greater than proxyIndex.row().
364  // sourceLastChild is vertically below the result we're looking for
365  // and not necessarily in the correct parent.
366  // We travel up through its parent hierarchy until we are in the
367  // right parent, then return the correct sibling.
368 
369  // Source: Proxy: Row
370  // - A - A - 0
371  // - B - B - 1
372  // - C - C - 2
373  // - D - D - 3
374  // - - E - E - 4
375  // - - F - F - 5
376  // - - G - G - 6
377  // - - H - H - 7
378  // - - I - I - 8
379  // - - - J - J - 9
380  // - - - K - K - 10
381  // - - - L - L - 11
382  // - - M - M - 12
383  // - - N - N - 13
384  // - O - O - 14
385 
386  // Note that L, N and O are lastChildIndexes, and therefore have a mapping. If we
387  // are trying to map G from the proxy to the source, We at this point have an iterator
388  // pointing to (L -> 11). The proxy row of G is 6. (proxyIndex.row() == 6). We seek the
389  // sourceIndex which is vertically above L by the distance proxyLastRow - proxyIndex.row().
390  // In this case the verticalDistance is 5.
391 
392  int verticalDistance = proxyLastRow - proxyIndex.row();
393 
394  // We traverse the ancestors of L, until we can index the desired row in the source.
395 
396  QModelIndex ancestor = sourceLastChild;
397  while (ancestor.isValid()) {
398  const int ancestorRow = ancestor.row();
399  if (verticalDistance <= ancestorRow) {
400  return ancestor.sibling(ancestorRow - verticalDistance, proxyIndex.column());
401  }
402  verticalDistance -= (ancestorRow + 1);
403  ancestor = ancestor.parent();
404  }
405  Q_ASSERT(!"Didn't find target row.");
406  return QModelIndex();
407 }
408 
409 QModelIndex KDescendantsProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
410 {
411  Q_D(const KDescendantsProxyModel);
412 
413  if (!sourceModel()) {
414  return QModelIndex();
415  }
416 
417  if (d->m_mapping.isEmpty()) {
418  return QModelIndex();
419  }
420 
421  {
422  // TODO: Consider a parent Mapping to speed this up.
423 
424  Mapping::right_const_iterator it = d->m_mapping.rightConstBegin();
425  const Mapping::right_const_iterator end = d->m_mapping.rightConstEnd();
426  const QModelIndex sourceParent = sourceIndex.parent();
427  Mapping::right_const_iterator result = end;
428 
429  for (; it != end; ++it) {
430  QModelIndex index = it.value();
431  bool found_block = false;
432  while (index.isValid()) {
433  const QModelIndex ancestor = index.parent();
434  if (ancestor == sourceParent && index.row() >= sourceIndex.row()) {
435  found_block = true;
436  if (result == end || it.key() < result.key()) {
437  result = it;
438  break; // Leave the while loop. index is still valid.
439  }
440  }
441  index = ancestor;
442  }
443  if (found_block && !index.isValid())
444  // Looked through the ascendants of it.key() without finding sourceParent.
445  // That means we've already got the result we need.
446  {
447  break;
448  }
449  }
450  Q_ASSERT(result != end);
451  const QModelIndex sourceLastChild = result.value();
452  int proxyRow = result.key();
453  QModelIndex index = sourceLastChild;
454  while (index.isValid()) {
455  const QModelIndex ancestor = index.parent();
456  if (ancestor == sourceParent) {
457  return createIndex(proxyRow - (index.row() - sourceIndex.row()), sourceIndex.column());
458  }
459  proxyRow -= (index.row() + 1);
460  index = ancestor;
461  }
462  Q_ASSERT(!"Didn't find valid proxy mapping.");
463  return QModelIndex();
464  }
465 
466 }
467 
468 int KDescendantsProxyModel::columnCount(const QModelIndex &parent) const
469 {
470  if (parent.isValid() /* || rowCount(parent) == 0 */ || !sourceModel()) {
471  return 0;
472  }
473 
474  return sourceModel()->columnCount();
475 }
476 
477 QVariant KDescendantsProxyModel::data(const QModelIndex &index, int role) const
478 {
479  Q_D(const KDescendantsProxyModel);
480 
481  if (!sourceModel()) {
482  return QVariant();
483  }
484 
485  if (!index.isValid()) {
486  return sourceModel()->data(index, role);
487  }
488 
489  QModelIndex sourceIndex = mapToSource(index);
490 
491  if ((d->m_displayAncestorData) && (role == Qt::DisplayRole)) {
492  if (!sourceIndex.isValid()) {
493  return QVariant();
494  }
495  QString displayData = sourceIndex.data().toString();
496  sourceIndex = sourceIndex.parent();
497  while (sourceIndex.isValid()) {
498  displayData.prepend(d->m_ancestorSeparator);
499  displayData.prepend(sourceIndex.data().toString());
500  sourceIndex = sourceIndex.parent();
501  }
502  return displayData;
503  } else {
504  return sourceIndex.data(role);
505  }
506 }
507 
508 QVariant KDescendantsProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
509 {
510  if (!sourceModel() || columnCount() <= section) {
511  return QVariant();
512  }
513 
514  return QAbstractProxyModel::headerData(section, orientation, role);
515 }
516 
517 Qt::ItemFlags KDescendantsProxyModel::flags(const QModelIndex &index) const
518 {
519  if (!index.isValid() || !sourceModel()) {
520  return QAbstractProxyModel::flags(index);
521  }
522 
523  const QModelIndex srcIndex = mapToSource(index);
524  Q_ASSERT(srcIndex.isValid());
525  return sourceModel()->flags(srcIndex);
526 }
527 
528 void KDescendantsProxyModelPrivate::sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
529 {
531 
532  if (!q->sourceModel()->hasChildren(parent)) {
533  Q_ASSERT(q->sourceModel()->rowCount(parent) == 0);
534  // parent was not a parent before.
535  return;
536  }
537 
538  int proxyStart = -1;
539 
540  const int rowCount = q->sourceModel()->rowCount(parent);
541 
542  if (rowCount > start) {
543  const QModelIndex belowStart = q->sourceModel()->index(start, 0, parent);
544  proxyStart = q->mapFromSource(belowStart).row();
545  } else if (rowCount == 0) {
546  proxyStart = q->mapFromSource(parent).row() + 1;
547  } else {
548  Q_ASSERT(rowCount == start);
549  static const int column = 0;
550  QModelIndex idx = q->sourceModel()->index(rowCount - 1, column, parent);
551  while (q->sourceModel()->hasChildren(idx)) {
552  Q_ASSERT(q->sourceModel()->rowCount(idx) > 0);
553  idx = q->sourceModel()->index(q->sourceModel()->rowCount(idx) - 1, column, idx);
554  }
555  // The last item in the list is getting a sibling below it.
556  proxyStart = q->mapFromSource(idx).row() + 1;
557  }
558  const int proxyEnd = proxyStart + (end - start);
559 
560  m_insertPair = qMakePair(proxyStart, proxyEnd);
561  q->beginInsertRows(QModelIndex(), proxyStart, proxyEnd);
562 }
563 
564 void KDescendantsProxyModelPrivate::sourceRowsInserted(const QModelIndex &parent, int start, int end)
565 {
567 
568  Q_ASSERT(q->sourceModel()->index(start, 0, parent).isValid());
569 
570  const int rowCount = q->sourceModel()->rowCount(parent);
571  Q_ASSERT(rowCount > 0);
572 
573  const int difference = end - start + 1;
574 
575  if (rowCount == difference) {
576  // @p parent was not a parent before.
577  m_pendingParents.append(parent);
578  scheduleProcessPendingParents();
579  return;
580  }
581 
582  const int proxyStart = m_insertPair.first;
583 
584  Q_ASSERT(proxyStart >= 0);
585 
586  updateInternalIndexes(proxyStart, difference);
587 
588  if (rowCount - 1 == end) {
589  // The previously last row (the mapped one) is no longer the last.
590  // For example,
591 
592  // - A - A 0
593  // - - B - B 1
594  // - - C - C 2
595  // - - - D - D 3
596  // - - - E -> - E 4
597  // - - F - F 5
598  // - - G -> - G 6
599  // - H - H 7
600  // - I -> - I 8
601 
602  // As last children, E, F and G have mappings.
603  // Consider that 'J' is appended to the children of 'C', below 'E'.
604 
605  // - A - A 0
606  // - - B - B 1
607  // - - C - C 2
608  // - - - D - D 3
609  // - - - E -> - E 4
610  // - - - J - ??? 5
611  // - - F - F 6
612  // - - G -> - G 7
613  // - H - H 8
614  // - I -> - I 9
615 
616  // The updateInternalIndexes call above will have updated the F and G mappings correctly because proxyStart is 5.
617  // That means that E -> 4 was not affected by the updateInternalIndexes call.
618  // Now the mapping for E -> 4 needs to be updated so that it's a mapping for J -> 5.
619 
620  Q_ASSERT(!m_mapping.isEmpty());
621  static const int column = 0;
622  const QModelIndex oldIndex = q->sourceModel()->index(rowCount - 1 - difference, column, parent);
623  Q_ASSERT(m_mapping.leftContains(oldIndex));
624 
625  const QModelIndex newIndex = q->sourceModel()->index(rowCount - 1, column, parent);
626 
627  QModelIndex indexAbove = oldIndex;
628 
629  if (start > 0) {
630  // If we have something like this:
631  //
632  // - A
633  // - - B
634  // - - C
635  //
636  // and we then insert D as a sibling of A below it, we need to remove the mapping for A,
637  // and the row number used for D must take into account the descendants of A.
638 
639  while (q->sourceModel()->hasChildren(indexAbove)) {
640  Q_ASSERT(q->sourceModel()->rowCount(indexAbove) > 0);
641  indexAbove = q->sourceModel()->index(q->sourceModel()->rowCount(indexAbove) - 1, column, indexAbove);
642  }
643  Q_ASSERT(q->sourceModel()->rowCount(indexAbove) == 0);
644  }
645 
646  Q_ASSERT(m_mapping.leftContains(indexAbove));
647 
648  const int newProxyRow = m_mapping.leftToRight(indexAbove) + difference;
649 
650  // oldIndex is E in the source. proxyRow is 4.
651  m_mapping.removeLeft(oldIndex);
652 
653  // newIndex is J. (proxyRow + difference) is 5.
654  m_mapping.insert(newIndex, newProxyRow);
655  }
656 
657  for (int row = start; row <= end; ++row) {
658  static const int column = 0;
659  const QModelIndex idx = q->sourceModel()->index(row, column, parent);
660  Q_ASSERT(idx.isValid());
661  if (q->sourceModel()->hasChildren(idx)) {
662  Q_ASSERT(q->sourceModel()->rowCount(idx) > 0);
663  m_pendingParents.append(idx);
664  }
665  }
666 
667  m_rowCount += difference;
668 
669  q->endInsertRows();
670  scheduleProcessPendingParents();
671 }
672 
673 void KDescendantsProxyModelPrivate::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
674 {
676 
677  const int proxyStart = q->mapFromSource(q->sourceModel()->index(start, 0, parent)).row();
678 
679  static const int column = 0;
680  QModelIndex idx = q->sourceModel()->index(end, column, parent);
681  while (q->sourceModel()->hasChildren(idx)) {
682  Q_ASSERT(q->sourceModel()->rowCount(idx) > 0);
683  idx = q->sourceModel()->index(q->sourceModel()->rowCount(idx) - 1, column, idx);
684  }
685  const int proxyEnd = q->mapFromSource(idx).row();
686 
687  m_removePair = qMakePair(proxyStart, proxyEnd);
688 
689  q->beginRemoveRows(QModelIndex(), proxyStart, proxyEnd);
690 }
691 
692 static QModelIndex getFirstDeepest(QAbstractItemModel *model, const QModelIndex &parent, int *count)
693 {
694  static const int column = 0;
695  Q_ASSERT(model->hasChildren(parent));
696  Q_ASSERT(model->rowCount(parent) > 0);
697  for (int row = 0; row < model->rowCount(parent); ++row) {
698  (*count)++;
699  const QModelIndex child = model->index(row, column, parent);
700  Q_ASSERT(child.isValid());
701  if (model->hasChildren(child)) {
702  return getFirstDeepest(model, child, count);
703  }
704  }
705  return model->index(model->rowCount(parent) - 1, column, parent);
706 }
707 
708 void KDescendantsProxyModelPrivate::sourceRowsRemoved(const QModelIndex &parent, int start, int end)
709 {
711  Q_UNUSED(end)
712 
713  const int rowCount = q->sourceModel()->rowCount(parent);
714 
715  const int proxyStart = m_removePair.first;
716  const int proxyEnd = m_removePair.second;
717 
718  const int difference = proxyEnd - proxyStart + 1;
719  {
720  Mapping::right_iterator it = m_mapping.rightLowerBound(proxyStart);
721  const Mapping::right_iterator endIt = m_mapping.rightUpperBound(proxyEnd);
722 
723  if (endIt != m_mapping.rightEnd())
724  while (it != endIt) {
725  it = m_mapping.eraseRight(it);
726  }
727  else
728  while (it != m_mapping.rightUpperBound(proxyEnd)) {
729  it = m_mapping.eraseRight(it);
730  }
731  }
732 
733  m_removePair = qMakePair(-1, -1);
734  m_rowCount -= difference;
735  Q_ASSERT(m_rowCount >= 0);
736 
737  updateInternalIndexes(proxyStart, -1 * difference);
738 
739  if (rowCount != start || rowCount == 0) {
740  q->endRemoveRows();
741  return;
742  }
743 
744  static const int column = 0;
745  const QModelIndex newEnd = q->sourceModel()->index(rowCount - 1, column, parent);
746  Q_ASSERT(newEnd.isValid());
747 
748  if (m_mapping.isEmpty()) {
749  m_mapping.insert(newEnd, newEnd.row());
750  q->endRemoveRows();
751  return;
752  }
753  if (q->sourceModel()->hasChildren(newEnd)) {
754  int count = 0;
755  const QModelIndex firstDeepest = getFirstDeepest(q->sourceModel(), newEnd, &count);
756  Q_ASSERT(firstDeepest.isValid());
757  const int firstDeepestProxy = m_mapping.leftToRight(firstDeepest);
758 
759  m_mapping.insert(newEnd, firstDeepestProxy - count);
760  q->endRemoveRows();
761  return;
762  }
763  Mapping::right_iterator lowerBound = m_mapping.rightLowerBound(proxyStart);
764  if (lowerBound == m_mapping.rightEnd()) {
765  int proxyRow = (lowerBound - 1).key();
766 
767  for (int row = newEnd.row(); row >= 0; --row) {
768  const QModelIndex newEndSibling = q->sourceModel()->index(row, column, parent);
769  if (!q->sourceModel()->hasChildren(newEndSibling)) {
770  ++proxyRow;
771  } else {
772  break;
773  }
774  }
775  m_mapping.insert(newEnd, proxyRow);
776  q->endRemoveRows();
777  return;
778  } else if (lowerBound == m_mapping.rightBegin()) {
779  int proxyRow = rowCount - 1;
780  QModelIndex trackedParent = parent;
781  while (trackedParent.isValid()) {
782  proxyRow += (trackedParent.row() + 1);
783  trackedParent = trackedParent.parent();
784  }
785  m_mapping.insert(newEnd, proxyRow);
786  q->endRemoveRows();
787  return;
788  }
789  const Mapping::right_iterator boundAbove = lowerBound - 1;
790 
791  QVector<QModelIndex> targetParents;
792  targetParents.push_back(parent);
793  {
794  QModelIndex target = parent;
795  int count = 0;
796  while (target.isValid()) {
797  if (target == boundAbove.value()) {
798  m_mapping.insert(newEnd, count + boundAbove.key() + newEnd.row() + 1);
799  q->endRemoveRows();
800  return;
801  }
802  count += (target.row() + 1);
803  target = target.parent();
804  if (target.isValid()) {
805  targetParents.push_back(target);
806  }
807  }
808  }
809 
810  QModelIndex boundParent = boundAbove.value().parent();
811  QModelIndex prevParent = boundParent;
812  Q_ASSERT(boundParent.isValid());
813  while (boundParent.isValid()) {
814  prevParent = boundParent;
815  boundParent = boundParent.parent();
816 
817  if (targetParents.contains(prevParent)) {
818  break;
819  }
820 
821  if (!m_mapping.leftContains(prevParent)) {
822  break;
823  }
824 
825  if (m_mapping.leftToRight(prevParent) > boundAbove.key()) {
826  break;
827  }
828  }
829 
830  QModelIndex trackedParent = parent;
831 
832  int proxyRow = boundAbove.key();
833 
834  Q_ASSERT(prevParent.isValid());
835  proxyRow -= prevParent.row();
836  while (trackedParent != boundParent) {
837  proxyRow += (trackedParent.row() + 1);
838  trackedParent = trackedParent.parent();
839  }
840  m_mapping.insert(newEnd, proxyRow + newEnd.row());
841  q->endRemoveRows();
842 }
843 
844 void KDescendantsProxyModelPrivate::sourceRowsAboutToBeMoved(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destStart)
845 {
846  Q_UNUSED(srcParent)
847  Q_UNUSED(srcStart)
848  Q_UNUSED(srcEnd)
849  Q_UNUSED(destParent)
850  Q_UNUSED(destStart)
851  sourceLayoutAboutToBeChanged();
852 }
853 
854 void KDescendantsProxyModelPrivate::sourceRowsMoved(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destStart)
855 {
856  Q_UNUSED(srcParent)
857  Q_UNUSED(srcStart)
858  Q_UNUSED(srcEnd)
859  Q_UNUSED(destParent)
860  Q_UNUSED(destStart)
861  sourceLayoutChanged();
862 }
863 
864 void KDescendantsProxyModelPrivate::sourceModelAboutToBeReset()
865 {
867  q->beginResetModel();
868 }
869 
870 void KDescendantsProxyModelPrivate::sourceModelReset()
871 {
874  if (q->sourceModel()->hasChildren()) {
875  Q_ASSERT(q->sourceModel()->rowCount() > 0);
876  m_pendingParents.append(QModelIndex());
877  scheduleProcessPendingParents();
878  }
879  q->endResetModel();
880 }
881 
882 void KDescendantsProxyModelPrivate::sourceLayoutAboutToBeChanged()
883 {
885 
886  if (m_ignoreNextLayoutChanged) {
887  m_ignoreNextLayoutChanged = false;
888  return;
889  }
890 
891  if (m_mapping.isEmpty()) {
892  return;
893  }
894 
895  Q_EMIT q->layoutAboutToBeChanged();
896 
897  QPersistentModelIndex srcPersistentIndex;
898  const auto lst = q->persistentIndexList();
899  for (const QPersistentModelIndex &proxyPersistentIndex : lst) {
900  m_proxyIndexes << proxyPersistentIndex;
901  Q_ASSERT(proxyPersistentIndex.isValid());
902  srcPersistentIndex = q->mapToSource(proxyPersistentIndex);
903  Q_ASSERT(srcPersistentIndex.isValid());
904  m_layoutChangePersistentIndexes << srcPersistentIndex;
905  }
906 }
907 
908 void KDescendantsProxyModelPrivate::sourceLayoutChanged()
909 {
911 
912  if (m_ignoreNextLayoutAboutToBeChanged) {
913  m_ignoreNextLayoutAboutToBeChanged = false;
914  return;
915  }
916 
917  if (m_mapping.isEmpty()) {
918  return;
919  }
920 
921  m_rowCount = 0;
922 
923  synchronousMappingRefresh();
924 
925  for (int i = 0; i < m_proxyIndexes.size(); ++i) {
926  q->changePersistentIndex(m_proxyIndexes.at(i), q->mapFromSource(m_layoutChangePersistentIndexes.at(i)));
927  }
928 
929  m_layoutChangePersistentIndexes.clear();
930  m_proxyIndexes.clear();
931 
932  Q_EMIT q->layoutChanged();
933 }
934 
935 void KDescendantsProxyModelPrivate::sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
936 {
938  Q_ASSERT(topLeft.model() == q->sourceModel());
939  Q_ASSERT(bottomRight.model() == q->sourceModel());
940 
941  const int topRow = topLeft.row();
942  const int bottomRow = bottomRight.row();
943 
944  for (int i = topRow; i <= bottomRow; ++i) {
945  const QModelIndex sourceTopLeft = q->sourceModel()->index(i, topLeft.column(), topLeft.parent());
946  Q_ASSERT(sourceTopLeft.isValid());
947  const QModelIndex proxyTopLeft = q->mapFromSource(sourceTopLeft);
948  // TODO. If an index does not have any descendants, then we can emit in blocks of rows.
949  // As it is we emit once for each row.
950  const QModelIndex sourceBottomRight = q->sourceModel()->index(i, bottomRight.column(), bottomRight.parent());
951  const QModelIndex proxyBottomRight = q->mapFromSource(sourceBottomRight);
952  Q_ASSERT(proxyTopLeft.isValid());
953  Q_ASSERT(proxyBottomRight.isValid());
954  Q_EMIT q->dataChanged(proxyTopLeft, proxyBottomRight);
955  }
956 }
957 
958 void KDescendantsProxyModelPrivate::sourceModelDestroyed()
959 {
961 }
962 
963 QMimeData *KDescendantsProxyModel::mimeData(const QModelIndexList &indexes) const
964 {
965  if (!sourceModel()) {
966  return QAbstractProxyModel::mimeData(indexes);
967  }
968  Q_ASSERT(sourceModel());
969  QModelIndexList sourceIndexes;
970  for (const QModelIndex &index : indexes) {
971  sourceIndexes << mapToSource(index);
972  }
973  return sourceModel()->mimeData(sourceIndexes);
974 }
975 
976 QStringList KDescendantsProxyModel::mimeTypes() const
977 {
978  if (!sourceModel()) {
980  }
981  Q_ASSERT(sourceModel());
982  return sourceModel()->mimeTypes();
983 }
984 
985 Qt::DropActions KDescendantsProxyModel::supportedDropActions() const
986 {
987  if (!sourceModel()) {
989  }
990  return sourceModel()->supportedDropActions();
991 }
992 
993 #include "moc_kdescendantsproxymodel.cpp"
bool hasIndex(int row, int column, const QModelIndex &parent) const const
void setRootIndex(const QModelIndex &index)
virtual int rowCount(const QModelIndex &parent) const const =0
QHash::iterator insert(const Key &key, const T &value)
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const =0
void layoutChanged(const QList< QPersistentModelIndex > &parents, QAbstractItemModel::LayoutChangeHint hint)
typedef MatchFlags
Proxy Model for restructuring a Tree into a list.
void append(const T &value)
virtual Qt::DropActions supportedDropActions() const const override
QString & prepend(QChar ch)
QVector::iterator erase(QVector::iterator begin, QVector::iterator end)
void modelAboutToBeReset()
virtual QMimeData * mimeData(const QModelIndexList &indexes) const const override
void rowsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
virtual QStringList mimeTypes() const const
const QList< QKeySequence > & begin()
virtual QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const const
void layoutAboutToBeChanged(const QList< QPersistentModelIndex > &parents, QAbstractItemModel::LayoutChangeHint hint)
bool isValid() const const
void setAncestorSeparator(const QString &separator)
Sets the ancestor separator used between data of ancestors.
bool isValid() const const
void rowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row)
bool contains(const T &value) const const
void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
~KDescendantsProxyModel() override
Destroys the descendant entities proxy model.
QHash::const_iterator constEnd() const const
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector< int > &roles)
DisplayRole
int row() const const
QString ancestorSeparator() const
Separator used between data of ancestors.
virtual Qt::DropActions supportedDropActions() const const
virtual void setSourceModel(QAbstractItemModel *sourceModel)
virtual QVariant data(const QModelIndex &index, int role) const const =0
virtual QMimeData * mimeData(const QModelIndexList &indexes) const const
QModelIndex parent() const const
void rowsRemoved(const QModelIndex &parent, int first, int last)
virtual QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits=1, Qt::MatchFlags flags=Qt::MatchFlags(Qt::MatchStartsWith|Qt::MatchWrap)) const override
Reimplemented to match all descendants.
QModelIndex createIndex(int row, int column, void *ptr) const const
KDescendantsProxyModel(QObject *parent=nullptr)
Creates a new descendant entities proxy model.
virtual QStringList mimeTypes() const const override
const QList< QKeySequence > & end()
QHash::const_iterator constBegin() const const
QAbstractItemModel * sourceModel() const const
const QAbstractItemModel * model() const const
QVariant data(int role) const const
void setSourceModel(QAbstractItemModel *model) override
Sets the source model of the proxy.
virtual bool hasChildren(const QModelIndex &parent) const const
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const const override
QModelIndex sibling(int row, int column) const const
virtual Qt::ItemFlags flags(const QModelIndex &index) const const override
virtual int columnCount(const QModelIndex &parent) const const =0
int column() const const
void setDisplayAncestorData(bool display)
Set whether to show ancestor data in the model.
void push_back(const T &value)
virtual Qt::ItemFlags flags(const QModelIndex &index) const const
Orientation
void rowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow)
void rowsInserted(const QModelIndex &parent, int first, int last)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const const
typedef DropActions
QString toString() const const
QVector::iterator end()
bool displayAncestorData() const
Whether ancestor data will be displayed.
void destroyed(QObject *obj)
int row() const const
Q_EMITQ_EMIT
typedef ItemFlags
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Aug 3 2020 22:48:50 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.