diff -r 000000000000 -r 16d8024aca5e src/hbwidgets/itemviews/hbabstractitemview_p.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hbwidgets/itemviews/hbabstractitemview_p.cpp Mon Apr 19 14:02:13 2010 +0300 @@ -0,0 +1,969 @@ +/**************************************************************************** +** +** 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 "hbabstractitemview_p.h" +#include "hbabstractitemview.h" +#include "hbabstractviewitem.h" +#include "hbabstractitemcontainer.h" +#include "hbmodeliterator.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +HbAbstractItemViewPrivate::HbAbstractItemViewPrivate() : + mSelectionMode(HbAbstractItemView::NoSelection), + mOptions(NoOptions), + mSelectionSettings(None), + mHitItem(0), + mContainer(0), + mSelectionModel(0), + mContSelectionAction(QItemSelectionModel::NoUpdate), + mWasScrolling(false), + mFilterRemoved(false), + mClearingSelection(false), + mAnimateItems(false), + mPostponedScrollHint(HbAbstractItemView::PositionAtTop), + mPreviousSelectedCommand(QItemSelectionModel::NoUpdate), + mInstantClickedModifiers(0), + mAnimationTimer(0), + mModelIterator(0), + mEnabledAnimations(HbAbstractItemView::All) +{ +} + +HbAbstractItemViewPrivate::~HbAbstractItemViewPrivate() +{ + if (mModelIterator) { + delete mModelIterator; + mModelIterator = 0; + } +} + +/*! + +*/ +void HbAbstractItemViewPrivate::init(HbAbstractItemContainer *container, HbModelIterator *modelIterator) +{ + Q_Q(HbAbstractItemView); + + q->setLongPressEnabled(true); + q->setFlag(QGraphicsItem::ItemIsFocusable, true); + q->setFocusPolicy(Qt::StrongFocus); + + q->setContentWidget(container); + + mContainer = container; + mContainer->setItemView(q); + + mModelIterator = modelIterator; + + HbMainWindow *window = q->mainWindow(); + if (window + && q->scene()) { // added to scene + q->connect(window, SIGNAL(aboutToChangeOrientation()), + q, SLOT(orientationAboutToBeChanged())); + + q->connect(window, SIGNAL(orientationChanged(Qt::Orientation)), + q, SLOT(orientationChanged(Qt::Orientation))); + + if (q->verticalScrollBar()) { + q->verticalScrollBar()->installSceneEventFilter(q); + } + if (q->horizontalScrollBar()) { + q->horizontalScrollBar()->installSceneEventFilter(q); + } + } +} + +/*! + Replaces current model with the given one. This deletes the existing + view items and calls reset() to update the view to correspond to + current model. +*/ +void HbAbstractItemViewPrivate::setModel(QAbstractItemModel *model) +{ + Q_Q(HbAbstractItemView); + + if (model != mModelIterator->model()) { + mAnimateItems = false; + clearCurrentModel(); + mModelIterator->setModel(model); + initializeNewModel(); + + q->reset(); + + if (!mAnimationTimer) { + mAnimationTimer = new QTimer(q); + mAnimationTimer->setObjectName(QString("animationTimer")); + mAnimationTimer->setSingleShot(true); + + QObject::connect(mAnimationTimer, SIGNAL(timeout()), q, SLOT(_q_animationEnabled())); + } + + mAnimationTimer->start(3000); + } +} + +/*! + Resets current model,selection Model,mRootIndex and mCurrentIndex to null. +*/ +void HbAbstractItemViewPrivate::clearCurrentModel() +{ + Q_Q(HbAbstractItemView); + if (mModelIterator->model()) { + QAbstractItemModel *model = mModelIterator->model(); + q->disconnect(model, SIGNAL(destroyed()), + q, SLOT(_q_modelDestroyed())); + q->disconnect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), + q, SLOT( dataChanged(QModelIndex,QModelIndex))); + q->disconnect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), + q, SLOT(rowsInserted(QModelIndex,int,int))); + q->disconnect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), + q, SLOT(rowsRemoved(QModelIndex,int,int))); + q->disconnect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), + q, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int))); + q->disconnect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), + q, SLOT(rowsAboutToBeInserted(QModelIndex,int,int))); + q->disconnect(model, SIGNAL(columnsInserted(QModelIndex,int,int)), + q, SLOT(columnsInserted(QModelIndex,int,int))); + q->disconnect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), + q, SLOT(columnsAboutToBeInserted(QModelIndex,int,int))); + q->disconnect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)), + q, SLOT(columnsRemoved(QModelIndex,int,int))); + q->disconnect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), + q, SLOT(columnsAboutToBeRemoved(QModelIndex,int,int))); + q->disconnect(model, SIGNAL(modelReset()), q, SLOT(reset())); + q->disconnect(model, SIGNAL(layoutChanged()), q, SLOT(_q_layoutChanged())); + + mModelIterator->setModel(0); + } + + setSelectionModel(0); + + mCurrentIndex = QModelIndex(); + mModelIterator->setRootIndex(QPersistentModelIndex()); +} + +/*! + Updates current selectionModel to selectionModel.If selectionModel is invalid, current + selectionModel is not updated. +*/ +void HbAbstractItemViewPrivate::setSelectionModel(QItemSelectionModel *selectionModel) +{ + Q_Q( HbAbstractItemView ); + if (selectionModel + && selectionModel->model() != mModelIterator->model()) { + qWarning("QAbstractItemView::setSelectionModel() failed: " + "Trying to set a selection model, which works on " + "a different model than the view."); + return; + } + + if (mSelectionModel) { + q->disconnect(mSelectionModel, SIGNAL(selectionChanged(QItemSelection, QItemSelection)), + q, SLOT(currentSelectionChanged(QItemSelection, QItemSelection))); + + q->disconnect(mSelectionModel, SIGNAL(currentChanged(QModelIndex, QModelIndex)), + q, SLOT(currentIndexChanged(QModelIndex, QModelIndex))); + + delete mSelectionModel; + mSelectionModel = 0; + } + + mSelectionModel = selectionModel; + + if (mSelectionModel) { + q->connect(mSelectionModel, SIGNAL(selectionChanged(QItemSelection, QItemSelection)), + q, SLOT(currentSelectionChanged(QItemSelection, QItemSelection))); + q->connect(mSelectionModel, SIGNAL(currentChanged(QModelIndex, QModelIndex)), + q, SLOT(currentIndexChanged(QModelIndex, QModelIndex))); + } +} + +/*! + Initializes newModel +*/ +void HbAbstractItemViewPrivate::initializeNewModel() +{ + Q_Q(HbAbstractItemView); + + if (mModelIterator->model()) { + QAbstractItemModel *model = mModelIterator->model(); + // These asserts do basic sanity checking of the model + Q_ASSERT_X(model->index(0,0) == model->index(0,0), + "HbAbstractItemView::setModel", + "A model should return the exact same index " + "(including its internal id/pointer) when asked for it twice in a row."); + Q_ASSERT_X(model->index(0,0).parent() == QModelIndex(), + "HbAbstractItemView::setModel", + "The parent of a top level index should be invalid"); + + q->connect(model, SIGNAL(destroyed()), + q, SLOT(_q_modelDestroyed())); + q->connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), + q, SLOT( dataChanged(QModelIndex,QModelIndex))); + q->connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), + q, SLOT(rowsInserted(QModelIndex,int,int))); + q->connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), + q, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int))); + q->connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), + q, SLOT(rowsRemoved(QModelIndex,int,int))); + q->connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), + q, SLOT(rowsAboutToBeInserted(QModelIndex,int,int))); + q->connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)), + q, SLOT(columnsInserted(QModelIndex,int,int))); + q->connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), + q, SLOT(columnsAboutToBeInserted(QModelIndex,int,int))); + q->connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)), + q, SLOT(columnsRemoved(QModelIndex,int,int))); + q->connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), + q, SLOT(columnsAboutToBeRemoved(QModelIndex,int,int))); + q->connect(model, SIGNAL(modelReset()), q, SLOT(reset())); + q->connect(model, SIGNAL(layoutChanged()), q, SLOT(_q_layoutChanged())); + + setSelectionModel(new QItemSelectionModel(model, q)); + } +} + +/*! + \private + + Slot is called whenever the model is destroyed. + deletes all children and disconnects all signal- slot connections + with model and selectionmodel +*/ +void HbAbstractItemViewPrivate::_q_modelDestroyed() +{ + Q_Q(HbAbstractItemView); + + mModelIterator->setModel(0); + setSelectionModel(0); + q->reset(); +} + +/*! + \private + + Slot is called whenever the model layout changes. This resets the container. +*/ +void HbAbstractItemViewPrivate::_q_layoutChanged() +{ + mContainer->setModelIndexes(mModelIterator->nextIndex(QModelIndex())); +} + +void HbAbstractItemViewPrivate::_q_animationEnabled() +{ + mAnimateItems = true; +} + +void HbAbstractItemViewPrivate::_q_animationFinished(const HbEffect::EffectStatus &status) +{ + Q_UNUSED(status); + if ( status.effectEvent == "appear") { + if (mPostponedScrollIndex.isValid()) { + int count = mAppearAnimationIndexes.count(); + for (int i=0; ifirstAndLastVisibleModelIndex(firstVisibleModelIndex, lastVisibleModelIndex); + + int firstVisibleRow = firstVisibleModelIndex.isValid() ? firstVisibleModelIndex.row() : 0; + int lastVisibleRow = lastVisibleModelIndex.isValid() ? lastVisibleModelIndex.row() : 0; + + // save current, if it is visible + firstVisibleRow = qMax(0, firstVisibleRow); + lastVisibleRow = qMax(0, lastVisibleRow); + + if (mCurrentIndex.row() >= firstVisibleRow + && mCurrentIndex.row() <= lastVisibleRow) { + mVisibleIndex = mCurrentIndex; + } else if (mModelIterator->model()) { + mVisibleIndex = mModelIterator->index(firstVisibleRow); + } +} + +/*! + \private + + Minimum amount of pixels to scroll to make \a item visible. + Negative value scrolls view up, and positive value vice versa. + Returns 0, if no scrolling is needed. + + Horizontally ensures that item is visible. + */ +QPointF HbAbstractItemViewPrivate::pixelsToScroll(const HbAbstractViewItem *item, + HbAbstractItemView::ScrollHint hint) +{ + Q_Q(HbAbstractItemView); + + QPointF result(0,0); + + if (item) { + refreshContainerGeometry(); + + QRectF itemRect = itemBoundingRect(item); + QRectF viewRect = q->boundingRect(); + + if (!viewRect.isValid()) { + return result; + } + + QSizeF sizeOffset; + sizeOffset.setHeight(qMin(itemRect.height(), viewRect.height())); + sizeOffset.setWidth(qMin(itemRect.width(), viewRect.width())); + + if (mScrollDirections & Qt::Vertical) { + switch (hint) { + case HbAbstractItemView::PositionAtTop: { + result.setY(itemRect.bottom() - viewRect.top() - sizeOffset.height()); + break; + } + case HbAbstractItemView::PositionAtBottom: { + result.setY(itemRect.top() + sizeOffset.height() - viewRect.bottom()); + break; + } + case HbAbstractItemView::PositionAtCenter: { + qreal yCentre = viewRect.top() + (viewRect.height()) / 2 ; + result.setY(itemRect.top() - yCentre + sizeOffset.height()/2); + break; + } + case HbAbstractItemView::EnsureVisible: + default: { + if (itemRect.top() < viewRect.top()) { + result.setY(itemRect.bottom() - viewRect.top() - sizeOffset.height()); + } else if (itemRect.bottom() > viewRect.bottom()) { + result.setY(itemRect.top() + sizeOffset.height() - viewRect.bottom()); + } + break; + } + } + + if (itemRect.width() < viewRect.width()) { + if (itemRect.left() < viewRect.left()) { + result.setX(itemRect.left() - viewRect.left()); + } else if (itemRect.right() > viewRect.right()) { + result.setX(itemRect.right() - viewRect.right()); + } + } else { + // item does not fit in the screen, always align according to + // mirroring + if (HbApplication::layoutDirection() == Qt::LeftToRight) { + result.setX(itemRect.left() - viewRect.left()); + } else { + result.setX(itemRect.right() - viewRect.right()); + } + } + } + else if (mScrollDirections & Qt::Horizontal) { + switch (hint) { + case HbAbstractItemView::PositionAtTop: { // left + result.setX(itemRect.right() - viewRect.left() - sizeOffset.width()); + break; + } + case HbAbstractItemView::PositionAtBottom: { // right + result.setX(itemRect.left() + sizeOffset.width() - viewRect.right()); + break; + } + case HbAbstractItemView::PositionAtCenter: { + qreal xCentre = viewRect.left() + (viewRect.width()) / 2 ; + result.setX(itemRect.left() - xCentre + sizeOffset.width()/2); + break; + } + case HbAbstractItemView::EnsureVisible: + default: { + if (itemRect.left() < viewRect.left()) { + result.setX(itemRect.right() - viewRect.left() - sizeOffset.width()); + } else if (itemRect.right() > viewRect.right()) { + result.setX(itemRect.left() + sizeOffset.width() - viewRect.right()); + } + break; + } + } + + if (itemRect.top() < viewRect.top()) { + result.setY(itemRect.top() - viewRect.top()); + } else if (itemRect.bottom() > viewRect.bottom()) { + result.setY(itemRect.bottom() - viewRect.bottom()); + } + } + } + + return result; +} + +QItemSelectionModel::SelectionFlags HbAbstractItemViewPrivate::singleSelectionCommand( + const HbAbstractViewItem *item, + const QEvent *event) +{ + if (item) { + switch (event->type()) { + case QEvent::GraphicsSceneMousePress: + case QEvent::GraphicsSceneMouseDoubleClick: + if (item->selectionAreaContains(static_cast(event)->scenePos())) { + mSelectionSettings |= Selection; + } + break; + case QEvent::GraphicsSceneMouseRelease: + if ( mHitItem + && item->modelIndex() == mHitItem->modelIndex() + && mSelectionSettings.testFlag(Selection)) { + mSelectionSettings &= ~Selection; + return QItemSelectionModel::ClearAndSelect; + } + break; + default: + break; + } + } + + return QItemSelectionModel::NoUpdate; +} + + +QItemSelectionModel::SelectionFlags HbAbstractItemViewPrivate::multiSelectionCommand( + const HbAbstractViewItem *item, + const QEvent *event) +{ + if (item) { + switch (event->type()) { + case QEvent::GraphicsSceneMousePress: + case QEvent::GraphicsSceneMouseDoubleClick: + if (item->selectionAreaContains(static_cast(event)->scenePos())) { + mSelectionSettings |= Selection; + } + break; + case QEvent::GraphicsSceneMouseRelease: + if (mHitItem + && item->modelIndex() == mHitItem->modelIndex() + && mSelectionSettings.testFlag(Selection)) { + mSelectionSettings &= ~Selection; + return QItemSelectionModel::Toggle; + } + break; + default: + break; + } + } + return QItemSelectionModel::NoUpdate; +} + +QItemSelectionModel::SelectionFlags HbAbstractItemViewPrivate::contiguousSelectionCommand( + const HbAbstractViewItem *item, + const QEvent *event ) +{ + Q_Q(HbAbstractItemView); + if (item) { + switch (event->type()) { + case QEvent::GraphicsSceneMousePress: + case QEvent::GraphicsSceneMouseDoubleClick: { + if (item->selectionAreaContains(static_cast(event)->scenePos())) { + mSelectionSettings |= Selection; + + if (mSelectionModel && mSelectionModel->isSelected(item->modelIndex())) { + mContSelectionAction = QItemSelectionModel::Deselect; + } else { + mContSelectionAction = QItemSelectionModel::Select; + } + + // TODO: This should be changed to changing the gesture area of effect when that is possible... + // Gesture filter does not reset all of its internals: workaround is to delete and create the filter + q->setLongPressEnabled(false); + q->removeSceneEventFilter(mGestureFilter); + mFilterRemoved = true; + } + break; + } + case QEvent::GraphicsSceneMouseRelease: { + QItemSelectionModel::SelectionFlag flags = QItemSelectionModel::NoUpdate; + if (mSelectionSettings.testFlag(Selection)){ + flags = mContSelectionAction; + mSelectionSettings &= ~Selection; + mContSelectionAction = QItemSelectionModel::NoUpdate; + } + + if (mFilterRemoved) { + // setLongPressEnabled installs filter + q->setLongPressEnabled(true); + mFilterRemoved = false; + } + return flags; + } + case QEvent::GraphicsSceneMouseMove: + return mContSelectionAction; + default: + break; + } + } + return QItemSelectionModel::NoUpdate; +} + +/*! + Overwrites the default scroll area scrollbar updating algorithm when + recycling is used. On recycling the scrollbar position & size is calculated + using rows and their pixel size is not used. +*/ +void HbAbstractItemViewPrivate::updateScrollBar(Qt::Orientation orientation) +{ + if (!handleScrollBar(orientation)) { + HbScrollAreaPrivate::updateScrollBar(orientation); + } else { + if (mContainer->layout() && !mContainer->layout()->isActivated()) { + mContainer->layout()->activate(); + } + + if (mContainer->uniformItemSizes()) { + updateScrollBarForUniformSizedItems(); + } else { + updateScrollBarForVariableSizedItems(); + } + } +} + +/*! + Returns the abstract view item from given scene position, if there is any. +*/ +HbAbstractViewItem *HbAbstractItemViewPrivate::itemAt(const QPointF& position) const +{ + Q_Q(const HbAbstractItemView); + + HbAbstractViewItem *hitItem = 0; + QList items = q->scene()->items(position); + + int count = items.count(); + for (int current = 0; current < count; ++current) { + QGraphicsItem *item = items.at(current); + hitItem = viewItem(item); + // second condition needed, because in form there can be radio button list + // and list of the form itself on top of each other + if (hitItem && mContainer->items().indexOf(hitItem) != -1) + return hitItem; + } + return hitItem; +} + +/*! + +*/ +void HbAbstractItemViewPrivate::refreshContainerGeometry() +{ + Q_Q(const HbAbstractItemView); + + if (mContainer->layout()) { + if (!mContainer->layout()->isActivated()) { + // Make sure that the layout process has stopped. + mContainer->layout()->activate(); + } + } + + QSizeF newSize = mContainer->effectiveSizeHint(Qt::PreferredSize); + + if (!mScrollDirections.testFlag(Qt::Vertical)) { + newSize.setHeight(q->size().height()); + } + + if (!mScrollDirections.testFlag(Qt::Horizontal)) { + newSize.setWidth(q->size().width()); + } + + mContainer->resize( newSize ); +} + + +QRectF HbAbstractItemViewPrivate::itemBoundingRect(const QGraphicsItem *item) const + { + Q_Q(const HbAbstractItemView); + + if (mContainer) { + QGraphicsLayout *containerLayout = mContainer->layout(); + if (containerLayout) { + containerLayout->activate(); + } + } + + return item->mapToItem(q, item->boundingRect()).boundingRect(); + } + +/*! + 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 HbAbstractItemViewPrivate::visible(HbAbstractViewItem* item, bool fullyVisible) const +{ + Q_Q(const HbAbstractItemView); + bool visible = false; + if (item) { + QRectF itemRect(itemBoundingRect(item)); + QRectF abstractViewRect(itemBoundingRect(q)); + if (fullyVisible) { + if (abstractViewRect.contains(itemRect)) { + visible = true; + } + } else { + if (abstractViewRect.intersects(itemRect)) { + visible = true; + } + } + } + return visible; +} + +/*! + Returns current Item. +*/ +HbAbstractViewItem* HbAbstractItemViewPrivate::currentItem() const +{ + return mContainer->itemByIndex(mCurrentIndex); +} + +/*! + Tries to convert given graphics item to HbLIstViewItem. + qgraphicsitem_cast cannot be used here as it does not support subclassing. + Also qobject_cast cannot be used directly as QGraphicsItem is not derived from + QObject. But you can ask qgraphicsitem whether it is a widget or not + and cast the item to widget based on this information. After the item is + casted to widget then qobject_cast can be used. +*/ +HbAbstractViewItem* HbAbstractItemViewPrivate::viewItem(QGraphicsItem *item) const + { + HbAbstractViewItem *result = 0; + if (item && item->isWidget()) { + result = qobject_cast(static_cast(item)); + } + return result; + } + +void HbAbstractItemViewPrivate::updateItems() +{ + QList items = mContainer->items(); + foreach (HbAbstractViewItem *item, items) { + item->updateChildItems(); + } +} + +void HbAbstractItemViewPrivate::scrollTo(const QModelIndex &index, HbAbstractItemView::ScrollHint hint) +{ + Q_Q(HbAbstractItemView); + // when called from HbAbstractItemView::scrollTo(), mPostponedScrollIndex is invalid + HbAbstractViewItem *viewItem = q->itemByIndex(index); + if (viewItem) { + HbScrollArea::ClampingStyle clampingStyle = mClampingStyle; + if (clampingStyle == HbScrollArea::BounceBackClamping) { + mClampingStyle = HbScrollArea::StrictClamping; + } + revealItem(viewItem, hint); + mClampingStyle = clampingStyle; + } else if ( index.isValid() + && index == mPostponedScrollIndex) { + q->scrollTo(index, hint); + } +} + +void HbAbstractItemViewPrivate::revealItem(const HbAbstractViewItem *item, + HbAbstractItemView::ScrollHint hint ) +{ + Q_Q(HbAbstractItemView); + QPointF delta = pixelsToScroll(item, hint); + if (delta != QPointF()) { + QPointF newPos = -mContainer->pos() + delta; + checkBoundaries(newPos); + // scroll area logic is oposite to real position + q->scrollContentsTo(newPos); + } +} +void HbAbstractItemViewPrivate::checkBoundaries(QPointF &newPos) +{ + Q_Q(HbAbstractItemView); + + if (mClampingStyle != HbScrollArea::NoClamping) { + QRectF viewRect = q->boundingRect(); + QSizeF containerSize = mContainer->layout()->preferredSize(); + + if (newPos.y() < topBoundary() ) { + newPos.setY(topBoundary()); + } + + // it is possible that above checking set newPos.y > 0 + if (newPos.y() > bottomBoundary()) { + newPos.setY(bottomBoundary()); + } + + if (newPos.x() < leftBoundary() ) { + newPos.setX(leftBoundary()); + } + + // it is possible that above checking set newPos.x > 0 + if (newPos.x() > rightBoundary()) { + newPos.setX(rightBoundary()); + } + } +} + +void HbAbstractItemViewPrivate::updateScrollBarForUniformSizedItems() +{ + Q_Q(const HbAbstractItemView); + + HbAbstractViewItem *firstItem = mContainer->items().first(); + qreal uniformItemHeight = firstItem->size().height(); + qreal containerVirtualHeight = uniformItemHeight * (mModelIterator->indexCount()); + qreal thumbPosition(0); + int firstBufferItemRowNumber = mModelIterator->indexPosition(firstItem->modelIndex()); + + QRectF itemRect = itemBoundingRect(firstItem); + qreal realTopBoundary = itemRect.top(); + qreal virtualTopBoundary = realTopBoundary - (firstBufferItemRowNumber*uniformItemHeight); + + if ((containerVirtualHeight - q->boundingRect().height()) != 0) { + thumbPosition = + (-virtualTopBoundary) / (containerVirtualHeight - q->boundingRect().height()); + } + + thumbPosition = qBound((qreal)0.0, thumbPosition, (qreal)1.0); + + if (mVerticalScrollBar) { + if (containerVirtualHeight!=0) { + mVerticalScrollBar->setPageSize(qBound ( (qreal)0.0, + q->boundingRect().height() / containerVirtualHeight, + (qreal)1.0)); + } + mVerticalScrollBar->setValue(thumbPosition); + } +} + +void HbAbstractItemViewPrivate::setScrollBarMetrics(Qt::Orientation orientation) +{ + if (!handleScrollBar(orientation) ) { + HbScrollAreaPrivate::setScrollBarMetrics(orientation); + } else { + //We just make sure that the base clas is not called + //It set the page size wrongly + updateScrollBar(orientation); + } +} + +/*! + This function combines the conditions to solve whether the scroll bar calcultion should be handled in + this class or is the base class calculation sufficient +*/ +bool HbAbstractItemViewPrivate::handleScrollBar(Qt::Orientation orientation) +{ + if (!mContainer->itemRecycling() + || mContainer->itemPrototypes().count() != 1 + || orientation == Qt::Horizontal + || mContainer->items().count() == 0) { + return false; + } else { + return true; + } +} + +void HbAbstractItemViewPrivate::updateScrollBarForVariableSizedItems() +{ + Q_Q(const HbAbstractItemView); + HbAbstractViewItem *firstItem = mContainer->items().first(); + + // View position is the amount of hidden (fully or partially) + // rows above the view area. + int position = mModelIterator->indexPosition(firstItem->modelIndex()); + if (position == -1) { + return; + } + qreal viewY = (qreal)(position); + + // View area height is the amount of rows within the view area. + qreal viewH = 0; + + //Index count calculation is time consuming with tree + int indexCount = mModelIterator->indexCount(); + + // Total height is the amount of rows in the model. + qreal totalH = indexCount; + + qreal itemTop = firstItem->mapToItem(q, firstItem->pos()).y(); + qreal viewHeight = q->size().height(); + int itemCount = mContainer->items().count(); + + for (int i=0; i < itemCount; ++i) { + qreal itemHeight = mContainer->items().at(i)->size().height(); + qreal itemBottom = itemTop + itemHeight; + if (itemTop < 0) { + // Some part of the item is above the view area. + if (itemBottom < 0) { + // Fully above the view area + viewY += 1; + } else { + // Partially at the view area and partially above the view area. + viewY += (1.0 - itemBottom / itemHeight); + viewH += itemBottom / itemHeight; + } + } else if (itemTop < viewHeight) { + // So part of the item is at the view area. + if (itemBottom < viewHeight) { + // Fully at the view area + viewH += 1; + } else { + // Partially at the view area and partially below the view area. + viewH += (viewHeight - itemTop) / itemHeight; + } + } else { + break; + } + + itemTop += itemHeight; + } + + // Shifting the values to scrollbar range that is from 0.0-1.0. + qreal pos = viewY / (totalH - viewH); + pos = qBound((qreal)0.0, pos, (qreal)1.0); + + if (mVerticalScrollBar) { + if (indexCount!=0) { + mVerticalScrollBar->setPageSize(viewH / (qreal)(indexCount)); + } + mVerticalScrollBar->setValue(pos); + } +} + +void HbAbstractItemViewPrivate::rowsRemoved(const QModelIndex &parent,int start,int end) +{ + if (mModelIterator->model()->columnCount(parent) == 0) { + return; + } + + if (start <= mCurrentIndex.row() && mCurrentIndex.row() <= end) { + // new current: 1) next after last deleted (note that + // start and end index in model prior to deleting) + // 2) just before first deleted + QModelIndex newCurrentIndex = mModelIterator->model()->index(start, 0, parent); + if (!newCurrentIndex.isValid()) { + newCurrentIndex = mModelIterator->model()->index(qMax(0,start-1), 0, parent); + } + + if (mSelectionModel) { + mSelectionModel->setCurrentIndex(newCurrentIndex, QItemSelectionModel::NoUpdate); + } + } + + for (int current = end; current >= start; --current) { + //The items are already removed from the model. That's why their indexes are already invalid. + //Here we loop the items in container and call removeItem() with QModelIndex(). + bool animate = mEnabledAnimations & HbAbstractItemView::Disappear ? mAnimateItems : false; + mContainer->removeItem(QModelIndex(), animate); + } +} + +QItemSelectionModel::SelectionFlags HbAbstractItemViewPrivate::selectionFlags( + const HbAbstractViewItem *item, + const QEvent *event) +{ + if (!item || !item->modelIndex().isValid() || !(item->flags() & QGraphicsItem::ItemIsSelectable)) + return QItemSelectionModel::NoUpdate; + + QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::NoUpdate; + if (item && mHitItem && event){ + switch (mSelectionMode) { + case HbAbstractItemView::SingleSelection: + flags = singleSelectionCommand(item, event); + break; + case HbAbstractItemView::MultiSelection: + flags = multiSelectionCommand(item, event); + break; + case HbAbstractItemView::ContiguousSelection: { + flags = contiguousSelectionCommand(item, event); + break; + } + case HbAbstractItemView::NoSelection: // Never update selection model + break; + } + } + + return flags; +} + +void HbAbstractItemViewPrivate::resetContainer() +{ + mPostponedScrollIndex = QPersistentModelIndex(); + mContainer->reset(); +} + +void HbAbstractItemViewPrivate::startAppearEffect(const QModelIndex &parent, int start, int end) +{ + Q_Q(HbAbstractItemView); + if( mAppearAnimationIndexes.count()) { + mAppearAnimationIndexes.clear(); + } + QList< QGraphicsItem * >items; + for (int i = start; i <= end; i++) { + QPersistentModelIndex index = mModelIterator->index(i, parent); + HbAbstractViewItem *item = q->itemByIndex(index); + if (item) { + items.append(item); + mAppearAnimationIndexes.append(index); + } + } + + refreshContainerGeometry(); + + HbEffect::start(items, "viewitem", "appear", q, "_q_animationFinished"); +} + +void HbAbstractItemViewPrivate::ensureVisible(QPointF position, qreal xMargin, qreal yMargin) +{ + mPostponedScrollIndex = QPersistentModelIndex(); + HbScrollAreaPrivate::ensureVisible(position, xMargin, yMargin); +} + +