diff -r 000000000000 -r 16d8024aca5e src/hbwidgets/itemviews/hbgriditemcontainer_p.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hbwidgets/itemviews/hbgriditemcontainer_p.cpp Mon Apr 19 14:02:13 2010 +0300 @@ -0,0 +1,517 @@ +/**************************************************************************** +** +** 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 +#include +#include +#include +#include +#include +#include + +/*! + \class HbGridItemContainer + \brief HbGridItemContainer implements HbAbstractItemContainer. + @internal + + Creates HbGridLayout, manages grid view items. + + SeeAlso HbAbstractItemContainer,HbGridView,HbGridViewParameters,HbGridLayout + + IMPORTANT!! + It is possible that last row will not be full - int this case it contain + invisible items with have BAD_INDEX. Visibile property of item is also + used to determine if removed item was really removed or it was previously + removed from model and currently some item above or bellow should be removed + (for better understanding look to: + void HbGridItemContainerPrivate::removeItem(const QModelIndex &index)) +*/ + +/*! + Constructor. + */ +HbGridItemContainer::HbGridItemContainer(QGraphicsItem *parent) + :HbAbstractItemContainer( *new HbGridItemContainerPrivate(), parent) +{ + Q_D(HbGridItemContainer); + d->q_ptr = this; + d->init(); +} +/*! + Constructor. +*/ +HbGridItemContainer::HbGridItemContainer( HbGridItemContainerPrivate &dd, QGraphicsItem *parent ) + :HbAbstractItemContainer(dd, parent) +{ + Q_D(HbGridItemContainer); + d->q_ptr = this; + d->init(); +} + +/*! + Destructor. + */ +HbGridItemContainer::~HbGridItemContainer() +{ +} + +/*! + \reimp Adds item for model \a index to container. +*/ +void HbGridItemContainer::addItem(const QModelIndex &index, bool animate) +{ + Q_D(HbGridItemContainer); + + if (!index.isValid()) + return; + + int bufferIndex = 0; + if (d->mItems.count() != 0) { + bufferIndex = qMax(0, index.row() - d->mItems.first()->modelIndex().row()); + } + // inserting new item because of buffer size + if (d->mItems.count() == 0 + || d->mItems.count() < maxItemCount()) { + insertItem(bufferIndex, index, animate); + viewLayout()->invalidate(); + } + // special case - only for grid, if added item is above the + // visible region we need to shift all visible items by one! + else if (d->mItems.count() > 0 + && d->mItems.first()->modelIndex().row() > index.row()) { + d->shiftUpItem(animate); // shift up in this case always return something + viewLayout()->invalidate(); + } + // new item is in visible range + else if (bufferIndex < d->mItems.count()) { + HbAbstractViewItem *last = d->mItems.last(); + if (animate) { + last->setOpacity(0.0); + } + setItemModelIndex(last, index); + viewLayout()->moveItem(last, d->mapToLayoutIndex(bufferIndex), animate); + d->mItems.move(d->mItems.count() - 1, bufferIndex); + viewLayout()->invalidate(); + } + + if (d->mItems.count() % d->mItemsPerRow == 1) { // scrollable area size has been changed + resize(viewLayout()->sizeHint(Qt::PreferredSize)); + } +} + +/*! + \reimp Removes item representing \a index from container. +*/ +void HbGridItemContainer::removeItem(const QModelIndex &index, bool animate) +{ + Q_D(HbGridItemContainer); + + d->removeItem(index, animate); +} + +/*! + \reimp +*/ +void HbGridItemContainer::reset() +{ + Q_D(HbGridItemContainer); + HbAbstractItemContainer::reset(); + d->resetBuffer(); +} + +/*! + \reimp + Set model indexes starting from \a startIndex. If \a startIndex is not + a index of first item in a row, then it is calculated automatically + (prevent) from different content looking (grid items arrangement should be + always the same). If there is not enought items function fetch data that + are before \a startIndex. After calling this function item, with specified + index is in container but it position depends on model size. + If \a startIndex is invlaid then container is filled starting from first + item in model. +*/ +void HbGridItemContainer::setModelIndexes(const QModelIndex &startIndex) +{ + Q_D(HbGridItemContainer); + + if (!d->mItemView || !d->mItemView->model()) { + return; + } + + QModelIndex index = startIndex; + if (!index.isValid()) { + index = d->mItemView->model()->index(0, 0); + if (!index.isValid()) + return; + } + index = d->mItemView->model()->index( + d->alignIndexToClosestFirstInRow(index.row()), 0); + + int modelItemsCount = d->mItemView->model()->rowCount(); + int itemsCount = d->mItems.count(); + int diff = index.row() + itemsCount - modelItemsCount; + if (diff >= d->mItemsPerRow) { + diff = modelItemsCount - itemsCount; + if (diff % d->mItemsPerRow) diff = diff + d->mItemsPerRow - diff % d->mItemsPerRow; + index = d->mItemView->model()->index(diff, 0); + if (!index.isValid()) + index = d->mItemView->model()->index(0, 0); + } + + int i = 0; + for (; i < itemsCount && index.isValid(); ++i) { + setItemModelIndex(d->mItems.at(i), index); + index = d->mItemView->modelIterator()->nextIndex(index); + } + + if (i < itemsCount) { + for (; i % d->mItemsPerRow != 0; i++) { + setItemModelIndex(d->mItems.at(i), index); + } + if (i < itemsCount) { + while (i > d->mItems.count()) { + d->mItems.removeLast(); + } + } + } +} + +/*! + Returns HbGridLayout used by gridview. +*/ +HbGridLayout *HbGridItemContainer::viewLayout() const +{ + Q_D(const HbGridItemContainer); + return d->mLayout; +} + +/*! + \reimp +*/ +void HbGridItemContainer::viewResized(const QSizeF &size) +{ + Q_D(HbGridItemContainer); + if (!(qFuzzyCompare(d->mViewSize.height(), size.height()) + && qFuzzyCompare(d->mViewSize.width(), size.width()))) { + QPointF p = pos(); + p.setY(p.y() * size.height() / d->mViewSize.height()); + p.setX(p.x() * size.width() / d->mViewSize.width()); + setPos(p); + d->mViewSize = size; + d->resetBuffer(); + } +} + +/*! + \reimp +*/ +void HbGridItemContainer::setItemModelIndex(HbAbstractViewItem *item, + const QModelIndex &index) +{ + Q_D(HbGridItemContainer); + + if (item->modelIndex() != index) { + d->mLayout->recycleItems(itemByIndex(index), item); + } + + HbAbstractItemContainer::setItemModelIndex(item, index); + if (!index.isValid()/* && item->isVisible()*/) { + // this is needed because this is only way to determine + // if there were no item, or it was already removed from model + item->setVisible(false); + } + else if (index.isValid() && !item->isVisible()) { + item->setVisible(true); + } +} + +/*! + \reimp +*/ + +QPointF HbGridItemContainer::recycleItems(const QPointF &delta) +{ + Q_D(HbGridItemContainer); + + if (!d->mItemRecycling || d->mItemsPerRow <=0) { + return delta; + } + + QRectF viewRect(d->itemBoundingRect(d->mItemView)); + QSizeF itemsCanvas(layout()->preferredSize()); + qreal invisibleArea = 0; + + qreal diff = 0.0; + if (Qt::Vertical == d->mScrollDirection) { + invisibleArea = itemsCanvas.height() - viewRect.height(); + diff = pos().y() - delta.y(); + if ((delta.y() < 0.0 && diff > 0) + || (delta.y() > 0.0 && invisibleArea + diff < 0)) { + diff = delta.y(); + } + else { + diff = 0.0; + } + } + else { + invisibleArea = itemsCanvas.width() - viewRect.width(); + diff = pos().x() - delta.x(); + if ((delta.x() < 0.0 && diff > 0) + || (delta.x() > 0.0 && invisibleArea + diff < 0)) { + diff = delta.x(); + } + else { + diff = 0.0; + } + } + + if (diff != 0.0) { + if (HbAbstractItemViewPrivate::d_ptr(d->mItemView)->mOptions & HbAbstractItemViewPrivate::PanningActive) { + // jump is almost in the middle of fetched buffer - in most of cases + // after scrolling was stopped panning should be done without fetching + // items + // in case when buffer == 1 below lines do not change diff + qreal extraDiff = invisibleArea/2 - d->mCachedItemHeight; + if (extraDiff < 0.0) { + extraDiff = 0.0; // impossible because this mean that bufferSize == 0 + } + + if (diff < 0.0) { + diff -= extraDiff; + } + else { + diff += extraDiff; + } + } + + qreal result = (d->recycling(diff)); + + QPointF newDelta(Qt::Vertical == d->mScrollDirection + ?QPointF(0.0, delta.y() - result) + : QPointF(delta.x() - result, 0.0)); + + return newDelta; + } + + return delta; +} + +/*! + \reimp +*/ +void HbGridItemContainer::itemRemoved( HbAbstractViewItem *item, bool animate ) +{ + if (item) { + Q_D(HbGridItemContainer); + + d->mLayout->removeItem(item, animate); + d->mLayout->invalidate(); + } +} + +/*! + \reimp +*/ +void HbGridItemContainer::itemAdded(int index, HbAbstractViewItem *item, bool animate) +{ + if (item) { + Q_D(HbGridItemContainer); + if (animate) { + item->setOpacity(0.0); + } + d->mLayout->insertItem(d->mapToLayoutIndex(index), item, animate); + d->mLayout->invalidate(); + } +} + +/*! + Updates rows and columns count according to changed \a newOrientation. +*/ +void HbGridItemContainer::orientationChanged(Qt::Orientation) +{ + Q_D(HbGridItemContainer); + if(d->mItemView && d->mLayout) { + qSwap(d->mRowCount, d->mColumnCount); + d->mLayout->setRowCount(d->mRowCount); + d->mLayout->setColumnCount(d->mColumnCount); + d->resetBuffer(); + } +} + +/*! + Items are arranged in gridLayout according to \a scrollDirection + Qt::Horizontal layout in horizontal direction and Qt::Vertical layout + in vertical direction. +*/ +void HbGridItemContainer::scrollDirectionChanged(Qt::Orientations scrollDirection) +{ + Q_D(HbGridItemContainer); + if(d->mScrollDirection != scrollDirection){ + viewLayout()->setScrollDirection(scrollDirection); + d->mScrollDirection = scrollDirection; + d->resetBuffer(); + } +} + +/*! + Sets total no of rows to \a rowCount. +*/ +void HbGridItemContainer::setRowCount(int rowCount) +{ + Q_D(HbGridItemContainer); + + Q_ASSERT_X(rowCount > 0, "HbGridItemContainer::setRowCount", "row count can not be <= 0"); + + if ((rowCount > 0) && (rowCount != d->mRowCount)) { + d->mRowCount = rowCount; + viewLayout()->setRowCount(d->mRowCount); + d->resetBuffer(); + } +} + +/*! + Returns rowCount. +*/ +int HbGridItemContainer::rowCount() const +{ + return viewLayout()->rowCount(); +} + +/*! + Sets total no of columns to \a columnCount. +*/ +void HbGridItemContainer::setColumnCount(int columnCount) +{ + Q_D(HbGridItemContainer); + + Q_ASSERT_X(columnCount > 0, "HbGridItemContainer::setColumnCount", "column count can not be <= 0"); + + if ((columnCount > 0) && (columnCount != d->mColumnCount)) { + d->mColumnCount = columnCount; + d->mLayout->setColumnCount(d->mColumnCount); + d->resetBuffer(); + } +} + +/*! + Returns columnCount. +*/ +int HbGridItemContainer::columnCount() const +{ + return viewLayout()->columnCount(); +} + +/*! + \reimp +*/ +int HbGridItemContainer::maxItemCount() const +{ + Q_D(const HbGridItemContainer); + + int count = HbAbstractItemContainer::maxItemCount(); + // inform the grid layout if recycling is on or off + viewLayout()->setRecycling(d->mItemView->itemRecycling()); + // check if it has been specified, how many items can be show at a time. + if (d->mItemView->itemRecycling()) { + count = qMin(d->mMinCount, count); + } + return count; +} + +/*! + \reimp +*/ +QVariant HbGridItemContainer::itemChange(GraphicsItemChange change, const QVariant & value) +{ + return HbAbstractItemContainer::itemChange(change, value); +} + +HbAbstractViewItem *HbGridItemContainer::createDefaultPrototype() const +{ + return new HbGridViewItem(); +} + +/*! + Scrolls container to show \a index. +*/ +void HbGridItemContainer::scrollTo(const QModelIndex &index, HbAbstractItemView::ScrollHint hint) +{ + Q_D(HbGridItemContainer); + + if (!index.isValid() || d->mItems.count() <= 0) + return; + + switch (hint) { + case HbAbstractItemView::EnsureVisible: d->scrollToEnsureVisible(index); break; + case HbAbstractItemView::PositionAtTop: d->scrollToPositionAtTop(index); break; + case HbAbstractItemView::PositionAtBottom: d->scrollToPositionAtBottom(index); break; + case HbAbstractItemView::PositionAtCenter: d->scrollToPositionAtCenter(index); break; + }; +} + +QModelIndex HbGridItemContainer::lastValidItemIndex() const +{ + Q_D(const HbGridItemContainer); + return d->lastValidItemIndex(); +} + +QModelIndex HbGridItemContainer::getViewIndexInTheCenter() const +{ + Q_D(const HbGridItemContainer); + return HbAbstractItemViewPrivate::d_ptr(d->mItemView)->mVisibleIndex; +} + +void HbGridItemContainer::animationFinished(const HbEffect::EffectStatus &status) +{ + Q_D(HbGridItemContainer); + + HbAbstractViewItem *item = static_cast(status.item); + + int itemCount = d->mAnimatedItems.count(); + for (int i = 0; i < itemCount; ++i) { + QPair animatedItem = d->mAnimatedItems.at(i); + if (animatedItem.first == item) { + d->mAnimatedItems.removeAt(i); + break; + } + } + + d->mLayout->removeItem(item, true); + item->deleteLater(); +} + +void HbGridItemContainer::layoutAnimationFinished(QGraphicsLayoutItem *item, HbGridLayout::AnimationType animationType) +{ + Q_D(HbGridItemContainer); + + if (animationType == HbGridLayout::InsertAnimation) { + item->graphicsItem()->setOpacity(1.0); + HbEffect::start(item->graphicsItem(), "gridviewitem", "appear", d->mItemView, "_q_animationFinished"); + } +} + + +#include "moc_hbgriditemcontainer_p.cpp" +