diff -r f7ac710697a9 -r 06ff229162e9 src/hbwidgets/itemviews/hbabstractitemcontainer_p.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hbwidgets/itemviews/hbabstractitemcontainer_p.cpp Fri May 14 16:09:54 2010 +0300 @@ -0,0 +1,1192 @@ +/**************************************************************************** +** +** 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 "hbabstractitemcontainer_p.h" +#include "hbabstractitemcontainer_p_p.h" + +#include "hbabstractviewitem.h" +#include "hbabstractitemview.h" +#include "hbabstractitemview_p.h" +#include "hbmodeliterator.h" +#include + +#include +#include +#include +#include + + +/*! + HbAbstractItemContainer is used in HbAbstractItemView to hold the layout and view items. + Container should have a layout, otherwise its size is zero always. + HbAbstractItemContainer can have any kind of layout as it child. +*/ + +/*! + Function is called after new \a item was added into \a index position into + container. +*/ + +/*! + Function is called after the \a item was removed from the container. +*/ + +/*! + Function is called when container needs to be resized. +*/ + +/*! + Returns the default prototype. + + Subclasses of this class must implement this function to introduce their own default prototype. + Default prototype is used to create view items unless the class user excplicitly sets prototype with setItemPrototype method. + Implementation of this method must construct and return a new view item widget. +*/ + +const int HB_DEFAULT_BUFFERSIZE = 4; +const int UpdateItemBufferEvent = QEvent::registerEventType(); + +HbAbstractItemContainerPrivate::HbAbstractItemContainerPrivate() : + HbWidgetPrivate(), + mItemView(0), + mBufferSize(HB_DEFAULT_BUFFERSIZE), + mItemRecycling(false), + mUniformItemSizes(false) +{ +} + +HbAbstractItemContainerPrivate::~HbAbstractItemContainerPrivate() +{ +} + + +void HbAbstractItemContainerPrivate::init() +{ + Q_Q(HbAbstractItemContainer); + q->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); +} + +/*! + Returns given item's bounding rectangle in scene coordinates. + */ +QRectF HbAbstractItemContainerPrivate::itemBoundingRect(const QGraphicsItem *item) const +{ + Q_Q(const HbAbstractItemContainer); + if (q->layout()) { + q->layout()->activate(); + } + return item->mapToItem(mItemView, item->boundingRect()).boundingRect(); +} + +void HbAbstractItemContainerPrivate::firstAndLastVisibleBufferIndex( + int& firstVisibleBufferIndex, + int& lastVisibleBufferIndex, + const QRectF &viewRect, + bool fullyVisible) const +{ + Q_Q(const HbAbstractItemContainer); + + if (q->layout() && !q->layout()->isActivated()) { + q->layout()->activate(); + } + + firstVisibleBufferIndex = -1; + lastVisibleBufferIndex = -1; + + int count = mItems.count(); + for (int i = 0; i < count; ++i) { + if (visible(mItems.at(i), viewRect, fullyVisible)) { + if (firstVisibleBufferIndex == -1) { + firstVisibleBufferIndex = i; + } + lastVisibleBufferIndex = i; + } else if ( lastVisibleBufferIndex != -1 ) { + // no need to check the remaining ones. + break; + } + } +} + +/*! + \private + + Returns true if given item is located within viewport (i.e. view), otherwise + returns false. If fullyVisible parameter is true method will return true only + for item that is shown fully. In this case for partially visible items false is returned. +*/ +bool HbAbstractItemContainerPrivate::visible(HbAbstractViewItem* item, const QRectF &viewRect, bool fullyVisible) const +{ + if (item) { + QRectF itemRect(itemBoundingRect(item)); + if (fullyVisible) { + if (viewRect.contains(itemRect)) { + return true; + } + } else { + if (viewRect.intersects(itemRect)) { + return true; + } + } + } + + return false; +} + +/*! + Clears the prototype list and deletes the prototypes. +*/ +void HbAbstractItemContainerPrivate::deletePrototypes() +{ + qDeleteAll(mPrototypes); + mPrototypes.clear(); +} + +int HbAbstractItemContainerPrivate::findStateItem(const QModelIndex &index) const +{ + for (int current = 0; current < mItemStateList.count(); ++current) { + if (mItemStateList.at(current).index == index) { + return current; + } + } + return -1; +} + +void HbAbstractItemContainerPrivate::initPrototype(HbAbstractViewItem *prototype) const +{ + prototype->setParentItem(mItemView); + prototype->setItemView(mItemView); + prototype->resize(QSizeF(0, 0)); + prototype->hide(); +} + +HbAbstractViewItem* HbAbstractItemContainerPrivate::createItem(const QModelIndex& index) +{ + Q_Q(HbAbstractItemContainer); + + HbAbstractViewItem *result = 0; + HbAbstractViewItem *prototype = itemPrototype(index); + if (prototype) { + result = q_check_ptr(prototype->createItem()); + Q_ASSERT_X(result->prototype() == prototype, "HbAbstractItemContainerPrivate::createItem", "Copy constructor must be used for creating concrete view items in createItem(). Create your custom view item with 'new YourCustomViewItem(*this)' instead of 'new YourCustomViewItem(this)'"); + result->setParentItem(q); + + emit q->itemCreated(result); + } + return result; +} + +HbAbstractViewItem* HbAbstractItemContainerPrivate::itemPrototype(const QModelIndex& index) const +{ + Q_Q(const HbAbstractItemContainer); + + if (mPrototypes.isEmpty()) { + HbAbstractViewItem *defaultPrototype = q->createDefaultPrototype(); + if (defaultPrototype) { + initPrototype(defaultPrototype); + + mPrototypes.append(defaultPrototype); + } + } + + HbAbstractViewItem *result = 0; + int count = mPrototypes.count() - 1; + for (int i = count; i >= 0; i--) { + if (mPrototypes[i]->canSetModelIndex(index)) { + result = mPrototypes[i]; + break; + } + } + return result; +} + + +/*! + \private + + Updates item buffer to contain correct amount of items for the current situation. +*/ +void HbAbstractItemContainerPrivate::updateItemBuffer() +{ + Q_Q(HbAbstractItemContainer); + + if (!mItemView) { + return; + } + + int targetCount = q->maxItemCount(); + int itemCount = mItems.count(); + + if (itemCount == targetCount) { + return; + } + + // Store the first item position related to view. + QPointer firstItem = mItems.value(qMax(0, itemCount - targetCount)); + QPointF firstItemPos; + if (firstItem) { + firstItemPos = itemBoundingRect(firstItem).topLeft(); + } + + // Perform the incresing/decreasing + if (itemCount < targetCount) { + increaseBufferSize(targetCount - itemCount); + } else { + decreaseBufferSize(itemCount - targetCount); + } + + restoreItemPosition(firstItem, firstItemPos); +} + +/*! + Increases the item buffer size with given \a amount. + + First tries to add as many items after the last item with valid + index as it can. If there is not enough valid indexes then inserts items + before the first item with valid index. +*/ +void HbAbstractItemContainerPrivate::increaseBufferSize(int amount) +{ + Q_Q(HbAbstractItemContainer); + + // Append new items. + QModelIndex index; + for (int i = mItems.count() - 1; i >= 0; --i) { + index = mItems.at(i)->modelIndex(); + if (index.isValid()) { + break; + } + } + + int itemsAdded = 0; + // in practise following conditions must apply: itemview is empty and scrollTo() has been called. + // Starts populating items from given mFirstItemIndex + if ( !index.isValid() + && mFirstItemIndex.isValid()) { + index = mFirstItemIndex; + q->insertItem(mItems.count(), index); + itemsAdded++; + + mFirstItemIndex = QModelIndex(); + } + + while (itemsAdded < amount) { + index = mItemView->modelIterator()->nextIndex(index); + if (!index.isValid()) { + break; + } + + q->insertItem(mItems.count(), index); + itemsAdded++; + } + + if (itemsAdded == amount) { + return; + } + + // Prepend new items. + for (int i = 0; i < mItems.count(); ++i) { + index = mItems.at(i)->modelIndex(); + if (index.isValid()) { + break; + } + } + + while (itemsAdded < amount) { + index = mItemView->modelIterator()->previousIndex(index); + if (!index.isValid()) { + break; + } + + q->insertItem(0, index); + itemsAdded++; + } +} + +/*! + Decreases the item buffer size with given \a amount. + + Tries to avoid deleting visible items and keep the buffer balanced. +*/ +void HbAbstractItemContainerPrivate::decreaseBufferSize(int amount) +{ + Q_UNUSED(amount) + + int firstVisible = 0; + int lastVisible = 0; + firstAndLastVisibleBufferIndex(firstVisible, lastVisible, mItemView->geometry(), false); + + int deletableItemsOnTop = firstVisible - 1; + int deletableItemsOnBottom = mItems.count() - lastVisible - 1; + int itemsDeleted = 0; + HbAbstractViewItem* item = 0; + + // in decreasing the buffer we try to keep it balanced + while (itemsDeleted < amount ) { + if (deletableItemsOnTop > deletableItemsOnBottom) { + item = mItems.takeFirst(); + deletableItemsOnTop--; + } else { + item = mItems.takeLast(); + deletableItemsOnBottom--; + } + deleteItem(item); + ++itemsDeleted; + } + +} + +/*! + \private +*/ +HbAbstractViewItem* HbAbstractItemContainerPrivate::item(const QModelIndex &index) const +{ + int itemCount = mItems.count(); + for (int i = 0; i < itemCount; ++i) { + // This could use binary search as model indexes are in sorted. + if (mItems.at(i)->modelIndex() == index) { + return mItems.at(i); + } + } + + // TODO: The lower (commented out) part of the code is an optimized version of the above. + // However, there are problems with TreeView's deep models concerning the optimized version. + // The optimized version should be fixed and taken into use later on. + + /* + int itemCount = mItems.count(); + if (itemCount > 0) { + if (index.isValid()) { + int itemIndex = mItemView->indexPosition(index) - mItemView->indexPosition(mItems.first()->modelIndex()); + return mItems.value(itemIndex); + } else { + for (int i = 0; i < itemCount; ++i) { + // This could use binary search as model indexes are in sorted. + HbAbstractViewItem *item = mItems.at(i); + if (item->modelIndex() == index) { + return item; + } + } + } + } + */ + + return 0; +} + +/*! + \private + + Removes \a item with model index \a index. +*/ +void HbAbstractItemContainerPrivate::doRemoveItem(HbAbstractViewItem *item, const QModelIndex &index, bool animate) +{ + if (item) { + deleteItem(item, animate); + if (!index.isValid()) { + mItemStates.remove(index); + } + } +} + + +/*! + \private + + Deletes \a item. +*/ +void HbAbstractItemContainerPrivate::deleteItem(HbAbstractViewItem *item, bool animate) +{ + Q_Q(HbAbstractItemContainer); + + q->setItemTransientState(item->modelIndex(), item->transientState()); + mItems.removeOne(item); + q->itemRemoved(item, animate); + +#ifndef HB_EFFECTS + delete item; +#else + if (!HbEffect::effectRunning(item, "disappear") + && !HbEffect::effectRunning(item, "collapse")) { + delete item; + } +#endif +} + +/* + \private + The previous or the next index must be in the buffer. We cannot assume in this base class + that the container is a list we just have to loop through the items and find if the previous + or next exist in the buffer. Next index should not be the first one in the buffer and the + previous index should not be the last one in the buffer in order this index to the buffer. +*/ +bool HbAbstractItemContainerPrivate::intoContainerBuffer(const QModelIndex &index) const +{ + QModelIndex nextIndex = mItemView->modelIterator()->nextIndex(index); + QModelIndex previousIndex = mItemView->modelIterator()->previousIndex(index); + + int itemCount = mItems.count(); + for (int i = 0; i < itemCount; ++i) { + QModelIndex currentIndex = mItems.at(i)->modelIndex(); + if (currentIndex == nextIndex && i != 0){ + return true; + } else if (currentIndex == previousIndex && i != (itemCount - 1)) { + return true; + } + } + return false; +} + +int HbAbstractItemContainerPrivate::containerBufferIndexForModelIndex(const QModelIndex &index) const +{ + int bufferIndex = 0; + QModelIndex nextIndex = mItemView->modelIterator()->nextIndex(index); + QModelIndex previousIndex = mItemView->modelIterator()->previousIndex(index); + + while (bufferIndex < mItems.count()) { + QModelIndex currentIndex = mItems.at(bufferIndex)->modelIndex(); + if (currentIndex == nextIndex) { + break; + } + + ++bufferIndex; + + if (currentIndex == previousIndex) { + break; + } + } + return bufferIndex; +} + +void HbAbstractItemContainerPrivate::restoreItemPosition(HbAbstractViewItem *item, const QPointF &position) +{ + Q_Q(HbAbstractItemContainer); + + if (item) { + QPointF delta = itemBoundingRect(item).topLeft() - position; + if (!delta.isNull()) { + q->setPos(q->pos() - delta); + + if (mItemView) { + // this will force the HbScrollArea to adjust the content correctly. Adjustment + // is not done in the setPos generated event handling by default to speed up scrolling. + HbAbstractItemViewPrivate::d_ptr(mItemView)->adjustContent(); + } + } + } +} + +void HbAbstractItemContainerPrivate::insertItem(HbAbstractViewItem *item, int pos, const QModelIndex &index, bool animate) +{ + Q_Q(HbAbstractItemContainer); + + if (item) { + mItems.insert(pos, item); + q->itemAdded(pos, item, animate); + + q->setItemModelIndex(item, index); + } +} + +qreal HbAbstractItemContainerPrivate::getDiffWithoutScrollareaCompensation(const QPointF &delta) const +{ + Q_Q( const HbAbstractItemContainer); + const QSizeF containerSize(q->size()); + const QPointF containerPos(q->pos()); + qreal diff = 0.0; + qreal invisibleArea = 0.0; + if (delta.y() > 0) { + // space at the bottom + QSizeF viewSize = mItemView->size(); + invisibleArea = containerSize.height() - viewSize.height() + containerPos.y(); + if (invisibleArea < delta.y()) { + diff = delta.y() - invisibleArea; + } + } else { + // space at the top + invisibleArea = -containerPos.y(); + if (containerPos.y() > delta.y()) { + diff = delta.y() + invisibleArea; + } + } + + return diff; +} + +/*! + Constructs a new HbAbstractItemContainer with \a parent. +*/ +HbAbstractItemContainer::HbAbstractItemContainer(QGraphicsItem *parent) : + HbWidget(*new HbAbstractItemContainerPrivate(), parent) +{ + Q_D(HbAbstractItemContainer); + + d->q_ptr = this; + d->init(); +} + +/*! + Constructs an item with private class object \a dd and \a parent. +*/ +HbAbstractItemContainer::HbAbstractItemContainer(HbAbstractItemContainerPrivate &dd, QGraphicsItem *parent) : + HbWidget(dd, parent) +{ + Q_D(HbAbstractItemContainer); + + d->q_ptr = this; + d->init(); +} + +/*! + Destroys the container. +*/ +HbAbstractItemContainer::~HbAbstractItemContainer() +{ +} + +/*! + \reimp +*/ +bool HbAbstractItemContainer::event(QEvent *e) +{ + if (e->type() == QEvent::LayoutRequest) { + QGraphicsWidget *parentWid = parentWidget(); + if (!parentLayoutItem() + && parentWid) { + HbApplication::postEvent(parentWid, new QEvent(QEvent::LayoutRequest)); + } + Q_D(HbAbstractItemContainer); + d->updateItemBuffer(); + } else if (e->type() == UpdateItemBufferEvent) { + Q_D(HbAbstractItemContainer); + d->updateItemBuffer(); + } + + return HbWidget::event(e); +} + +/*! + Returns view item object corresponding given model \a index. This might be 0 pointer if + there is no view item representing given index or given \a index is invalid. +*/ +HbAbstractViewItem* HbAbstractItemContainer::itemByIndex(const QModelIndex &index) const +{ + Q_D(const HbAbstractItemContainer); + + if (!index.isValid()) { + return 0; + } + return d->item(index); +} + +/*! + This function is provided for convenience. + + It removes all the items from the container and clears the internal state model. +*/ +void HbAbstractItemContainer::removeItems() +{ + Q_D(HbAbstractItemContainer); + + qDeleteAll(d->mItems); + d->mItems.clear(); + d->mItemStateList.clear(); + d->mItemStates.clear(); +} + +/*! + Returns the item view that container is connected. +*/ +HbAbstractItemView *HbAbstractItemContainer::itemView() const +{ + Q_D(const HbAbstractItemContainer); + return d->mItemView; +} + +/*! + Sets item \a view that container is connected. +*/ +void HbAbstractItemContainer::setItemView(HbAbstractItemView *view) +{ + Q_D(HbAbstractItemContainer); + + if (view != d->mItemView) { + if (d->mItemView) { + d->mItemView->removeEventFilter(this); + } + + d->mItemView = view; + + foreach (HbAbstractViewItem *prototype, d->mPrototypes) { + d->initPrototype(prototype); + } + + if (d->mItemView) { + setParentItem(view); + d->mItemView->installEventFilter(this); + } + } +} + +/*! + Assigns new model \a index to the given \a item. Item's current state is saved + and state for \a index is restored to item. +*/ +void HbAbstractItemContainer::setItemModelIndex(HbAbstractViewItem *item, const QModelIndex &index) +{ + + if (item && item->modelIndex() != index) { + + setItemTransientState(item->modelIndex(), item->transientState()); + + // Transfer the state from item currently representing index to new item, if such exists. + HbAbstractViewItem *oldItem = itemByIndex(index); + + if (oldItem) { + item->setTransientState(oldItem->transientState()); + } else { + item->setTransientState(itemTransientState(index)); + } + + + item->setModelIndex(index); + } +} + +/*! + Sets item's model indexes starting from given \a startIndex. If \a startIndex is + QModelIndex() then startIndex is taken from the first item. + + \note If there are not enough model indexes from \a startIndex to last model index + for all the items then QModelIndex() is assigned to rest of the view items. +*/ +void HbAbstractItemContainer::setModelIndexes(const QModelIndex &startIndex) +{ + Q_D(HbAbstractItemContainer); + + if (!d->mItemView || !d->mItemView->model()) { + return; + } + + QModelIndex index = startIndex; + if (!index.isValid()) { + if (!d->mItems.isEmpty()) { + index = d->mItems.first()->modelIndex(); + } + + if (!index.isValid()) { + index = d->mItemView->modelIterator()->nextIndex(index); + } + } + + QModelIndexList indexList; + + int itemCount(d->mItems.count()); + + if (itemCount != 0 && index.isValid()) { + indexList.append(index); + } + + for (int i = indexList.count(); i < itemCount; ++i) { + index = d->mItemView->modelIterator()->nextIndex(indexList.last()); + + if (index.isValid()) { + indexList.append(index); + } else { + break; + } + } + + for (int i = indexList.count(); i < itemCount; ++i) { + index = d->mItemView->modelIterator()->previousIndex(indexList.first()); + + if (index.isValid()) { + indexList.prepend(index); + } else { + break; + } + } + + // if items have been added/removed in the middle of the buffer, there might be items + // that can be reused at the end of the buffer. The following block will scan for + // those items and move them in correct position + int lastUsedItem = -1; + for (int indexCounter = 0; indexCounter < indexList.count(); ++indexCounter) { + HbAbstractViewItem *item = d->mItems.at(indexCounter); + if (item && item->modelIndex() == indexList.at(indexCounter)) { + lastUsedItem = indexCounter; + } else { + for (int itemCounter = lastUsedItem + 1; itemCounter < d->mItems.count(); itemCounter++) { + HbAbstractViewItem *item2 = d->mItems.at(itemCounter); + if (item2->modelIndex() == indexList.at(indexCounter)) { + d->mItems.swap(indexCounter, itemCounter); + itemRemoved(d->mItems.at(indexCounter), false); + itemRemoved(d->mItems.at(itemCounter), false); + itemAdded(qMin(indexCounter, itemCounter), d->mItems.at(qMin(indexCounter, itemCounter)), false); + itemAdded(qMax(indexCounter, itemCounter), d->mItems.at(qMax(indexCounter, itemCounter)), false); + lastUsedItem = itemCounter; + break; + } + } + } + } + + int indexCount(indexList.count()); + for (int i = 0; i < itemCount; ++i) { + HbAbstractViewItem *item = d->mItems.at(i); + HbAbstractViewItem *prototype = 0; + // setItemModelIndex() is considered recycling. It is called only, if recycling is permitted + if (i < indexCount) { + prototype = d->itemPrototype(indexList.at(i)); + } + if (prototype) { + if ( prototype == item->prototype() + && d->mItemRecycling) { + setItemModelIndex(item, indexList.at(i)); + } else if ( d->mItemRecycling + || ( !d->mItemRecycling + && item->modelIndex() != indexList.at(i))) { + d->deleteItem(item); + insertItem(i, indexList.at(i)); + } // else: !d->mItemRecycling && item->modelIndex().isValid() == indexList.at(i)) + } else { + d->deleteItem(item); + itemCount--; + i--; + } + } +} + +/*! + Returns list of items inside item buffer. +*/ +QList HbAbstractItemContainer::items() const +{ + Q_D(const HbAbstractItemContainer); + return d->mItems; +} + +/*! + Adds item for model \a index to container. +*/ +void HbAbstractItemContainer::addItem(const QModelIndex &index, bool animate) +{ + Q_D(HbAbstractItemContainer); + + // Add new item if maximum item count allows it or item falls within the range of + // item buffer items. + if (d->intoContainerBuffer(index)) { + int bufferIndex = d->containerBufferIndexForModelIndex(index); + + if (bufferIndex >= d->mItems.count() + || d->mItems.at(bufferIndex)->modelIndex() != index) { + // Store the second item position related to view. + HbAbstractViewItem *referenceItem = d->mItems.value(1); + QPointF referenceItemPos; + if (referenceItem) { + referenceItemPos = d->itemBoundingRect(referenceItem).topLeft(); + } + + HbAbstractViewItem *recycledItem = 0; + QRectF viewRect = d->itemBoundingRect(d->mItemView); + + if (d->mItemRecycling && !viewRect.isEmpty()) { + // Recycling allowed. Try recycling the items from buffer. + int firstVisible = 0; + int lastVisible = 0; + d->firstAndLastVisibleBufferIndex(firstVisible, lastVisible, viewRect, false); + + int itemsOnTop = firstVisible - 1; + int itemsOnBottom = d->mItems.count() - lastVisible - 1; + + if (itemsOnBottom > 0) { + recycledItem = d->mItems.takeLast(); + } else if (itemsOnTop > 0) { + recycledItem = d->mItems.takeFirst(); + bufferIndex--; + bufferIndex = qMax(0, bufferIndex); + } + + if (recycledItem) { + itemRemoved(recycledItem, false); + d->insertItem(recycledItem, bufferIndex, index, animate); + } + } + + if (!recycledItem) { + // No recycling has happened. Insert completely new item. + insertItem(bufferIndex, index, animate); + } + + // Restore second item position. + d->restoreItemPosition(referenceItem, referenceItemPos); + + if (!recycledItem && d->mItemRecycling) { + // Resize the buffer. + d->updateItemBuffer(); + } + } + } else if (d->mItems.count() < maxItemCount()) { + d->updateItemBuffer(); + } +} + +/*! + Removes item representing \a index from container. +*/ +void HbAbstractItemContainer::removeItem(const QModelIndex &index, bool animate) +{ + Q_D(HbAbstractItemContainer); + + HbAbstractViewItem *item = d->item(index); + d->doRemoveItem(item, index, animate); +} + +/*! + Removes item from \a pos. +*/ +void HbAbstractItemContainer::removeItem(int pos, bool animate) +{ + Q_D(HbAbstractItemContainer); + + Q_ASSERT(pos >= 0 && pos < d->mItems.count()); + + HbAbstractViewItem *item = d->mItems.at(pos); + QModelIndex index = item->modelIndex(); + d->doRemoveItem(item, index, animate); +} + +/*! + Derived class should implement this function to perform item recycling based on container \a delta. + Given \a delta is the distance between container's current position and desired new position. Recycling + should be done based on the new position and function should return the actual delta. Actual delta could + be modified value from \a delta. Delta modification can be useful e.g. to compensate the item changes + that are caused by relayouting of items after changing the item position within layout. + + Default implementation does not recycle items and returns the \a delta unchanged. +*/ +QPointF HbAbstractItemContainer::recycleItems(const QPointF &delta) +{ + return delta; +} + +/*! + \reimp +*/ +bool HbAbstractItemContainer::eventFilter(QObject *obj, QEvent *event) +{ + Q_D(HbAbstractItemContainer); + + if (obj == d->mItemView) { + switch (event->type()){ + case QEvent::GraphicsSceneResize: { + viewResized(d->mItemView->size()); + d->updateItemBuffer(); + break; + } + default: + break; + } + } + + return HbWidget::eventFilter(obj, event); +} + +/*! + Returns maximum amount of items that item buffer can hold. + + Default implementation returns the total number of indexes that can + be accessed on current view. + + Derived class that supports item recycling should define their own implementation + for this function. + + \note Return value should not be more than HbAbstractItemView::indexCount() +*/ +int HbAbstractItemContainer::maxItemCount() const +{ + Q_D(const HbAbstractItemContainer); + + if (d->mItemView) { + return d->mItemView->modelIterator()->indexCount(); + } else { + return 0; + } +} + +/*! + Deletes other prototypes and sets \a prototype as only prototype. + + Returns true if the prototype list was changed; otherwise returns false. +*/ +bool HbAbstractItemContainer::setItemPrototype(HbAbstractViewItem *prototype) +{ + QList prototypeList; + prototypeList.append(prototype); + return setItemPrototypes(prototypeList); +} + +/*! + Returns the list of item prototypes. +*/ +QList HbAbstractItemContainer::itemPrototypes() const +{ + Q_D(const HbAbstractItemContainer); + + if (d->mPrototypes.isEmpty()) { + HbAbstractViewItem *defaultPrototype = createDefaultPrototype(); + if (defaultPrototype) { + d->initPrototype(defaultPrototype); + + d->mPrototypes.append(defaultPrototype); + } + } + + return d->mPrototypes; +} + +/*! + Sets the list of prototypes. + + Returns true if the prototype list was changed; otherwise returns false. +*/ +bool HbAbstractItemContainer::setItemPrototypes(const QList &prototypes) +{ + Q_D(HbAbstractItemContainer); + + bool changed = false; + + if (prototypes.count() > 0) { + if (d->mPrototypes != prototypes) { + foreach (HbAbstractViewItem *prototype, d->mPrototypes) { + if (!prototypes.contains(prototype)) { + delete prototype; + } + } + + foreach (HbAbstractViewItem *prototype, prototypes) { + if (!d->mPrototypes.contains(prototype)) { + d->initPrototype(prototype); + } + } + changed = true; + } + + d->mPrototypes = prototypes; + } + + return changed; + +} + +/*! + Returns transient state of view item with \a index. +*/ +QHash HbAbstractItemContainer::itemTransientState(const QModelIndex &index) const +{ + Q_D(const HbAbstractItemContainer); + return d->mItemStates.value(index); +} + +/*! + This is an overloaded member function, provided for convenience. + + Stores \a key with \a value of a view item with \a index into state model. + \a key is usually name of a Qt property. If \a value is invalid, state item with the \a key is removed. + + Default values of properties should not be added. +*/ +void HbAbstractItemContainer::setItemTransientStateValue(const QModelIndex &index, const QString &key, const QVariant &value) +{ + Q_D(HbAbstractItemContainer); + if (index.isValid()) { + QHash stateItem = d->mItemStates.value(index); + if (!value.isValid()) { + stateItem.remove(key); + } else { + stateItem.insert(key, value); + } + if (stateItem.count()) { + d->mItemStates.insert(index, stateItem); + } else { + d->mItemStates.remove(index); + } + } else { + d->mItemStates.remove(index); + } +} + + +/*! + Stores state of a view item with \a index into item state model. State of the view item is usually + retrieved by calling HbAbstractViewItem::transientState(). + + Existing state is replaced. If \a state is empty, existing state is removed. + Default values of state items should not be added into \a state. + + \sa HbAbstractViewItem::transientState() +*/ +void HbAbstractItemContainer::setItemTransientState(const QModelIndex &index, QHash state) +{ + Q_D(HbAbstractItemContainer); + if (index.isValid() && state.count()) { + d->mItemStates.insert(index, state); + } else { + d->mItemStates.remove(index); + } +} + +/*! + \reimp +*/ +QVariant HbAbstractItemContainer::itemChange(GraphicsItemChange change, const QVariant & value) +{ + return HbWidget::itemChange(change, value); +} + +/*! + Returns the model indexes of items that are located on top left and bottom right corners + of visible area. +*/ +void HbAbstractItemContainer::firstAndLastVisibleModelIndex( + QModelIndex& firstVisibleModelIndex, + QModelIndex& lastVisibleModelIndex, + bool fullyVisible) const +{ + Q_D(const HbAbstractItemContainer); + + QRectF viewRect(d->itemBoundingRect(d->mItemView)); + + int firstVisibleBufferIndex( -1 ); + int lastVisibleBufferIndex( -1 ); + d->firstAndLastVisibleBufferIndex( firstVisibleBufferIndex, lastVisibleBufferIndex, viewRect, fullyVisible ); + if (firstVisibleBufferIndex != -1) { + firstVisibleModelIndex = d->mItems.at(firstVisibleBufferIndex)->modelIndex(); + } + if (lastVisibleBufferIndex != -1) { + lastVisibleModelIndex = d->mItems.at(lastVisibleBufferIndex)->modelIndex(); + } +} + +/*! + Clears the state model. +*/ +void HbAbstractItemContainer::removeItemTransientStates() +{ + Q_D(HbAbstractItemContainer); + d->mItemStateList.clear(); + d->mItemStates.clear(); +} + +/*! + Sets item recycling to \a enabled. + By default recycling is off. + */ +void HbAbstractItemContainer::setItemRecycling(bool enabled) +{ + Q_D(HbAbstractItemContainer); + if (d->mItemRecycling != enabled) { + d->mItemRecycling = enabled; + if (!enabled) { + removeItemTransientStates(); + } + + d->updateItemBuffer(); + } +} + +/*! + Returns whether item recycling feature is in use. + */ +bool HbAbstractItemContainer::itemRecycling() const +{ + Q_D(const HbAbstractItemContainer); + return d->mItemRecycling; +} + +/*! + Sets the feature informing whether all items in the item view have the same size. + In case all the items have the same size, the item view can do some + optimizations for performance purposes. +*/ +void HbAbstractItemContainer::setUniformItemSizes(bool enable) +{ + Q_D(HbAbstractItemContainer); + d->mUniformItemSizes = enable; +} + +/*! + Returns whether the uniform item sizes feature is in use. + */ +bool HbAbstractItemContainer::uniformItemSizes() const +{ + Q_D(const HbAbstractItemContainer); + return d->mUniformItemSizes; +} + +/*! + Inserts item for \a index to \a pos. +*/ +void HbAbstractItemContainer::insertItem(int pos, const QModelIndex &index, bool animate) +{ + Q_D(HbAbstractItemContainer); + HbAbstractViewItem *item = d->createItem(index); + d->insertItem(item, pos, index, animate); +} + +/*! + Reset the internal state of the container. +*/ +void HbAbstractItemContainer::reset() +{ + // position need to be reseted while changing model + setPos(0.0, 0.0); + removeItems(); + QCoreApplication::postEvent(this, new QEvent((QEvent::Type)UpdateItemBufferEvent)); +} + +/*! +*/ +void HbAbstractItemContainer::resizeContainer() +{ + Q_D(HbAbstractItemContainer); + + QSizeF newSize = effectiveSizeHint(Qt::PreferredSize); + + if (d->mItemView) { + Qt::Orientations scrollingDirections = d->mItemView->scrollDirections(); + + if (!scrollingDirections.testFlag(Qt::Vertical)) { + newSize.setHeight(d->mItemView->size().height()); + } + + if (!scrollingDirections.testFlag(Qt::Horizontal)) { + newSize.setWidth(d->mItemView->size().width()); + } + } + + resize(newSize); +} + +#include "moc_hbabstractitemcontainer_p.cpp" +