1 /***************************************************************************
2 * Copyright (C) 2005-2018 by the Quassel Project *
3 * devel@quassel-irc.org *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) version 3. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
21 #include "flatproxymodel.h"
23 #include <QItemSelection>
26 FlatProxyModel::FlatProxyModel(QObject *parent)
27 : QAbstractProxyModel(parent)
32 QModelIndex FlatProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
34 if (!sourceIndex.isValid())
37 SourceItem *sourceItem = sourceToInternal(sourceIndex);
39 return createIndex(sourceItem->pos(), sourceIndex.column(), sourceItem);
43 QModelIndex FlatProxyModel::mapToSource(const QModelIndex &proxyIndex) const
45 if (!proxyIndex.isValid())
48 Q_ASSERT(proxyIndex.model() == this);
49 Q_ASSERT(_rootSourceItem);
51 int row = proxyIndex.row();
52 QModelIndex sourceParent;
53 SourceItem *sourceItem = _rootSourceItem->findChild(row);
55 if (sourceItem->pos() == row) {
56 return sourceModel()->index(sourceItem->sourceRow(), proxyIndex.column(), sourceParent);
59 sourceParent = sourceModel()->index(sourceItem->sourceRow(), 0, sourceParent);
60 sourceItem = sourceItem->findChild(row);
64 qWarning() << "FlatProxyModel::mapToSource(): couldn't find source index for" << proxyIndex;
66 return {}; // make compilers happy :)
70 bool FlatProxyModel::_RangeRect::operator<(const FlatProxyModel::_RangeRect &other) const
72 if (left != other.left)
73 return left < other.left;
75 if (right != other.right)
76 return right < other.right;
79 return top < other.top;
82 return bottom < other.bottom;
86 QItemSelection FlatProxyModel::mapSelectionFromSource(const QItemSelection &sourceSelection) const
88 QList<_RangeRect> newRanges;
89 QHash<QModelIndex, SourceItem *> itemLookup;
90 // basics steps of the loop:
91 // 1. convert each source ItemSelectionRange to a mapped Range (internal one for easier post processing)
92 // 2. insert it into the list of previously mapped Ranges sorted by top location
93 for (int i = 0; i < sourceSelection.count(); i++) {
94 const QItemSelectionRange ¤tRange = sourceSelection[i];
95 QModelIndex currentParent = currentRange.topLeft().parent();
96 Q_ASSERT(currentParent == currentRange.bottomRight().parent());
98 SourceItem *parentItem = nullptr;
99 if (!itemLookup.contains(currentParent)) {
100 parentItem = sourceToInternal(currentParent);
101 itemLookup[currentParent] = parentItem;
104 parentItem = itemLookup[currentParent];
107 _RangeRect newRange = { currentRange.topLeft().column(), currentRange.bottomRight().column(),
108 currentRange.topLeft().row(), currentRange.bottomRight().row(),
109 parentItem->child(currentRange.topLeft().row()), parentItem->child(currentRange.bottomRight().row()) };
111 if (newRanges.isEmpty()) {
112 newRanges << newRange;
116 _RangeRect &first = newRanges[0];
117 if (newRange < first) {
118 newRanges.prepend(newRange);
122 bool inserted = false;
123 for (int j = 0; j < newRanges.count() - 1; j++) {
124 _RangeRect &a = newRanges[j];
125 _RangeRect &b = newRanges[j + 1];
127 if (a < newRange && newRange < b) {
128 newRanges[j + 1] = newRange;
136 _RangeRect &last = newRanges[newRanges.count() - 1];
137 if (last < newRange) {
138 newRanges.append(newRange);
145 // we've got a sorted list of ranges now. so we can easily check if there is a possibility to merge
146 for (int i = newRanges.count() - 1; i > 0; i--) {
147 _RangeRect &a = newRanges[i - 1];
148 _RangeRect &b = newRanges[i];
149 if (a.left != b.left || a.right != b.right)
152 if (a.bottom < b.top - 1) {
156 // all merge checks passed!
157 if (b.bottom > a.bottom) {
159 a.bottomItem = b.bottomItem;
160 } // otherwise b is totally enclosed in a -> nothing to do but drop b.
161 newRanges.removeAt(i);
164 QItemSelection proxySelection;
165 for (int i = 0; i < newRanges.count(); i++) {
166 _RangeRect &r = newRanges[i];
167 proxySelection << QItemSelectionRange(createIndex(r.top, r.left, r.topItem), createIndex(r.bottom, r.right, r.bottomItem));
169 return proxySelection;
173 QItemSelection FlatProxyModel::mapSelectionToSource(const QItemSelection &proxySelection) const
175 QItemSelection sourceSelection;
177 for (int i = 0; i < proxySelection.count(); i++) {
178 const QItemSelectionRange &range = proxySelection[i];
180 SourceItem *topLeftItem = nullptr;
181 SourceItem *bottomRightItem = nullptr;
182 auto *currentItem = static_cast<SourceItem *>(range.topLeft().internalPointer());
183 int row = range.topLeft().row();
184 int left = range.topLeft().column();
185 int right = range.bottomRight().column();
186 while (currentItem && row <= range.bottomRight().row()) {
187 Q_ASSERT(currentItem->pos() == row);
189 topLeftItem = currentItem;
191 if (currentItem->parent() == topLeftItem->parent()) {
192 bottomRightItem = currentItem;
195 Q_ASSERT(topLeftItem && bottomRightItem);
196 sourceSelection << QItemSelectionRange(mapToSource(createIndex(topLeftItem->pos(), left, topLeftItem)), mapToSource(createIndex(bottomRightItem->pos(), right, bottomRightItem)));
197 topLeftItem = nullptr;
198 bottomRightItem = nullptr;
202 currentItem = currentItem->next();
206 if (topLeftItem && bottomRightItem) { // there should be one range left.
207 sourceSelection << QItemSelectionRange(mapToSource(createIndex(topLeftItem->pos(), left, topLeftItem)), mapToSource(createIndex(bottomRightItem->pos(), right, bottomRightItem)));
211 return sourceSelection;
215 void FlatProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
217 if (QAbstractProxyModel::sourceModel()) {
218 disconnect(QAbstractProxyModel::sourceModel(), nullptr, this, nullptr);
221 QAbstractProxyModel::setSourceModel(sourceModel);
223 emit layoutAboutToBeChanged();
224 removeSubTree(QModelIndex(), false /* don't emit removeRows() */);
225 insertSubTree(QModelIndex(), false /* don't emit insertRows() */);
226 emit layoutChanged();
229 connect(sourceModel, &QAbstractItemModel::columnsAboutToBeInserted,
230 this, &FlatProxyModel::on_columnsAboutToBeInserted);
231 connect(sourceModel, &QAbstractItemModel::columnsAboutToBeRemoved,
232 this, &FlatProxyModel::on_columnsAboutToBeRemoved);
233 connect(sourceModel, &QAbstractItemModel::columnsInserted,
234 this, &FlatProxyModel::on_columnsInserted);
235 connect(sourceModel, &QAbstractItemModel::columnsRemoved,
236 this, &FlatProxyModel::on_columnsRemoved);
238 connect(sourceModel, &QAbstractItemModel::dataChanged,
239 this, &FlatProxyModel::on_dataChanged);
240 // on_headerDataChanged(Qt::Orientation orientation, int first, int last)
242 connect(sourceModel, &QAbstractItemModel::layoutAboutToBeChanged,
243 this, &FlatProxyModel::on_layoutAboutToBeChanged);
244 connect(sourceModel, &QAbstractItemModel::layoutChanged,
245 this, &FlatProxyModel::on_layoutChanged);
247 connect(sourceModel, &QAbstractItemModel::modelAboutToBeReset,
248 this, &FlatProxyModel::on_modelAboutToBeReset);
249 // void on_modelReset()
251 connect(sourceModel, &QAbstractItemModel::rowsAboutToBeInserted,
252 this, &FlatProxyModel::on_rowsAboutToBeInserted);
253 connect(sourceModel, &QAbstractItemModel::rowsAboutToBeRemoved,
254 this, &FlatProxyModel::on_rowsAboutToBeRemoved);
255 connect(sourceModel, &QAbstractItemModel::rowsInserted,
256 this, &FlatProxyModel::on_rowsInserted);
257 connect(sourceModel, &QAbstractItemModel::rowsRemoved,
258 this, &FlatProxyModel::on_rowsRemoved);
263 void FlatProxyModel::insertSubTree(const QModelIndex &source_idx, bool emitInsert)
265 SourceItem *newSubTree = new SourceItem(source_idx.row(), sourceToInternal(sourceModel()->parent(source_idx)));
267 if (newSubTree->parent()) {
268 newSubTree->setPos(newSubTree->parent()->pos() + source_idx.row() + 1);
270 SourceItem *lastItem = insertSubTreeHelper(newSubTree, newSubTree, source_idx);
273 Q_ASSERT(lastItem->next() == nullptr);
276 beginInsertRows(QModelIndex(), newSubTree->pos(), lastItem->pos());
278 if (newSubTree->parent()) {
279 if (newSubTree->parent()->childCount() > source_idx.row()) {
280 SourceItem *next = newSubTree->parent()->child(source_idx.row());
281 lastItem->setNext(next);
282 int nextPos = lastItem->pos() + 1;
284 next->setPos(nextPos);
289 if (source_idx.row() > 0) {
290 SourceItem *previous = newSubTree->parent()->child(source_idx.row() - 1);
291 while (previous->childCount() > 0) {
292 previous = previous->child(previous->childCount() - 1);
294 previous->setNext(newSubTree);
297 newSubTree->parent()->setNext(newSubTree);
301 _rootSourceItem = newSubTree;
309 FlatProxyModel::SourceItem *FlatProxyModel::insertSubTreeHelper(SourceItem *parentItem, SourceItem *lastItem_, const QModelIndex &source_idx)
311 SourceItem *lastItem = lastItem_;
312 SourceItem *newItem = nullptr;
313 for (int row = 0; row < sourceModel()->rowCount(source_idx); row++) {
314 newItem = new SourceItem(row, parentItem);
315 newItem->setPos(lastItem->pos() + 1);
316 lastItem->setNext(newItem);
317 lastItem = insertSubTreeHelper(newItem, newItem, sourceModel()->index(row, 0, source_idx));
323 void FlatProxyModel::removeSubTree(const QModelIndex &source_idx, bool emitRemove)
325 SourceItem *sourceItem = sourceToInternal(source_idx);
329 SourceItem *prevItem = sourceItem->parent();
330 if (sourceItem->sourceRow() > 0) {
331 prevItem = prevItem->child(sourceItem->sourceRow() - 1);
332 while (prevItem->childCount() > 0) {
333 prevItem = prevItem->child(prevItem->childCount() - 1);
337 SourceItem *lastItem = sourceItem;
338 while (lastItem->childCount() > 0) {
339 lastItem = lastItem->child(lastItem->childCount() - 1);
343 beginRemoveRows(QModelIndex(), sourceItem->pos(), lastItem->pos());
347 prevItem->setNext(lastItem->next());
348 nextPos = prevItem->pos() + 1;
351 SourceItem *nextItem = lastItem->next();
353 nextItem->setPos(nextPos);
355 nextItem = nextItem->next();
358 sourceItem->parent()->removeChild(sourceItem);
366 QModelIndex FlatProxyModel::index(int row, int column, const QModelIndex &parent) const
368 if (parent.isValid()) {
369 qWarning() << "FlatProxyModel::index() called with valid parent:" << parent;
373 if (!_rootSourceItem) {
374 qWarning() << "FlatProxyModel::index() while model has no root Item";
378 SourceItem *item = _rootSourceItem;
379 while (item->pos() != row) {
380 item = item->findChild(row);
382 qWarning() << "FlatProxyModel::index() no such row:" << row;
387 Q_ASSERT(item->pos() == row);
388 return createIndex(row, column, item);
392 QModelIndex FlatProxyModel::parent(const QModelIndex &index) const
395 // this is a flat model
400 int FlatProxyModel::rowCount(const QModelIndex &index) const
402 if (!_rootSourceItem)
408 SourceItem *item = _rootSourceItem;
409 while (item->childCount() > 0) {
410 item = item->child(item->childCount() - 1);
412 return item->pos() + 1;
416 int FlatProxyModel::columnCount(const QModelIndex &index) const
419 if (!sourceModel()) {
423 return sourceModel()->columnCount(QModelIndex());
428 FlatProxyModel::SourceItem *FlatProxyModel::sourceToInternal(const QModelIndex &sourceIndex) const
430 QList<int> childPath;
431 for (QModelIndex idx = sourceIndex; idx.isValid(); idx = sourceModel()->parent(idx)) {
432 childPath.prepend(idx.row());
435 Q_ASSERT(!sourceIndex.isValid() || !childPath.isEmpty());
437 SourceItem *item = _rootSourceItem;
438 for (int i = 0; i < childPath.count(); i++) {
439 item = item->child(childPath[i]);
445 void FlatProxyModel::on_columnsAboutToBeInserted(const QModelIndex &parent, int start, int end)
448 beginInsertColumns(QModelIndex(), start, end);
452 void FlatProxyModel::on_columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
455 beginRemoveColumns(QModelIndex(), start, end);
459 void FlatProxyModel::on_columnsInserted(const QModelIndex &parent, int start, int end)
468 void FlatProxyModel::on_columnsRemoved(const QModelIndex &parent, int start, int end)
477 void FlatProxyModel::on_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
479 Q_ASSERT(sourceModel());
480 Q_ASSERT(sourceModel()->parent(topLeft) == sourceModel()->parent(bottomRight));
482 SourceItem *topLeftItem = sourceToInternal(topLeft);
483 Q_ASSERT(topLeftItem);
484 Q_ASSERT(topLeftItem->parent()->childCount() > bottomRight.row());
486 QModelIndex proxyTopLeft = createIndex(topLeftItem->pos(), topLeft.column(), topLeftItem);
487 QModelIndex proxyBottomRight = createIndex(topLeftItem->pos() + bottomRight.row() - topLeft.row(), bottomRight.column(), topLeftItem->parent()->child(bottomRight.row()));
488 emit dataChanged(proxyTopLeft, proxyBottomRight);
492 void FlatProxyModel::on_layoutAboutToBeChanged()
494 emit layoutAboutToBeChanged();
495 removeSubTree(QModelIndex(), false /* don't emit removeRows() */);
499 void FlatProxyModel::on_layoutChanged()
501 insertSubTree(QModelIndex(), false /* don't emit insertRows() */);
502 emit layoutChanged();
506 void FlatProxyModel::on_rowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
508 Q_ASSERT(sourceModel());
509 Q_ASSERT(_rootSourceItem);
511 SourceItem *sourceItem = sourceToInternal(parent);
512 Q_ASSERT(sourceItem);
514 beginInsertRows(QModelIndex(), sourceItem->pos() + start + 1, sourceItem->pos() + end + 1);
516 SourceItem *prevItem = sourceItem;
518 prevItem = sourceItem->child(start - 1);
519 while (prevItem->childCount() > 0) {
520 prevItem = prevItem->child(prevItem->childCount() - 1);
525 SourceItem *nextItem = prevItem->next();
527 SourceItem *newItem = nullptr;
528 int newPos = prevItem->pos() + 1;
529 for (int row = start; row <= end; row++) {
530 newItem = new SourceItem(row, sourceItem);
531 newItem->setPos(newPos);
533 prevItem->setNext(newItem);
536 prevItem->setNext(nextItem);
539 nextItem->setPos(newPos);
541 nextItem = nextItem->next();
546 void FlatProxyModel::on_rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
548 Q_ASSERT(sourceModel());
549 Q_ASSERT(_rootSourceItem);
551 SourceItem *sourceItem = sourceToInternal(parent);
552 beginRemoveRows(QModelIndex(), sourceItem->pos() + start + 1, sourceItem->pos() + end + 1);
554 // sanity check - if that check fails our indexes would be messed up
555 for (int row = start; row <= end; row++) {
556 if (sourceItem->child(row)->childCount() > 0) {
557 qWarning() << "on_rowsAboutToBeRemoved(): sourceModel() removed rows which have children on their own!" << sourceModel()->index(row, 0, parent);
564 void FlatProxyModel::on_rowsInserted(const QModelIndex &parent, int start, int end)
566 Q_ASSERT(sourceModel());
567 Q_ASSERT(_rootSourceItem);
569 SourceItem *sourceItem = sourceToInternal(parent);
570 Q_ASSERT(sourceItem);
571 Q_UNUSED(sourceItem);
573 // sanity check - if that check fails our indexes would be messed up
574 for (int row = start; row <= end; row++) {
575 QModelIndex child = sourceModel()->index(row, 0, parent);
576 if (sourceModel()->rowCount(child) > 0) {
577 qWarning() << "on_rowsInserted(): sourceModel() inserted rows which already have children on their own!" << child;
586 void FlatProxyModel::on_rowsRemoved(const QModelIndex &parent, int start, int end)
588 Q_ASSERT(sourceModel());
589 Q_ASSERT(_rootSourceItem);
591 SourceItem *sourceItem = sourceToInternal(parent);
592 Q_ASSERT(sourceItem);
594 SourceItem *prevItem = sourceItem;
596 prevItem = sourceItem->child(start - 1);
597 while (prevItem->childCount() > 0) {
598 prevItem = prevItem->child(prevItem->childCount() - 1);
603 SourceItem *nextItem = sourceItem->child(end)->next();
605 int newPos = prevItem->pos() + 1;
606 prevItem->setNext(nextItem);
609 nextItem->setPos(newPos);
611 nextItem = nextItem->next();
614 SourceItem *childItem;
615 for (int row = start; row <= end; row++) {
616 childItem = sourceItem->_childs.takeAt(start);
625 void FlatProxyModel::linkTest() const
627 qDebug() << "Checking FlatProxyModel for linklist integrity";
628 if (!_rootSourceItem)
632 SourceItem *item = _rootSourceItem;
634 qDebug() << item << ":" << item->pos() << "==" << pos;
635 Q_ASSERT(item->pos() == pos);
641 qDebug() << "Last item in linklist:" << item << item->pos();
643 int lastPos = item->pos();
644 item = _rootSourceItem;
645 while (item->childCount() > 0) {
646 item = item->child(item->childCount() - 1);
648 qDebug() << "Last item in tree:" << item << item->pos();
649 Q_ASSERT(lastPos == item->pos());
652 qDebug() << "success!";
656 void FlatProxyModel::completenessTest() const
658 qDebug() << "Checking FlatProxyModel for Completeness:";
660 checkChildCount(QModelIndex(), _rootSourceItem, pos);
661 qDebug() << "success!";
665 void FlatProxyModel::checkChildCount(const QModelIndex &index, const SourceItem *item, int &pos) const
670 qDebug() << index << "(Item:" << item << "):" << sourceModel()->rowCount(index) << "==" << item->childCount();
671 qDebug() << "ProxyPos:" << item->pos() << "==" << pos;
672 Q_ASSERT(sourceModel()->rowCount(index) == item->childCount());
674 for (int row = 0; row < sourceModel()->rowCount(index); row++) {
676 checkChildCount(sourceModel()->index(row, 0, index), item->child(row), pos);
681 // ========================================
683 // ========================================
684 FlatProxyModel::SourceItem::SourceItem(int row, SourceItem *parent)
688 parent->_childs.insert(row, this);
693 FlatProxyModel::SourceItem::~SourceItem()
695 for (int i = 0; i < childCount(); i++) {
702 int FlatProxyModel::SourceItem::sourceRow() const
707 return parent()->_childs.indexOf(const_cast<FlatProxyModel::SourceItem *>(this));
711 FlatProxyModel::SourceItem *FlatProxyModel::SourceItem::findChild(int proxyPos) const
713 Q_ASSERT(proxyPos > pos());
714 Q_ASSERT(_childs.count() > 0);
715 Q_ASSERT(proxyPos >= _childs[0]->pos());
718 int end = _childs.count() - 1;
720 while (end - start > 1) {
721 pivot = (end + start) / 2;
722 if (_childs[pivot]->pos() > proxyPos)
728 if (_childs[end]->pos() <= proxyPos)
731 return _childs[start];