1 /***************************************************************************
2 * Copyright (C) 2005-2016 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),
33 QModelIndex FlatProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
35 if (!sourceIndex.isValid())
38 SourceItem *sourceItem = sourceToInternal(sourceIndex);
40 return createIndex(sourceItem->pos(), sourceIndex.column(), sourceItem);
44 QModelIndex FlatProxyModel::mapToSource(const QModelIndex &proxyIndex) const
46 if (!proxyIndex.isValid())
49 Q_ASSERT(proxyIndex.model() == this);
50 Q_ASSERT(_rootSourceItem);
52 int row = proxyIndex.row();
53 QModelIndex sourceParent;
54 SourceItem *sourceItem = _rootSourceItem->findChild(row);
56 if (sourceItem->pos() == row) {
57 return sourceModel()->index(sourceItem->sourceRow(), proxyIndex.column(), sourceParent);
60 sourceParent = sourceModel()->index(sourceItem->sourceRow(), 0, sourceParent);
61 sourceItem = sourceItem->findChild(row);
65 qWarning() << "FlatProxyModel::mapToSource(): couldn't find source index for" << proxyIndex;
67 return QModelIndex(); // make compilers happy :)
71 bool FlatProxyModel::_RangeRect::operator<(const FlatProxyModel::_RangeRect &other) const
73 if (left != other.left)
74 return left < other.left;
76 if (right != other.right)
77 return right < other.right;
80 return top < other.top;
83 return bottom < other.bottom;
87 QItemSelection FlatProxyModel::mapSelectionFromSource(const QItemSelection &sourceSelection) const
89 QList<_RangeRect> newRanges;
90 QHash<QModelIndex, SourceItem *> itemLookup;
91 // basics steps of the loop:
92 // 1. convert each source ItemSelectionRange to a mapped Range (internal one for easier post processing)
93 // 2. insert it into the list of previously mapped Ranges sorted by top location
94 for (int i = 0; i < sourceSelection.count(); i++) {
95 const QItemSelectionRange ¤tRange = sourceSelection[i];
96 QModelIndex currentParent = currentRange.topLeft().parent();
97 Q_ASSERT(currentParent == currentRange.bottomRight().parent());
99 SourceItem *parentItem = 0;
100 if (!itemLookup.contains(currentParent)) {
101 parentItem = sourceToInternal(currentParent);
102 itemLookup[currentParent] = parentItem;
105 parentItem = itemLookup[currentParent];
108 _RangeRect newRange = { currentRange.topLeft().column(), currentRange.bottomRight().column(),
109 currentRange.topLeft().row(), currentRange.bottomRight().row(),
110 parentItem->child(currentRange.topLeft().row()), parentItem->child(currentRange.bottomRight().row()) };
112 if (newRanges.isEmpty()) {
113 newRanges << newRange;
117 _RangeRect &first = newRanges[0];
118 if (newRange < first) {
119 newRanges.prepend(newRange);
123 bool inserted = false;
124 for (int j = 0; j < newRanges.count() - 1; j++) {
125 _RangeRect &a = newRanges[j];
126 _RangeRect &b = newRanges[j + 1];
128 if (a < newRange && newRange < b) {
129 newRanges[j + 1] = newRange;
137 _RangeRect &last = newRanges[newRanges.count() - 1];
138 if (last < newRange) {
139 newRanges.append(newRange);
146 // we've got a sorted list of ranges now. so we can easily check if there is a possibility to merge
147 for (int i = newRanges.count() - 1; i > 0; i--) {
148 _RangeRect &a = newRanges[i - 1];
149 _RangeRect &b = newRanges[i];
150 if (a.left != b.left || a.right != b.right)
153 if (a.bottom < b.top - 1) {
157 // all merge checks passed!
158 if (b.bottom > a.bottom) {
160 a.bottomItem = b.bottomItem;
161 } // otherwise b is totally enclosed in a -> nothing to do but drop b.
162 newRanges.removeAt(i);
165 QItemSelection proxySelection;
166 for (int i = 0; i < newRanges.count(); i++) {
167 _RangeRect &r = newRanges[i];
168 proxySelection << QItemSelectionRange(createIndex(r.top, r.left, r.topItem), createIndex(r.bottom, r.right, r.bottomItem));
170 return proxySelection;
174 QItemSelection FlatProxyModel::mapSelectionToSource(const QItemSelection &proxySelection) const
176 QItemSelection sourceSelection;
178 for (int i = 0; i < proxySelection.count(); i++) {
179 const QItemSelectionRange &range = proxySelection[i];
181 SourceItem *topLeftItem = 0;
182 SourceItem *bottomRightItem = 0;
183 SourceItem *currentItem = static_cast<SourceItem *>(range.topLeft().internalPointer());
184 int row = range.topLeft().row();
185 int left = range.topLeft().column();
186 int right = range.bottomRight().column();
187 while (currentItem && row <= range.bottomRight().row()) {
188 Q_ASSERT(currentItem->pos() == row);
190 topLeftItem = currentItem;
192 if (currentItem->parent() == topLeftItem->parent()) {
193 bottomRightItem = currentItem;
196 Q_ASSERT(topLeftItem && bottomRightItem);
197 sourceSelection << QItemSelectionRange(mapToSource(createIndex(topLeftItem->pos(), left, topLeftItem)), mapToSource(createIndex(bottomRightItem->pos(), right, bottomRightItem)));
203 currentItem = currentItem->next();
207 if (topLeftItem && bottomRightItem) { // there should be one range left.
208 sourceSelection << QItemSelectionRange(mapToSource(createIndex(topLeftItem->pos(), left, topLeftItem)), mapToSource(createIndex(bottomRightItem->pos(), right, bottomRightItem)));
212 return sourceSelection;
216 void FlatProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
218 if (QAbstractProxyModel::sourceModel()) {
219 disconnect(QAbstractProxyModel::sourceModel(), 0, this, 0);
222 QAbstractProxyModel::setSourceModel(sourceModel);
224 emit layoutAboutToBeChanged();
225 removeSubTree(QModelIndex(), false /* don't emit removeRows() */);
226 insertSubTree(QModelIndex(), false /* don't emit insertRows() */);
227 emit layoutChanged();
230 connect(sourceModel, SIGNAL(columnsAboutToBeInserted(const QModelIndex &, int, int)),
231 this, SLOT(on_columnsAboutToBeInserted(const QModelIndex &, int, int)));
232 connect(sourceModel, SIGNAL(columnsAboutToBeRemoved(const QModelIndex &, int, int)),
233 this, SLOT(on_columnsAboutToBeRemoved(const QModelIndex &, int, int)));
234 connect(sourceModel, SIGNAL(columnsInserted(const QModelIndex &, int, int)),
235 this, SLOT(on_columnsInserted(const QModelIndex &, int, int)));
236 connect(sourceModel, SIGNAL(columnsRemoved(const QModelIndex &, int, int)),
237 this, SLOT(on_columnsRemoved(const QModelIndex &, int, int)));
239 connect(sourceModel, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
240 this, SLOT(on_dataChanged(const QModelIndex &, const QModelIndex &)));
241 // on_headerDataChanged(Qt::Orientation orientation, int first, int last)
243 connect(sourceModel, SIGNAL(layoutAboutToBeChanged()),
244 this, SLOT(on_layoutAboutToBeChanged()));
245 connect(sourceModel, SIGNAL(layoutChanged()),
246 this, SLOT(on_layoutChanged()));
248 connect(sourceModel, SIGNAL(modelAboutToBeReset()),
249 this, SLOT(on_modelAboutToBeReset()));
250 // void on_modelReset()
252 connect(sourceModel, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)),
253 this, SLOT(on_rowsAboutToBeInserted(const QModelIndex &, int, int)));
254 connect(sourceModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),
255 this, SLOT(on_rowsAboutToBeRemoved(const QModelIndex &, int, int)));
256 connect(sourceModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
257 this, SLOT(on_rowsInserted(const QModelIndex &, int, int)));
258 connect(sourceModel, SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
259 this, SLOT(on_rowsRemoved(const QModelIndex &, int, int)));
264 void FlatProxyModel::insertSubTree(const QModelIndex &source_idx, bool emitInsert)
266 SourceItem *newSubTree = new SourceItem(source_idx.row(), sourceToInternal(sourceModel()->parent(source_idx)));
268 if (newSubTree->parent()) {
269 newSubTree->setPos(newSubTree->parent()->pos() + source_idx.row() + 1);
271 SourceItem *lastItem = insertSubTreeHelper(newSubTree, newSubTree, source_idx);
274 Q_ASSERT(lastItem->next() == 0);
277 beginInsertRows(QModelIndex(), newSubTree->pos(), lastItem->pos());
279 if (newSubTree->parent()) {
280 if (newSubTree->parent()->childCount() > source_idx.row()) {
281 SourceItem *next = newSubTree->parent()->child(source_idx.row());
282 lastItem->setNext(next);
283 int nextPos = lastItem->pos() + 1;
285 next->setPos(nextPos);
290 if (source_idx.row() > 0) {
291 SourceItem *previous = newSubTree->parent()->child(source_idx.row() - 1);
292 while (previous->childCount() > 0) {
293 previous = previous->child(previous->childCount() - 1);
295 previous->setNext(newSubTree);
298 newSubTree->parent()->setNext(newSubTree);
302 _rootSourceItem = newSubTree;
310 FlatProxyModel::SourceItem *FlatProxyModel::insertSubTreeHelper(SourceItem *parentItem, SourceItem *lastItem_, const QModelIndex &source_idx)
312 SourceItem *lastItem = lastItem_;
313 SourceItem *newItem = 0;
314 for (int row = 0; row < sourceModel()->rowCount(source_idx); row++) {
315 newItem = new SourceItem(row, parentItem);
316 newItem->setPos(lastItem->pos() + 1);
317 lastItem->setNext(newItem);
318 lastItem = insertSubTreeHelper(newItem, newItem, sourceModel()->index(row, 0, source_idx));
324 void FlatProxyModel::removeSubTree(const QModelIndex &source_idx, bool emitRemove)
326 SourceItem *sourceItem = sourceToInternal(source_idx);
330 SourceItem *prevItem = sourceItem->parent();
331 if (sourceItem->sourceRow() > 0) {
332 prevItem = prevItem->child(sourceItem->sourceRow() - 1);
333 while (prevItem->childCount() > 0) {
334 prevItem = prevItem->child(prevItem->childCount() - 1);
338 SourceItem *lastItem = sourceItem;
339 while (lastItem->childCount() > 0) {
340 lastItem = lastItem->child(lastItem->childCount() - 1);
344 beginRemoveRows(QModelIndex(), sourceItem->pos(), lastItem->pos());
348 prevItem->setNext(lastItem->next());
349 nextPos = prevItem->pos() + 1;
352 SourceItem *nextItem = lastItem->next();
354 nextItem->setPos(nextPos);
356 nextItem = nextItem->next();
359 sourceItem->parent()->removeChild(sourceItem);
367 QModelIndex FlatProxyModel::index(int row, int column, const QModelIndex &parent) const
369 if (parent.isValid()) {
370 qWarning() << "FlatProxyModel::index() called with valid parent:" << parent;
371 return QModelIndex();
374 if (!_rootSourceItem) {
375 qWarning() << "FlatProxyModel::index() while model has no root Item";
376 return QModelIndex();
379 SourceItem *item = _rootSourceItem;
380 while (item->pos() != row) {
381 item = item->findChild(row);
383 qWarning() << "FlatProxyModel::index() no such row:" << row;
384 return QModelIndex();
388 Q_ASSERT(item->pos() == row);
389 return createIndex(row, column, item);
393 QModelIndex FlatProxyModel::parent(const QModelIndex &index) const
396 // this is a flat model
397 return QModelIndex();
401 int FlatProxyModel::rowCount(const QModelIndex &index) const
403 if (!_rootSourceItem)
409 SourceItem *item = _rootSourceItem;
410 while (item->childCount() > 0) {
411 item = item->child(item->childCount() - 1);
413 return item->pos() + 1;
417 int FlatProxyModel::columnCount(const QModelIndex &index) const
420 if (!sourceModel()) {
424 return sourceModel()->columnCount(QModelIndex());
429 FlatProxyModel::SourceItem *FlatProxyModel::sourceToInternal(const QModelIndex &sourceIndex) const
431 QList<int> childPath;
432 for (QModelIndex idx = sourceIndex; idx.isValid(); idx = sourceModel()->parent(idx)) {
433 childPath.prepend(idx.row());
436 Q_ASSERT(!sourceIndex.isValid() || !childPath.isEmpty());
438 SourceItem *item = _rootSourceItem;
439 for (int i = 0; i < childPath.count(); i++) {
440 item = item->child(childPath[i]);
446 void FlatProxyModel::on_columnsAboutToBeInserted(const QModelIndex &parent, int start, int end)
449 beginInsertColumns(QModelIndex(), start, end);
453 void FlatProxyModel::on_columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
456 beginRemoveColumns(QModelIndex(), start, end);
460 void FlatProxyModel::on_columnsInserted(const QModelIndex &parent, int start, int end)
469 void FlatProxyModel::on_columnsRemoved(const QModelIndex &parent, int start, int end)
478 void FlatProxyModel::on_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
480 Q_ASSERT(sourceModel());
481 Q_ASSERT(sourceModel()->parent(topLeft) == sourceModel()->parent(bottomRight));
483 SourceItem *topLeftItem = sourceToInternal(topLeft);
484 Q_ASSERT(topLeftItem);
485 Q_ASSERT(topLeftItem->parent()->childCount() > bottomRight.row());
487 QModelIndex proxyTopLeft = createIndex(topLeftItem->pos(), topLeft.column(), topLeftItem);
488 QModelIndex proxyBottomRight = createIndex(topLeftItem->pos() + bottomRight.row() - topLeft.row(), bottomRight.column(), topLeftItem->parent()->child(bottomRight.row()));
489 emit dataChanged(proxyTopLeft, proxyBottomRight);
493 void FlatProxyModel::on_layoutAboutToBeChanged()
495 emit layoutAboutToBeChanged();
496 removeSubTree(QModelIndex(), false /* don't emit removeRows() */);
500 void FlatProxyModel::on_layoutChanged()
502 insertSubTree(QModelIndex(), false /* don't emit insertRows() */);
503 emit layoutChanged();
507 void FlatProxyModel::on_rowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
509 Q_ASSERT(sourceModel());
510 Q_ASSERT(_rootSourceItem);
512 SourceItem *sourceItem = sourceToInternal(parent);
513 Q_ASSERT(sourceItem);
515 beginInsertRows(QModelIndex(), sourceItem->pos() + start + 1, sourceItem->pos() + end + 1);
517 SourceItem *prevItem = sourceItem;
519 prevItem = sourceItem->child(start - 1);
520 while (prevItem->childCount() > 0) {
521 prevItem = prevItem->child(prevItem->childCount() - 1);
526 SourceItem *nextItem = prevItem->next();
528 SourceItem *newItem = 0;
529 int newPos = prevItem->pos() + 1;
530 for (int row = start; row <= end; row++) {
531 newItem = new SourceItem(row, sourceItem);
532 newItem->setPos(newPos);
534 prevItem->setNext(newItem);
537 prevItem->setNext(nextItem);
540 nextItem->setPos(newPos);
542 nextItem = nextItem->next();
547 void FlatProxyModel::on_rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
549 Q_ASSERT(sourceModel());
550 Q_ASSERT(_rootSourceItem);
552 SourceItem *sourceItem = sourceToInternal(parent);
553 beginRemoveRows(QModelIndex(), sourceItem->pos() + start + 1, sourceItem->pos() + end + 1);
555 // sanity check - if that check fails our indexes would be messed up
556 for (int row = start; row <= end; row++) {
557 if (sourceItem->child(row)->childCount() > 0) {
558 qWarning() << "on_rowsAboutToBeRemoved(): sourceModel() removed rows which have children on their own!" << sourceModel()->index(row, 0, parent);
565 void FlatProxyModel::on_rowsInserted(const QModelIndex &parent, int start, int end)
567 Q_ASSERT(sourceModel());
568 Q_ASSERT(_rootSourceItem);
570 SourceItem *sourceItem = sourceToInternal(parent);
571 Q_ASSERT(sourceItem);
572 Q_UNUSED(sourceItem);
574 // sanity check - if that check fails our indexes would be messed up
575 for (int row = start; row <= end; row++) {
576 QModelIndex child = sourceModel()->index(row, 0, parent);
577 if (sourceModel()->rowCount(child) > 0) {
578 qWarning() << "on_rowsInserted(): sourceModel() inserted rows which already have children on their own!" << child;
587 void FlatProxyModel::on_rowsRemoved(const QModelIndex &parent, int start, int end)
589 Q_ASSERT(sourceModel());
590 Q_ASSERT(_rootSourceItem);
592 SourceItem *sourceItem = sourceToInternal(parent);
593 Q_ASSERT(sourceItem);
595 SourceItem *prevItem = sourceItem;
597 prevItem = sourceItem->child(start - 1);
598 while (prevItem->childCount() > 0) {
599 prevItem = prevItem->child(prevItem->childCount() - 1);
604 SourceItem *nextItem = sourceItem->child(end)->next();
606 int newPos = prevItem->pos() + 1;
607 prevItem->setNext(nextItem);
610 nextItem->setPos(newPos);
612 nextItem = nextItem->next();
615 SourceItem *childItem;
616 for (int row = start; row <= end; row++) {
617 childItem = sourceItem->_childs.takeAt(start);
626 void FlatProxyModel::linkTest() const
628 qDebug() << "Checking FlatProxyModel for linklist integrity";
629 if (!_rootSourceItem)
633 SourceItem *item = _rootSourceItem;
635 qDebug() << item << ":" << item->pos() << "==" << pos;
636 Q_ASSERT(item->pos() == pos);
642 qDebug() << "Last item in linklist:" << item << item->pos();
644 int lastPos = item->pos();
645 item = _rootSourceItem;
646 while (item->childCount() > 0) {
647 item = item->child(item->childCount() - 1);
649 qDebug() << "Last item in tree:" << item << item->pos();
650 Q_ASSERT(lastPos == item->pos());
653 qDebug() << "success!";
657 void FlatProxyModel::completenessTest() const
659 qDebug() << "Checking FlatProxyModel for Completeness:";
661 checkChildCount(QModelIndex(), _rootSourceItem, pos);
662 qDebug() << "success!";
666 void FlatProxyModel::checkChildCount(const QModelIndex &index, const SourceItem *item, int &pos) const
671 qDebug() << index << "(Item:" << item << "):" << sourceModel()->rowCount(index) << "==" << item->childCount();
672 qDebug() << "ProxyPos:" << item->pos() << "==" << pos;
673 Q_ASSERT(sourceModel()->rowCount(index) == item->childCount());
675 for (int row = 0; row < sourceModel()->rowCount(index); row++) {
677 checkChildCount(sourceModel()->index(row, 0, index), item->child(row), pos);
682 // ========================================
684 // ========================================
685 FlatProxyModel::SourceItem::SourceItem(int row, SourceItem *parent)
691 parent->_childs.insert(row, this);
696 FlatProxyModel::SourceItem::~SourceItem()
698 for (int i = 0; i < childCount(); i++) {
705 int FlatProxyModel::SourceItem::sourceRow() const
710 return parent()->_childs.indexOf(const_cast<FlatProxyModel::SourceItem *>(this));
714 FlatProxyModel::SourceItem *FlatProxyModel::SourceItem::findChild(int proxyPos) const
716 Q_ASSERT(proxyPos > pos());
717 Q_ASSERT(_childs.count() > 0);
718 Q_ASSERT(proxyPos >= _childs[0]->pos());
721 int end = _childs.count() - 1;
723 while (end - start > 1) {
724 pivot = (end + start) / 2;
725 if (_childs[pivot]->pos() > proxyPos)
731 if (_childs[end]->pos() <= proxyPos)
734 return _childs[start];