src/hbwidgets/itemviews/hbgriditemcontainer_p.cpp
changeset 0 16d8024aca5e
child 1 f7ac710697a9
--- /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 <hbgriditemcontainer_p.h>
+#include <hbgridviewitem.h>
+#include <hbgridlayout_p.h>
+#include <hbabstractitemview.h>
+#include <hbabstractitemview_p.h>
+#include <hbgriditemcontainer_p_p.h>
+#include <hbmodeliterator.h>
+
+/*!
+    \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<HbAbstractViewItem *>(status.item);
+    
+    int itemCount = d->mAnimatedItems.count();
+    for (int i = 0; i < itemCount; ++i) {
+        QPair<HbAbstractViewItem *, int> 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"
+