From: Marcus Eggenberger Date: Thu, 21 May 2009 14:47:51 +0000 (+0200) Subject: New FlatProxyModel X-Git-Tag: 0.5-rc1~187 X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=commitdiff_plain;h=920643004f172d255dd2420d1b03adea3b3a07b2 New FlatProxyModel A ProxyModel that flattens a treelike model in preorder. --- diff --git a/src/uisupport/CMakeLists.txt b/src/uisupport/CMakeLists.txt index 92fc9953..eccde9fb 100644 --- a/src/uisupport/CMakeLists.txt +++ b/src/uisupport/CMakeLists.txt @@ -16,6 +16,7 @@ set(SOURCES clickablelabel.cpp colorbutton.cpp contextmenuactionprovider.cpp + flatproxymodel.cpp graphicalui.cpp icon.cpp iconloader.cpp @@ -43,6 +44,7 @@ set(MOC_HDRS clickablelabel.h colorbutton.h contextmenuactionprovider.h + flatproxymodel.h graphicalui.h iconloader.h inputline.h diff --git a/src/uisupport/flatproxymodel.cpp b/src/uisupport/flatproxymodel.cpp new file mode 100644 index 00000000..ef190742 --- /dev/null +++ b/src/uisupport/flatproxymodel.cpp @@ -0,0 +1,667 @@ +/*************************************************************************** + * Copyright (C) 2005-08 by the Quassel Project * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) version 3. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "flatproxymodel.h" + +#include +#include + +FlatProxyModel::FlatProxyModel(QObject *parent) + : QAbstractProxyModel(parent), + _rootSourceItem(0) +{ +} + +QModelIndex FlatProxyModel::mapFromSource(const QModelIndex &sourceIndex) const { + if(!sourceIndex.isValid()) + return QModelIndex(); + + SourceItem *sourceItem = sourceToInternal(sourceIndex); + Q_ASSERT(sourceItem); + return createIndex(sourceItem->pos(), sourceIndex.column(), sourceItem); +} + +QModelIndex FlatProxyModel::mapToSource(const QModelIndex &proxyIndex) const { + if(!proxyIndex.isValid()) + return QModelIndex(); + + Q_ASSERT(proxyIndex.model() == this); + Q_ASSERT(_rootSourceItem); + + int row = proxyIndex.row(); + QModelIndex sourceParent; + SourceItem *sourceItem = _rootSourceItem->findChild(row); + while(sourceItem) { + if(sourceItem->pos() == row) { + return sourceModel()->index(sourceItem->sourceRow(), proxyIndex.column(), sourceParent); + } else { + sourceParent = sourceModel()->index(sourceItem->sourceRow(), 0, sourceParent); + sourceItem = sourceItem->findChild(row); + } + } + + qWarning() << "FlatProxyModel::mapToSource(): couldn't find source index for" << proxyIndex; + Q_ASSERT(false); + return QModelIndex(); // make compilers happy :) +} + +bool FlatProxyModel::_RangeRect::operator<(const FlatProxyModel::_RangeRect &other) const { + if(left != other.left) + return left < other.left; + + if(right != other.right) + return right < other.right; + + if(top != other.top) + return top < other.top; + + // top == other.top + return bottom < other.bottom; +} + +QItemSelection FlatProxyModel::mapSelectionFromSource(const QItemSelection &sourceSelection) const { + QList<_RangeRect> newRanges; + QHash itemLookup; + // basics steps of the loop: + // 1. convert each source ItemSelectionRange to a mapped Range (internal one for easier post processing) + // 2. insert it into the list of previously mapped Ranges sorted by top location + for(int i = 0; i < sourceSelection.count(); i++) { + const QItemSelectionRange ¤tRange = sourceSelection[i]; + QModelIndex currentParent = currentRange.topLeft().parent(); + Q_ASSERT(currentParent == currentRange.bottomRight().parent()); + + SourceItem *parentItem = 0; + if(!itemLookup.contains(currentParent)) { + parentItem = sourceToInternal(currentParent); + itemLookup[currentParent] = parentItem; + } else { + parentItem = itemLookup[currentParent]; + } + + _RangeRect newRange = { currentRange.topLeft().column(), currentRange.bottomRight().column(), + currentRange.topLeft().row(), currentRange.bottomRight().row(), + parentItem->child(currentRange.topLeft().row()), parentItem->child(currentRange.bottomRight().row()) }; + + if(newRanges.isEmpty()) { + newRanges << newRange; + continue; + } + + _RangeRect &first = newRanges[0]; + if(newRange < first) { + newRanges.prepend(newRange); + continue; + } + + bool inserted = false; + for(int j = 0; j < newRanges.count() - 1; j++) { + _RangeRect &a = newRanges[j]; + _RangeRect &b = newRanges[j + 1]; + + if(a < newRange && newRange < b) { + newRanges[j + 1] = newRange; + inserted = true; + break; + } + } + if(inserted) + continue; + + _RangeRect &last = newRanges[newRanges.count() - 1]; + if(last < newRange) { + newRanges.append(newRange); + continue; + } + + Q_ASSERT(false); + } + + // we've got a sorted list of ranges now. so we can easily check if there is a possibility to merge + for(int i = newRanges.count() - 1; i > 0; i--) { + _RangeRect &a = newRanges[i - 1]; + _RangeRect &b = newRanges[i]; + if(a.left != b.left || a.right != b.right) + continue; + + if(a.bottom < b.top - 1) { + continue; + } + + // all merge checks passed! + if(b.bottom > a.bottom) { + a.bottom = b.bottom; + a.bottomItem = b.bottomItem; + } // otherwise b is totally enclosed in a -> nothing to do but drop b. + newRanges.removeAt(i); + } + + QItemSelection proxySelection; + for(int i = 0; i < newRanges.count(); i++) { + _RangeRect &r = newRanges[i]; + proxySelection << QItemSelectionRange(createIndex(r.top, r.left, r.topItem), createIndex(r.bottom, r.right, r.bottomItem)); + } + return proxySelection; +} + +QItemSelection FlatProxyModel::mapSelectionToSource(const QItemSelection &proxySelection) const { + QItemSelection sourceSelection; + + for(int i = 0; i < proxySelection.count(); i++) { + const QItemSelectionRange &range = proxySelection[i]; + + SourceItem *topLeftItem = 0; + SourceItem *bottomRightItem = 0; + SourceItem *currentItem = static_cast(range.topLeft().internalPointer()); + int row = range.topLeft().row(); + int left = range.topLeft().column(); + int right = range.bottomRight().column(); + while(currentItem && row <= range.bottomRight().row()) { + Q_ASSERT(currentItem->pos() == row); + if(!topLeftItem) + topLeftItem = currentItem; + + if(currentItem->parent() == topLeftItem->parent()) { + bottomRightItem = currentItem; + } else { + Q_ASSERT(topLeftItem && bottomRightItem); + sourceSelection << QItemSelectionRange(mapToSource(createIndex(topLeftItem->pos(), left, topLeftItem)), mapToSource(createIndex(bottomRightItem->pos(), right, bottomRightItem))); + topLeftItem = 0; + bottomRightItem = 0; + } + + // update loop vars + currentItem = currentItem->next(); + row++; + } + + Q_ASSERT(topLeftItem && bottomRightItem); // there should be one range left. + sourceSelection << QItemSelectionRange(mapToSource(createIndex(topLeftItem->pos(), left, topLeftItem)), mapToSource(createIndex(bottomRightItem->pos(), right, bottomRightItem))); + } + + return sourceSelection; +} + +void FlatProxyModel::setSourceModel(QAbstractItemModel *sourceModel) { + if(QAbstractProxyModel::sourceModel()) { + disconnect(QAbstractProxyModel::sourceModel(), 0, this, 0); + } + + QAbstractProxyModel::setSourceModel(sourceModel); + + emit layoutAboutToBeChanged(); + removeSubTree(QModelIndex(), false /* don't emit removeRows() */); + insertSubTree(QModelIndex(), false /* don't emit insertRows() */); + emit layoutChanged(); + + if(sourceModel) { + connect(sourceModel, SIGNAL(columnsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(on_columnsAboutToBeInserted(const QModelIndex &, int, int))); + connect(sourceModel, SIGNAL(columnsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(on_columnsAboutToBeRemoved(const QModelIndex &, int, int))); + connect(sourceModel, SIGNAL(columnsInserted(const QModelIndex &, int, int)), + this, SLOT(on_columnsInserted(const QModelIndex &, int, int))); + connect(sourceModel, SIGNAL(columnsRemoved(const QModelIndex &, int, int)), + this, SLOT(on_columnsRemoved(const QModelIndex &, int, int))); + + connect(sourceModel, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(on_dataChanged(const QModelIndex &, const QModelIndex &))); + // on_headerDataChanged(Qt::Orientation orientation, int first, int last) + + connect(sourceModel, SIGNAL(layoutAboutToBeChanged()), + this, SLOT(on_layoutAboutToBeChanged())); + connect(sourceModel, SIGNAL(layoutChanged()), + this, SLOT(on_layoutChanged())); + + connect(sourceModel, SIGNAL(modelAboutToBeReset()), + this, SLOT(on_modelAboutToBeReset())); + // void on_modelReset() + + connect(sourceModel, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(on_rowsAboutToBeInserted(const QModelIndex &, int, int))); + connect(sourceModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(on_rowsAboutToBeRemoved(const QModelIndex &, int, int))); + connect(sourceModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(on_rowsInserted(const QModelIndex &, int, int))); + connect(sourceModel, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(on_rowsRemoved(const QModelIndex &, int, int))); + } + +} + +void FlatProxyModel::insertSubTree(const QModelIndex &source_idx, bool emitInsert) { + SourceItem *newSubTree = new SourceItem(source_idx.row(), sourceToInternal(sourceModel()->parent(source_idx))); + + if(newSubTree->parent()) { + newSubTree->setPos(newSubTree->parent()->pos() + source_idx.row() + 1); + } + SourceItem *lastItem = insertSubTreeHelper(newSubTree, newSubTree, source_idx); + + Q_ASSERT(lastItem); + Q_ASSERT(lastItem->next() == 0); + + if(emitInsert) + beginInsertRows(QModelIndex(), newSubTree->pos(), lastItem->pos()); + + if(newSubTree->parent()) { + if(newSubTree->parent()->childCount() > source_idx.row()) { + SourceItem *next = newSubTree->parent()->child(source_idx.row()); + lastItem->setNext(next); + int nextPos = lastItem->pos() + 1; + while(next) { + next->setPos(nextPos); + next = next->next(); + nextPos++; + } + } + if(source_idx.row() > 0) { + SourceItem *previous = newSubTree->parent()->child(source_idx.row() - 1); + while(previous->childCount() > 0) { + previous = previous->child(previous->childCount() - 1); + } + previous->setNext(newSubTree); + } else { + newSubTree->parent()->setNext(newSubTree); + } + } else { + _rootSourceItem = newSubTree; + } + + if(emitInsert) + endInsertRows(); +} + +FlatProxyModel::SourceItem *FlatProxyModel::insertSubTreeHelper(SourceItem *parentItem, SourceItem *lastItem_, const QModelIndex &source_idx) { + SourceItem *lastItem = lastItem_; + SourceItem *newItem = 0; + for(int row = 0; row < sourceModel()->rowCount(source_idx); row++) { + newItem = new SourceItem(row, parentItem); + newItem->setPos(lastItem->pos() + 1); + lastItem->setNext(newItem); + lastItem = insertSubTreeHelper(newItem, newItem, sourceModel()->index(row, 0, source_idx)); + } + return lastItem; +} + +void FlatProxyModel::removeSubTree(const QModelIndex &source_idx, bool emitRemove) { + SourceItem *sourceItem = sourceToInternal(source_idx); + if(!sourceItem) + return; + + SourceItem *prevItem = sourceItem->parent(); + if(sourceItem->sourceRow() > 0) { + prevItem = prevItem->child(sourceItem->sourceRow() - 1); + while(prevItem->childCount() > 0) { + prevItem = prevItem->child(prevItem->childCount() - 1); + } + } + + SourceItem *lastItem = sourceItem; + while(lastItem->childCount() > 0) { + lastItem = lastItem->child(lastItem->childCount() - 1); + } + + if(emitRemove) + beginRemoveRows(QModelIndex(), sourceItem->pos(), lastItem->pos()); + + int nextPos = 0; + if(prevItem) { + prevItem->setNext(lastItem->next()); + nextPos = prevItem->pos() + 1; + } + + SourceItem *nextItem = lastItem->next(); + while(nextItem) { + nextItem->setPos(nextPos); + nextPos++; + nextItem = nextItem->next(); + } + + sourceItem->parent()->removeChild(sourceItem); + delete sourceItem; + + if(emitRemove) + endRemoveRows(); +} + +QModelIndex FlatProxyModel::index(int row, int column, const QModelIndex &parent) const { + if(parent.isValid()) { + qWarning() << "FlatProxyModel::index() called with valid parent:" << parent; + return QModelIndex(); + } + + if(!_rootSourceItem) { + qWarning() << "FlatProxyModel::index() while model has no root Item"; + return QModelIndex(); + } + + SourceItem *item = _rootSourceItem; + while(item->pos() != row) { + item = item->findChild(row); + if(!item) { + qWarning() << "FlatProxyModel::index() no such row:" << row; + return QModelIndex(); + } + } + + Q_ASSERT(item->pos() == row); + return createIndex(row, column, item); +} + +QModelIndex FlatProxyModel::parent(const QModelIndex &index) const { + Q_UNUSED(index) + // this is a flat model + return QModelIndex(); +} + +int FlatProxyModel::rowCount(const QModelIndex &index) const { + if(!_rootSourceItem) + return 0; + + if(index.isValid()) + return 0; + + SourceItem *item = _rootSourceItem; + while(item->childCount() > 0) { + item = item->child(item->childCount() - 1); + } + return item->pos() + 1; +} + +int FlatProxyModel::columnCount(const QModelIndex &index) const { + Q_UNUSED(index) + if(!sourceModel()) { + return 0; + } else { + return sourceModel()->columnCount(QModelIndex()); + } +} + + +FlatProxyModel::SourceItem *FlatProxyModel::sourceToInternal(const QModelIndex &sourceIndex) const { + QList childPath; + for(QModelIndex idx = sourceIndex; idx.isValid(); idx = sourceModel()->parent(idx)) { + childPath.prepend(idx.row()); + } + + Q_ASSERT(!sourceIndex.isValid() || !childPath.isEmpty()); + + SourceItem *item = _rootSourceItem; + for(int i = 0; i < childPath.count(); i++) { + item = item->child(childPath[i]); + } + return item; +} + +void FlatProxyModel::on_columnsAboutToBeInserted(const QModelIndex &parent, int start, int end) { + Q_UNUSED(parent); + beginInsertColumns(QModelIndex(), start, end); +} + +void FlatProxyModel::on_columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { + Q_UNUSED(parent); + beginRemoveColumns(QModelIndex(), start, end); +} + +void FlatProxyModel::on_columnsInserted(const QModelIndex &parent, int start, int end) { + Q_UNUSED(parent); + Q_UNUSED(start); + Q_UNUSED(end); + endInsertColumns(); +} + + +void FlatProxyModel::on_columnsRemoved(const QModelIndex &parent, int start, int end) { + Q_UNUSED(parent); + Q_UNUSED(start); + Q_UNUSED(end); + endRemoveRows(); +} + +void FlatProxyModel::on_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { + Q_ASSERT(sourceModel()); + Q_ASSERT(sourceModel()->parent(topLeft) == sourceModel()->parent(bottomRight)); + + SourceItem *topLeftItem = sourceToInternal(topLeft); + Q_ASSERT(topLeftItem); + Q_ASSERT(topLeftItem->parent()->childCount() > bottomRight.row()); + + QModelIndex proxyTopLeft = createIndex(topLeftItem->pos(), topLeft.column(), topLeftItem); + QModelIndex proxyBottomRight = createIndex(topLeftItem->pos() + bottomRight.row() - topLeft.row(), bottomRight.column(), topLeftItem->parent()->child(bottomRight.row())); + emit dataChanged(proxyTopLeft, proxyBottomRight); +} + +void FlatProxyModel::on_layoutAboutToBeChanged() { + emit layoutAboutToBeChanged(); + removeSubTree(QModelIndex(), false /* don't emit removeRows() */); +} + +void FlatProxyModel::on_layoutChanged() { + insertSubTree(QModelIndex(), false /* don't emit insertRows() */); + emit layoutChanged(); +} + +void FlatProxyModel::on_rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) { + Q_ASSERT(sourceModel()); + Q_ASSERT(_rootSourceItem); + + SourceItem *sourceItem = sourceToInternal(parent); + Q_ASSERT(sourceItem); + + beginInsertRows(QModelIndex(), sourceItem->pos() + start + 1, sourceItem->pos() + end + 1); + + SourceItem *prevItem = sourceItem; + if(start > 0) { + prevItem = sourceItem->child(start - 1); + while(prevItem->childCount() > 0) { + prevItem = prevItem->child(prevItem->childCount() - 1); + } + } + Q_ASSERT(prevItem); + + SourceItem *nextItem = prevItem->next(); + + SourceItem *newItem = 0; + int newPos = prevItem->pos() + 1; + for(int row = start; row <= end; row++) { + newItem = new SourceItem(row, sourceItem); + newItem->setPos(newPos); + newPos++; + prevItem->setNext(newItem); + prevItem = newItem; + } + prevItem->setNext(nextItem); + + while(nextItem) { + nextItem->setPos(newPos); + newPos++; + nextItem = nextItem->next(); + } + +} + +void FlatProxyModel::on_rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { + Q_ASSERT(sourceModel()); + Q_ASSERT(_rootSourceItem); + + SourceItem *sourceItem = sourceToInternal(parent); + beginRemoveRows(QModelIndex(), sourceItem->pos() + start + 1, sourceItem->pos() + end + 1); + + // sanity check - if that check fails our indexes would be messed up + for(int row = start; row <= end; row++) { + if(sourceItem->child(row)->childCount() > 0) { + qWarning() << "on_rowsAboutToBeRemoved(): sourceModel() removed rows which have childs on their own!" << sourceModel()->index(row, 0, parent); + Q_ASSERT(false); + } + } +} + +void FlatProxyModel::on_rowsInserted(const QModelIndex &parent, int start, int end) { + Q_ASSERT(sourceModel()); + Q_ASSERT(_rootSourceItem); + + SourceItem *sourceItem = sourceToInternal(parent); + Q_ASSERT(sourceItem); + + // sanity check - if that check fails our indexes would be messed up + for(int row = start; row <= end; row++) { + QModelIndex child = sourceModel()->index(row, 0, parent); + if(sourceModel()->rowCount(child) > 0) { + qWarning() << "on_rowsInserted(): sourceModel() inserted rows which already have childs on their own!" << child; + Q_ASSERT(false); + } + } + + endInsertRows(); +} + +void FlatProxyModel::on_rowsRemoved(const QModelIndex &parent, int start, int end) { + Q_ASSERT(sourceModel()); + Q_ASSERT(_rootSourceItem); + + SourceItem *sourceItem = sourceToInternal(parent); + Q_ASSERT(sourceItem); + + SourceItem *prevItem = sourceItem; + if(start > 0) { + prevItem = sourceItem->child(start - 1); + while(prevItem->childCount() > 0) { + prevItem = prevItem->child(prevItem->childCount() - 1); + } + } + Q_ASSERT(prevItem); + + SourceItem *nextItem = sourceItem->child(end)->next(); + + int newPos = prevItem->pos() + 1; + prevItem->setNext(nextItem); + + while(nextItem) { + nextItem->setPos(newPos); + newPos++; + nextItem = nextItem->next(); + } + + SourceItem *childItem; + for(int row = start; row <= end; row++) { + childItem = sourceItem->_childs.takeAt(start); + delete childItem; + } + + endRemoveRows(); +} + +// integrity Tets +void FlatProxyModel::linkTest() const { + qDebug() << "Checking FlatProxyModel for linklist integrity"; + if(!_rootSourceItem) + return; + + int pos = -1; + SourceItem *item = _rootSourceItem; + while(true) { + qDebug() << item << ":" << item->pos() << "==" << pos; + Q_ASSERT(item->pos() == pos); + pos++; + if(!item->next()) + break; + item = item->next(); + } + qDebug() << "Last item in linklist:" << item << item->pos(); + + int lastPos = item->pos(); + item = _rootSourceItem; + while(item->childCount() > 0) { + item = item->child(item->childCount() - 1); + } + qDebug() << "Last item in tree:" << item << item->pos(); + Q_ASSERT(lastPos == item->pos()); + + qDebug() << "success!"; +} + +void FlatProxyModel::completenessTest() const { + qDebug() << "Checking FlatProxyModel for Completeness:"; + int pos = -1; + checkChildCount(QModelIndex(), _rootSourceItem, pos); + qDebug() << "success!"; +} + +void FlatProxyModel::checkChildCount(const QModelIndex &index, const SourceItem *item, int &pos) const { + if(!sourceModel()) + return; + + qDebug() << index << "(Item:" << item << "):" << sourceModel()->rowCount(index) << "==" << item->childCount(); + qDebug() << "ProxyPos:" << item->pos() << "==" << pos; + Q_ASSERT(sourceModel()->rowCount(index) == item->childCount()); + + for(int row = 0; row < sourceModel()->rowCount(index); row++) { + pos++; + checkChildCount(sourceModel()->index(row, 0, index), item->child(row), pos); + } +} + +// ======================================== +// SourceItem +// ======================================== +FlatProxyModel::SourceItem::SourceItem(int row, SourceItem *parent) + : _parent(parent), + _pos(-1), + _next(0) +{ + if(parent) { + parent->_childs.insert(row, this); + } +} + +FlatProxyModel::SourceItem::~SourceItem() { + for(int i = 0; i < childCount(); i++) { + delete child(i); + } + _childs.clear(); +} + +int FlatProxyModel::SourceItem::sourceRow() const { + if(!parent()) + return -1; + else + return parent()->_childs.indexOf(const_cast(this)); +} + +FlatProxyModel::SourceItem *FlatProxyModel::SourceItem::findChild(int proxyPos) const { + Q_ASSERT(proxyPos > pos()); + Q_ASSERT(_childs.count() > 0); + Q_ASSERT(proxyPos >= _childs[0]->pos()); + + int start = 0; + int end = _childs.count() - 1; + int pivot; + while(end - start != 1) { + pivot = (end + start) / 2; + if(_childs[pivot]->pos() > proxyPos) + end = pivot; + else + start = pivot; + } + + if(_childs[end]->pos() <= proxyPos) + return _childs[end]; + else + return _childs[start]; +} diff --git a/src/uisupport/flatproxymodel.h b/src/uisupport/flatproxymodel.h new file mode 100644 index 00000000..e0067606 --- /dev/null +++ b/src/uisupport/flatproxymodel.h @@ -0,0 +1,123 @@ +/*************************************************************************** + * Copyright (C) 2005-08 by the Quassel Project * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) version 3. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef FLATPROXYMODEL_H +#define FLATPROXYMODEL_H + +#include + +class FlatProxyModel : public QAbstractProxyModel { + Q_OBJECT + +public: + FlatProxyModel(QObject *parent = 0); + + virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; + virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const; + + virtual QItemSelection mapSelectionFromSource(const QItemSelection &sourceSelection) const; + virtual QItemSelection mapSelectionToSource(const QItemSelection &proxySelection) const; + + virtual void setSourceModel(QAbstractItemModel *sourceModel); + + virtual QModelIndex index(int row, int column, const QModelIndex &parent) const; + virtual QModelIndex parent(const QModelIndex &index) const; + + virtual int rowCount(const QModelIndex &index) const; + virtual int columnCount(const QModelIndex &index) const; + +public slots: + void linkTest() const; + void completenessTest() const; + +private slots: + void on_columnsAboutToBeInserted(const QModelIndex &parent, int start, int end); + void on_columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void on_columnsInserted(const QModelIndex &parent, int start, int end); + void on_columnsRemoved(const QModelIndex &parent, int start, int end); + + void on_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); +// void on_headerDataChanged(Qt::Orientation orientation, int first, int last); + + void on_layoutAboutToBeChanged(); + void on_layoutChanged(); + + inline void on_modelAboutToBeReset() { reset(); } + // void on_modelReset(); + + void on_rowsAboutToBeInserted(const QModelIndex &parent, int start, int end); + void on_rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void on_rowsInserted(const QModelIndex &parent, int start, int end); + void on_rowsRemoved(const QModelIndex &parent, int start, int end); + +private: + QList _childCount; + + class SourceItem; + SourceItem *_rootSourceItem; + + void insertSubTree(const QModelIndex &source_idx, bool emitInsert = true); + SourceItem *insertSubTreeHelper(SourceItem *parentItem, SourceItem *lastItem_, const QModelIndex &source_idx); + + void removeSubTree(const QModelIndex &source_idx, bool emitRemove = true); + + SourceItem *sourceToInternal(const QModelIndex &sourceIndex) const; + + void checkChildCount(const QModelIndex &index, const SourceItem *item, int &pos) const; + + class _RangeRect { + public: + int left, right, top, bottom; + SourceItem *topItem, *bottomItem; + bool operator<(const _RangeRect &other) const; + }; +}; + + +class FlatProxyModel::SourceItem { +public: + SourceItem(int row = 0, SourceItem *parent = 0); + ~SourceItem(); + + inline SourceItem *parent() const { return _parent; } + inline SourceItem *child(int i) const { return _childs[i]; } + inline int childCount() const { return _childs.count(); } + + inline int pos() const { return _pos; } + inline SourceItem *next() const { return _next; } + + int sourceRow() const; + SourceItem *findChild(int proxyPos) const; + +private: + inline void removeChild(SourceItem *item) { _childs.removeAt(_childs.indexOf(item)); } + inline void setPos(int i) { _pos = i; } + inline void setNext(SourceItem *next) { _next = next; } + + SourceItem *_parent; + QList _childs; + int _pos; + SourceItem *_next; + + friend class FlatProxyModel; +}; + + +#endif //FLATPROXYMODEL_H