/****************************************************************************
**
** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (developer.feedback@nokia.com)
**
** This file is part of the HbWidgets module of the UI Extensions for Mobile.
**
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at developer.feedback@nokia.com.
**
****************************************************************************/
#include "hbtreemodeliterator_p.h"
#include "hbtreemodeliterator_p_p.h"
HbTreeModelIterator::HbTreeModelIterator(QAbstractItemModel *model,
QModelIndex rootIndex, bool useCache)
: HbModelIterator(*new HbTreeModelIteratorPrivate())
{
Q_D(HbTreeModelIterator);
d->mUseCache = useCache;
setModel(model, rootIndex);
}
HbTreeModelIterator::~HbTreeModelIterator()
{
}
/*!
\reimp
Children of collapsed parents are not taken into account.
*/
int HbTreeModelIterator::indexCount(const QModelIndex &parent) const
{
Q_D(const HbTreeModelIterator);
int itemsCount = 0;
if (d->mModel) {
QModelIndex parentIndex = parent;
if (!parentIndex.isValid()) {
parentIndex = d->mRootIndex;
}
if (d->isInCountCache(parentIndex)) {
return d->mCachedCount.count;
}
if (parentIndex != d->mRootIndex) {
itemsCount = d->childIndexCount(parentIndex);
} else {
int rowsCount = d->mModel->rowCount(parentIndex);
itemsCount += rowsCount;
for (int i = 0; i < rowsCount; i++) {
itemsCount += d->childIndexCount(d->mModel->index(i, 0, parentIndex));
}
}
d->mCachedCount.count = itemsCount;
d->mCachedCount.index = parentIndex;
}
return itemsCount;
}
/*!
\reimp
Position depends on item state - expanded/colapsed
*/
int HbTreeModelIterator::indexPosition(const QModelIndex &index) const
{
Q_D(const HbTreeModelIterator);
int result = -1;
if (d->mModel) {
if (d->isInPositionCache(index)) {
return d->mCachedPosition.count;
} else if (d->mCachedPosition.index == d->mRootIndex
|| lessThan(d->mCachedPosition.index, index)) { //Cache has been reset, start from the beginning
result = d->searchForward(index);
} else {
result = d->searchBackward(index);
}
}
return result;
}
/*!
\reimp
Index is calculated from position and it depends on item state - expanded/colapsed
Very slow - need to interate throught whole model in worst case!
*/
QModelIndex HbTreeModelIterator::index(int pos, const QModelIndex &parent) const
{
Q_D(const HbTreeModelIterator);
QModelIndex index;
QModelIndex parentIndex = parent;
if (!parentIndex.isValid()) {
parentIndex = d->mRootIndex;
}
if (pos < 0) {
int count = indexCount(parentIndex);
pos = count - pos;
if (pos > count) {
return index;
}
}
if (d->isInCountCache(parentIndex)
&& d->mCachedCount.count >= 0
&& (d->mCachedCount.count/2) < pos) {
// is this reasonable?
// last go through whole branch to find last
// faster than nextIndex but...
int i = d->mCachedCount.count - pos;
if (i > 0) {
index = d->last(parentIndex);
--i;
}
while (i > 0) {
index = previousIndex(index);
if (!index.isValid()) break;
--i;
}
} else {
int i = 0;
index = d->first(parentIndex);
while (i < pos) {
index = nextIndex(index);
if (index.parent() != parentIndex) {
// next index must be in the same branch as parent!
QModelIndex temp = index.parent();
while (temp.isValid()
&& temp.parent() != parentIndex) {
temp = temp.parent();
}
if (!temp.isValid()) {
index = temp;
break;
}
}
if (!index.isValid()) {
break;
}
++i;
}
}
return index;
}
QModelIndex HbTreeModelIterator::child(int pos, const QModelIndex &parent) const
{
Q_D(const HbTreeModelIterator);
if (d->mModel) {
return d->mModel->index(pos, 0, parent);
}
return QModelIndex();
}
int HbTreeModelIterator::childCount(const QModelIndex &parent) const
{
Q_D(const HbTreeModelIterator);
if (d->mModel) {
return d->mModel->rowCount(parent);
}
return 0;
}
/*!
\reimp
Next index for valid index is determined in following way:
- If index is in collapsed branch QModelIndex is returned
- If index has children and it is expanded then first child is returned
- Otherwise if index has next sibling then that is returned
- Otherwise next valid sibling for parent is returned
- Otherwise QModelIndex is returned
\a index must belong to mRootIndex branch, otherwise result is not determined.
To get first index use nextIndex(QModelIndex()).
*/
QModelIndex HbTreeModelIterator::nextIndex(const QModelIndex &index) const
{
Q_D(const HbTreeModelIterator);
if (d->mModel) {
QModelIndex result;
if (!index.isValid()) {
result = d->first(d->mRootIndex);
} else {
// nextIndex(d->mRootIndex) == first(d->mRootIndex)
if (!d->isExpandedBranch(index.parent())) {
return QModelIndex();
}
QModelIndex tmpIndex = index;
do {
if (d->isExpanded(tmpIndex)) {
// if expanded, take first child - going deeper
result = tmpIndex.child(0, 0);
}
if (!result.isValid()) {
// if not expanded, or expanded but without child
// take next on the same level
result = tmpIndex.sibling(tmpIndex.row()+1, 0);
}
while (!result.isValid()) {
// if not possible to take next on the same level, go level up
tmpIndex = tmpIndex.parent();
if (tmpIndex == d->mRootIndex
|| !tmpIndex.isValid()) {
tmpIndex = QModelIndex();
break;
}
result = tmpIndex.sibling(tmpIndex.row()+1, 0);
}
}
while (!result.isValid() && tmpIndex.isValid());
}
return result;
} // if (mModel)
return QModelIndex();
}
/*!
\reimp
Previous index for valid index is determined in following way:
- If index is in collapsed branch QModelIndex is returned
- If index has previous sibling last child from it is returned
- Otherwise previous sibling is returned
- Otherwise parent index is returned
- Otherwise QModelIndex is returned
If index was invalid then last valid is returned.
\a index must belong to mRootIndex branch, otherwise result is not determined.
To get last index use previousIndex(QModelIndex()).
*/
QModelIndex HbTreeModelIterator::previousIndex(const QModelIndex &index) const
{
Q_D(const HbTreeModelIterator);
if (d->mModel) {
QModelIndex result;
if (!index.isValid()) {
result = d->last(d->mRootIndex);
} else if (index != d->mRootIndex) { // can't go outside mRootIndex
if (!d->isExpandedBranch(index.parent())) {
return QModelIndex();
}
QModelIndex tmpIndex = index;
// try to take previous on the same level
result = tmpIndex.sibling(tmpIndex.row()-1, 0);
if (result.isValid()) {
// take last
tmpIndex = d->last(result);
if (tmpIndex.isValid()) {
result = tmpIndex;
}
} else {
result = tmpIndex.parent();
if (result == d->mRootIndex) {
result = QModelIndex();
}
}
}
return result;
}
return QModelIndex();
}
void HbTreeModelIterator::setModel(QAbstractItemModel *model,
QModelIndex rootIndex)
{
Q_D(HbTreeModelIterator);
if (model != d->mModel) {
d->resetCache();
d->setModel(model, rootIndex);
if (d->mModel) {
connect(d->mModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
this, SLOT(rowsInserted(QModelIndex,int,int)));
connect(d->mModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
this, SLOT(rowsRemoved(QModelIndex,int,int)));
connect(d->mModel, SIGNAL(columnsInserted(QModelIndex,int,int)),
this, SLOT(columnsInserted(QModelIndex,int,int)));
connect(d->mModel, SIGNAL(columnsRemoved(QModelIndex,int,int)),
this, SLOT(columnsRemoved(QModelIndex,int,int)));
}
} else {
setRootIndex(rootIndex);
}
}
void HbTreeModelIterator::setRootIndex(QModelIndex rootIndex)
{
if (d->mRootIndex != rootIndex) {
Q_D(HbTreeModelIterator);
d->setRootIndex(rootIndex);
d->resetCache();
}
}
void HbTreeModelIterator::setItemContainer(HbAbstractItemContainer *itemContainer,
int expansionKey)
{
Q_D(HbTreeModelIterator);
d->mItemContainer = itemContainer;
d->mExpansionKey = expansionKey;
}
bool HbTreeModelIterator::lessThan(const QModelIndex &index1,
const QModelIndex &index2) const
{
Q_D(const HbTreeModelIterator);
if (index1 == index2) {
return false;
}
//- Create lists from item to root so that the root will be the first item in the list (prepend...)
QModelIndexList indexList1 = d->createParentChainList(index1);
QModelIndexList indexList2 = d->createParentChainList(index2);
int list1Count = indexList1.count();
int list2Count = indexList2.count();
int minCount = qMin(list1Count, list2Count);
//- Loop the lists starting from the root until different item is found or if other list ends.
//- If difference is found the comparison can be made compring the rows
//- If no difference has been found and other list ends step out of the loop
for (int i = 0; i < minCount; i++) {
if (indexList1.at(i)!= indexList2.at(i)) {
if (indexList1.at(i).row() < indexList2.at(i).row()) {
return true;
} else {
return false;
}
}
}
//- Now we can be sure that the items to be compared are from the same branch.
//- The longer list indicates lower position
return (list1Count < list2Count);
}
void HbTreeModelIterator::itemStateChanged(const QModelIndex &index, int stateKey)
{
Q_UNUSED(index);
Q_D(HbTreeModelIterator);
if (stateKey & d->mExpansionKey) {
d->resetCache();
}
}
void HbTreeModelIterator::rowsInserted(const QModelIndex &parent, int start, int end)
{
Q_D(HbTreeModelIterator);
Q_UNUSED(parent);
Q_UNUSED(start);
Q_UNUSED(end);
d->resetCache();
}
void HbTreeModelIterator::rowsRemoved(const QModelIndex &parent, int start, int end)
{
Q_D(HbTreeModelIterator);
Q_UNUSED(parent);
Q_UNUSED(start);
Q_UNUSED(end);
d->resetCache();
}
void HbTreeModelIterator::columnsInserted(const QModelIndex &parent, int start, int end)
{
Q_D(HbTreeModelIterator);
Q_UNUSED(parent);
Q_UNUSED(start);
Q_UNUSED(end);
d->resetCache();
}
void HbTreeModelIterator::columnsRemoved(const QModelIndex &parent, int start, int end)
{
Q_D(HbTreeModelIterator);
Q_UNUSED(parent);
Q_UNUSED(start);
Q_UNUSED(end);
d->resetCache();
}
#include "moc_hbtreemodeliterator_p.cpp"