src/hbwidgets/itemviews/hbtumbleview.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 19 Apr 2010 14:02:13 +0300
changeset 0 16d8024aca5e
child 1 f7ac710697a9
permissions -rw-r--r--
Revision: 201011 Kit: 201015

/****************************************************************************
**
** 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 "hblistview_p.h"
#include <hbtumbleview.h>
#include <hbtumbleviewitem.h>
#include "hblistitemcontainer_p.h"
#include "hblistitemcontainer_p_p.h"
#include "hbmodeliterator.h"

#include <hbevent.h>
#include <hbstyleoption.h>

#include <QGraphicsSceneMouseEvent>
#include <QStringListModel>

#define HB_TUMBLE_ITEM_ANIMATION_TIME 500
#define HB_TUMBLE_PREFERRED_ITEMS 3

#define HBTUMBLE_DEBUG
#ifdef HBTUMBLE_DEBUG
#include <QDebug>
#endif
class HbTumbleViewItemContainerPrivate;
class HbTumbleViewItemContainer:public HbListItemContainer
{
    Q_DECLARE_PRIVATE(HbTumbleViewItemContainer)
public:
    HbTumbleViewItemContainer(QGraphicsItem* parent = 0);
    QPointF recycleItems(const QPointF &delta);

    void setLoopingEnabled(bool looping) ;
    bool isLoopingEnabled() const ;
};

class HbTumbleViewItemContainerPrivate:public HbListItemContainerPrivate
{
    Q_DECLARE_PUBLIC(HbTumbleViewItemContainer)
public:
    HbTumbleViewItemContainerPrivate();
    QPointF recycleItems(const QPointF &delta);
    HbAbstractViewItem *shiftDownItem(QPointF& delta);
    HbAbstractViewItem *shiftUpItem(QPointF& delta);

    bool mIsLooped;
};

class HbTumbleViewPrivate : public HbListViewPrivate
{
    Q_DECLARE_PUBLIC(HbTumbleView)

public:
    HbTumbleViewPrivate();

    qreal topBoundary();//virtual functions
    qreal bottomBoundary();
    virtual void updateScrollMetrics();
    virtual void scrollTo(const QModelIndex &index, HbAbstractItemView::ScrollHint hint);

    void init(QAbstractItemModel *model);
    void calculateItemHeight();

    void selectMiddleItem();

    void createPrimitives();
    void createBackground();

    void _q_scrollingStarted();//private slot
    void _q_scrollingEnded();//private slot

private:
    qreal mHeight;
    HbAbstractViewItem *mPrevSelectedItem;
    bool mInternalScroll;
    bool mStartup;//needed for layout request

    //geometry prob, some how loop setGeometry call is happening
    QRectF mPrevSetGeometryRect;

    //primitives
    QGraphicsItem   *mBackground;
    QGraphicsItem   *mFrame;//overlay
    QGraphicsItem   *mHighlight;
    int             mSelected;
    bool mNeedScrolling;
};


HbTumbleViewItemContainer::HbTumbleViewItemContainer(QGraphicsItem* parent )
    :HbListItemContainer(*(new HbTumbleViewItemContainerPrivate),parent)
{

}

QPointF HbTumbleViewItemContainer::recycleItems(const QPointF &delta)
{
    Q_D(HbTumbleViewItemContainer);

    if (d->mPrototypes.count() != 1) {
        return delta;
    }

    QRectF viewRect(d->itemBoundingRect(d->mItemView));
    viewRect.moveTopLeft(viewRect.topLeft() + delta);

    int firstVisibleBufferIndex = -1;
    int lastVisibleBufferIndex = -1;
    d->firstAndLastVisibleBufferIndex(firstVisibleBufferIndex, lastVisibleBufferIndex, viewRect, false);

    int hiddenAbove = firstVisibleBufferIndex;
    int hiddenBelow = d->mItems.count() - lastVisibleBufferIndex - 1;

    if (d->mItems.count()
        && (firstVisibleBufferIndex == -1 || lastVisibleBufferIndex == -1)) {
        // All items out of sight.
        if (d->itemBoundingRect(d->mItems.first()).top() < 0) {
            // All items above view.
            hiddenAbove = d->mItems.count();
            hiddenBelow = 0;
        } else {
            // All items below view.
            hiddenAbove = 0;
            hiddenBelow = d->mItems.count();
        }
    }

    QPointF newDelta(delta);

    while (hiddenAbove > hiddenBelow + 1) {
        HbAbstractViewItem *item = d->shiftDownItem(newDelta);
        if (!item){
            break;
        }

        if (!d->visible(item, viewRect)) {
            hiddenBelow++;
        }
        hiddenAbove--;
    }

    while (hiddenBelow > hiddenAbove + 1) {
        HbAbstractViewItem *item = d->shiftUpItem(newDelta);
        if (!item) {
            break;
        }

        if (!d->visible( item, viewRect)) {
            hiddenAbove++;
        }
        hiddenBelow--;
    }

    return newDelta;
}
void HbTumbleViewItemContainer::setLoopingEnabled(bool looped) {
    Q_D(HbTumbleViewItemContainer);
    d->mIsLooped = looped;
}
bool HbTumbleViewItemContainer::isLoopingEnabled() const {
    Q_D(const HbTumbleViewItemContainer);
    return d->mIsLooped;
}

HbTumbleViewItemContainerPrivate::HbTumbleViewItemContainerPrivate()
    : mIsLooped(false) //TODO: make this true once issues are fixed.
{ //issues, initial loop creation
}


HbAbstractViewItem *HbTumbleViewItemContainerPrivate::shiftDownItem(QPointF& delta)
{
    Q_Q(HbTumbleViewItemContainer);

    HbAbstractViewItem *item = 0;
    HbAbstractViewItem *lastItem = mItems.last();

    QModelIndex nextIndex = mItemView->modelIterator()->nextIndex(lastItem->modelIndex());
    if(mIsLooped && (!nextIndex.isValid())){
       nextIndex = mItemView->model()->index(0,0);
    }
    if (nextIndex.isValid()) {
        item = mItems.takeFirst();

        q->itemRemoved(item);

        delta.setY(delta.y() - item->size().height());

        mItems.append(item);

        q->setItemModelIndex(item, nextIndex);

        q->itemAdded(mItems.count() - 1, item);
    }

    return item;
}

HbAbstractViewItem *HbTumbleViewItemContainerPrivate::shiftUpItem(QPointF& delta)
{
    Q_Q(HbTumbleViewItemContainer);

    HbAbstractViewItem *item = 0;
    HbAbstractViewItem *firstItem = mItems.first();

    QModelIndex previousIndex = mItemView->modelIterator()->previousIndex(firstItem->modelIndex());
    if(mIsLooped && !previousIndex.isValid()){
        previousIndex = mItemView->model()->index(mItemView->model()->rowCount()-1,0);
    }
    if (previousIndex.isValid()) {
        item = mItems.takeLast();

        q->itemRemoved(item);

        mItems.insert(0, item);

        q->setItemModelIndex(item, previousIndex);

        qreal itemHeight=0;
        if (q->uniformItemSizes()) {
            itemHeight = mItems.last()->preferredHeight();
        } else {
            //This is time consuming and causes backwards srolling to be slower than forwards.
            //The sizehint of the item is dirty.
            itemHeight = item->preferredHeight();
        }

        delta.setY(delta.y() + itemHeight);

        q->itemAdded(0, item);
    }
    return item;
}

HbTumbleViewPrivate::HbTumbleViewPrivate()
    :HbListViewPrivate()
    ,mHeight(10.0)
    ,mPrevSelectedItem(0)
    ,mInternalScroll(false)
    ,mStartup(true)
    ,mBackground(0)
    ,mFrame(0)
    ,mHighlight(0)
    ,mSelected(-1)
{
}

qreal HbTumbleViewPrivate::topBoundary()
{
    //top boundary and bottom boundary is different for tumble view
    //it is half item less than the middle of the view
    Q_Q( HbTumbleView );
    return (-(q->boundingRect().height()-mHeight)/2);
}

qreal HbTumbleViewPrivate::bottomBoundary()
{
    Q_Q ( HbTumbleView);    
    return mContents->boundingRect().height()-((q->boundingRect().height()+mHeight)/2);
} 


void HbTumbleViewPrivate::init(QAbstractItemModel *model)
{
    Q_Q(HbTumbleView);
    mNeedScrolling = true;
    //list settings
    HbTumbleViewItem *proto = new HbTumbleViewItem();
    proto->setFlag(QGraphicsItem::ItemIsFocusable,false);        
    q->setModel(model,proto);
    q->setSelectionMode(q->HbAbstractItemView::SingleSelection);
    q->setLongPressEnabled(false);
    q->setUniformItemSizes(true);
    q->setEnabledAnimations(HbAbstractItemView::TouchDown);

    //scroll area settings
    q->setClampingStyle(HbScrollArea::BounceBackClamping);
    q->setVerticalScrollBarPolicy(HbScrollArea::ScrollBarAlwaysOff);
    q->setHorizontalScrollBarPolicy(HbScrollArea::ScrollBarAlwaysOff);
    q->setFrictionEnabled(true);

    bool b = q->connect(q,SIGNAL(scrollingStarted()),q,SLOT(_q_scrollingStarted()));
    Q_ASSERT(b);
    b = q->connect(q,SIGNAL(scrollingEnded()),q,SLOT(_q_scrollingEnded()));
    Q_ASSERT(b);
    Q_UNUSED(b);
    createPrimitives();
}

void HbTumbleViewPrivate::selectMiddleItem()
{
    Q_Q(HbTumbleView);
    //scroll little amount so item settle's in
    if(!q->scene()) {
        return;
    }
    QPointF centerPt = q->mapToScene(q->boundingRect().center());
    HbAbstractViewItem *item = itemAt(centerPt);

    if(item) {
#ifdef HBTUMBLE_DEBUG  
    qDebug() << "HbTumbleViewPrivate::selectMiddleItem - " << item->modelIndex().row() ;
#endif
        //clampScroll(item);
        q->setCurrentIndex(item->modelIndex(),QItemSelectionModel::SelectCurrent);
        mSelected = item->modelIndex().row();
    }
}

void HbTumbleViewPrivate::scrollTo(const QModelIndex &index, HbAbstractItemView::ScrollHint hint)
{
#ifdef HBTUMBLE_DEBUG  
    qDebug() << "HbTumbleViewPrivate::scrollTo(" << index.row() << "," << hint << " )";
#endif
    
    HbListViewPrivate::scrollTo(index, hint);
}

void HbTumbleView::scrollTo(const QModelIndex &index, ScrollHint)
{
#ifdef HBTUMBLE_DEBUG  
    qDebug() << "HbTumbleView::scrollTo(" << index.row() << ", )";
#endif
    HbListView::scrollTo(index, PositionAtCenter);
}

void HbTumbleViewPrivate::createPrimitives()
{
    Q_Q(HbTumbleView);

    //this is the highlight which is placed at center
    if(!mHighlight) {
        mHighlight = q->style()->createPrimitive(HbStyle::P_TumbleView_highlight,q);
        q->style()->setItemName(mHighlight,"highlight");
    } 

    //createBackground(); //done in derived class
    //the parent item adds multiple tumbleviews and has only one bg.so this is commented
}

void HbTumbleViewPrivate::createBackground()
{
    Q_Q(HbTumbleView);
    //not called, but used in derived classes to get a bg/frame for single specialized tumbleview
    if(!mBackground) {
        mBackground = q->style()->createPrimitive(HbStyle::P_TumbleView_background,q);
        q->style()->setItemName(mBackground,"background");
    }
    if(!mFrame) {
        mFrame = q->style()->createPrimitive(HbStyle::P_TumbleView_frame,q);
        q->style()->setItemName(mFrame,"frame");//stays on top of background
    }
}

void HbTumbleViewPrivate::calculateItemHeight()
{
    Q_Q(HbTumbleView);
    if (!q->items().isEmpty()) {
        //Let's create a temporary item and take the height for the size hint
        HbAbstractViewItem *tempItem = q->itemPrototypes().first()->createItem();
        tempItem->setModelIndex(mModelIterator->nextIndex(QModelIndex()));

        qreal oldHeight = mHeight;
        mHeight = tempItem->effectiveSizeHint(Qt::PreferredSize).height();

        delete tempItem;

        if (mHeight != oldHeight) {
            q->updateGeometry();
        }
    }
}

void HbTumbleViewPrivate::updateScrollMetrics()
{
    mAbleToScrollY = true;
}

/*!
    @proto
    Tumbler Widget. used by datetimepicker. lot of changes to come.
*/
HbTumbleView::HbTumbleView(QGraphicsItem *parent)
    :HbListView(*new HbTumbleViewPrivate,
        new HbTumbleViewItemContainer,
        parent)
{
    Q_D(HbTumbleView);
    d->q_ptr = this;
    d->init(new QStringListModel(this));

}

/*!
    @proto
    Tumbler Widget. used by datetimepicker. lot of changes to come.
*/
HbTumbleView::HbTumbleView(const QStringList &list,QGraphicsItem *parent)
    :HbListView(*new HbTumbleViewPrivate,
        new HbTumbleViewItemContainer,
        parent)
{
    Q_D(HbTumbleView);
    d->q_ptr = this;
    d->init(new QStringListModel(list,this));

    d->calculateItemHeight();
}

/*!
    @proto
    Tumbler Widget. used by datetimepicker. lot of changes to come.
*/
HbTumbleView::HbTumbleView(HbTumbleViewPrivate &dd, QGraphicsItem *parent):
    HbListView(dd,
        new HbTumbleViewItemContainer,
        parent)
{
    Q_D(HbTumbleView);
    d->q_ptr = this;
    d->init(new QStringListModel(this));

    d->calculateItemHeight();
}
/*!
    @proto
    Tumbler Widget. used by datetimepicker. lot of changes to come.
*/
 
HbTumbleView::~HbTumbleView()
{
}

/*!
    @proto
    Tumbler Widget. used by datetimepicker. lot of changes to come.
*/
void HbTumbleView::setItems(const QStringList &list)
{
    Q_D(HbTumbleView);
    QStringListModel *stringListModel = qobject_cast<QStringListModel *>(model());
    if (stringListModel) {
        stringListModel->setStringList(list);
        d->calculateItemHeight();
    }
    updatePrimitives();
}

/*!
    @proto
    Tumbler Widget. used by datetimepicker. lot of changes to come.
*/
QStringList HbTumbleView::items() const
{
    QStringListModel *stringListModel = qobject_cast<QStringListModel *>(model());
    if(stringListModel) {
        return stringListModel->stringList();
    }
    return QStringList();
}
/*!
    @proto
    Tumbler Widget. used by datetimepicker. lot of changes to come.
*/
void HbTumbleView::setSelected(int index)
{
#ifdef HBTUMBLE_DEBUG
    qDebug() << "HbTumbleView::setSelected(" << index << ");" ;
#endif
    Q_D(HbTumbleView);
    d->mSelected = index;

    QModelIndex modelIndex = d->mModelIterator->index(index, rootIndex());
    if(modelIndex.isValid()) {
        setCurrentIndex(modelIndex,QItemSelectionModel::SelectCurrent);
        emitActivated(modelIndex);
    } 
}
 
/*!
    @proto
    Tumbler Widget. used by datetimepicker. lot of changes to come.
*/
int HbTumbleView::selected() const
{
    return currentIndex().row();
}
/*!
    @proto
    Tumbler Widget. used by datetimepicker. lot of changes to come.
*/

QGraphicsItem *HbTumbleView::primitive(HbStyle::Primitive id) const
{
    Q_D(const HbTumbleView);

    switch(id) {
        case HbStyle::P_TumbleView_background:
            return d->mBackground;
        case HbStyle::P_TumbleView_frame:
            return d->mFrame;
        case HbStyle::P_TumbleView_highlight:
            return d->mHighlight;
        default:
            return HbListView::primitive(id);
    }
}

/*!
    @proto
    Tumbler Widget. used by datetimepicker. lot of changes to come.
*/
void HbTumbleView::currentIndexChanged(const QModelIndex &current, const QModelIndex &previous)
{

    Q_D(HbTumbleView);
    HbListView::currentIndexChanged(current,previous);
    if(d->mNeedScrolling && current.isValid()){
        d->mInternalScroll = true;
        scrollTo(current,PositionAtCenter);
        emit itemSelected(current.row());
        d->mInternalScroll = false;
    }
}

/*!
    @proto
    Tumbler Widget. used by datetimepicker. lot of changes to come.
*/
void HbTumbleView::updatePrimitives()
{                   
    Q_D(HbTumbleView);

    HbStyleOption opt;
    initStyleOption(&opt);

    if(d->mBackground) {
        style()->updatePrimitive(d->mBackground,HbStyle::P_TumbleView_background,&opt);
    }
    if(d->mFrame) {
        style()->updatePrimitive(d->mFrame,HbStyle::P_TumbleView_frame,&opt);
    } 
    if(d->mHighlight) {
        style()->updatePrimitive(d->mHighlight,HbStyle::P_TumbleView_highlight,&opt);
    }   
    HbListView::updatePrimitives();

}

/*!
    @proto
    Tumbler Widget. used by datetimepicker. lot of changes to come.
*/
QVariant HbTumbleView::itemChange(GraphicsItemChange change,const QVariant &value)
{
    if((change==QGraphicsItem::ItemVisibleHasChanged) && (value.toBool()==true)) {
        QModelIndex selected=currentIndex();
        scrollTo(selected,PositionAtCenter);
    }
    return HbListView::itemChange(change,value);
}

/*!
    @proto
    Tumbler Widget. used by datetimepicker. lot of changes to come.
*/
void HbTumbleView::rowsInserted(const QModelIndex &parent,int start,int end)
{
   // Q_D(HbTumbleView);
    HbListView::rowsInserted(parent,start,end);
    scrollTo(currentIndex(),PositionAtCenter);
}

/*!
    @proto
    Tumbler Widget. used by datetimepicker. lot of changes to come.
*/
void HbTumbleView::rowsAboutToBeInserted(const QModelIndex &index, int start, int end)
{
    HbListView::rowsAboutToBeInserted(index,start,end);
}

/*!
    @proto
    Tumbler Widget. used by datetimepicker. lot of changes to come.
*/
bool HbTumbleView::event(QEvent *e)
{
    Q_D(HbTumbleView);
    if(e->type() == HbEvent::ChildFocusOut) {
        return true;
    }
    else if(d->mStartup && (e->type() == QEvent::LayoutRequest) ) {
        if(d->mSelected < 0) {
            d->selectMiddleItem();
        }
        d->mStartup = false;
    }
    return HbListView::event(e);
}

/*!
    @proto
    Tumbler Widget. used by datetimepicker. lot of changes to come.
*/
void HbTumbleView::setGeometry(const QRectF &rect)
{
    Q_D(HbTumbleView);
    if(d->mPrevSetGeometryRect == rect) {
#ifdef HBTUMBLE_DEBUG
    qDebug() << "HbTumbleView::setGeometry equal rect";
#endif
        return;
    }
#ifdef HBTUMBLE_DEBUG
    qDebug() << "HbTumbleView::setGeometry not equal rect";
#endif
    d->mPrevSetGeometryRect = rect;
    HbListView::setGeometry(rect);

    d->calculateItemHeight();
    QModelIndex selected = currentIndex();
    if(selected.isValid() && !d->mIsAnimating) {
        scrollTo(selected,PositionAtCenter);
    }
    updatePrimitives();
}

/*!
    @proto
    Tumbler Widget. used by datetimepicker. lot of changes to come.
*/
void HbTumbleView::setLoopingEnabled(bool looped) 
{
    Q_D(HbTumbleView);
    HbTumbleViewItemContainer *container=static_cast<HbTumbleViewItemContainer*>(d->mContainer);
    if(container) {
        container->setLoopingEnabled(looped);
    }
}

/*!
    @proto
    Tumbler Widget. used by datetimepicker. lot of changes to come.
*/
bool HbTumbleView::isLoopingEnabled() const
{
    Q_D(const HbTumbleView);
    HbTumbleViewItemContainer *container=static_cast<HbTumbleViewItemContainer*>(d->mContainer);
    if(container) {
        return container->isLoopingEnabled();
    }
    return false;
}

/*!
    @proto
    Tumbler Widget. used by datetimepicker. lot of changes to come.
*/
void HbTumbleView::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
#ifdef HBTUMBLE_DEBUG
    qDebug() << "HbTumbleView::mousePressEvent";
#endif
    Q_D(HbTumbleView);
    QPointF pt = mapToScene(event->pos());
    d->mPrevSelectedItem = d->itemAt(pt);

    HbListView::mousePressEvent(event);
}

/*!
    @proto
    Tumbler Widget. used by datetimepicker. lot of changes to come.
*/
void HbTumbleView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
#ifdef HBTUMBLE_DEBUG
    qDebug() << "HbTumbleView::mouseReleaseEvent";
#endif
    //Q_D(HbTumbleView);
    //if(d->mPrevSelectedItem) {
        //d->mInternalScroll = true;
    //}
    HbListView::mouseReleaseEvent(event);
    //TODO: add functinality in HbAbstractItemView or HbScrollArea to stop revealItem
    //which happens for half visible item tap.

    /*if(d->mPrevSelectedItem) {
        d->stopAnimating();
        d->mInternalScroll = false;
        d->clampScroll(d->mPrevSelectedItem);
    }*/
    
}

/*!
    @proto
    Tumbler Widget. used by datetimepicker. lot of changes to come.
*/
QSizeF HbTumbleView::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
{
    Q_UNUSED(constraint);
    Q_D(const HbTumbleView); 
    QSizeF sh=HbListView::sizeHint(which,constraint);
    switch(which) {
        case Qt::MinimumSize:
			sh = QSizeF(sh.width(),HB_TUMBLE_PREFERRED_ITEMS*d->mHeight);
            break;
        case Qt::PreferredSize:
            sh = QSizeF(sh.width(),HB_TUMBLE_PREFERRED_ITEMS*d->mHeight);
            break;
        case Qt::MaximumSize:
            break;
        default:
            qWarning("HbTumbleView::sizeHint(): unknown 'which'");
            break;
    }
    return sh;
     //TODO:fix sizeHinting.
    //return HbListView::sizeHint(which,constraint);
}
 

void HbTumbleViewPrivate::_q_scrollingStarted()
{
#ifdef HBTUMBLE_DEBUG
    qDebug() << "HbTumbleViewPrivate::_q_scorllingStarted() :" << mInternalScroll;
#endif
    if(mInternalScroll) {
        return;
    }
    mPrevSelectedItem = 0;

}

void HbTumbleViewPrivate::_q_scrollingEnded()
{
#ifdef HBTUMBLE_DEBUG
    qDebug() << "HbTumbleViewPrivate::_q_scorllingEnded() :" << mInternalScroll;
#endif
    Q_Q(HbTumbleView);

    if(!q->scene()) {
        return;
    }
     if(mInternalScroll || mStartup) {
        return;
    }

    QPointF pt = q->mapToScene(q->boundingRect().center());
    HbAbstractViewItem *centerItem=itemAt(pt);
    if(centerItem) {
        if(centerItem->modelIndex().isValid()) {
            q->setCurrentIndex(centerItem->modelIndex(),QItemSelectionModel::SelectCurrent);
            //emit q->itemSelected(centerItem->modelIndex().row());
            q->emitActivated(centerItem->modelIndex());
        } 
    }
}

void HbTumbleView::rowsAboutToBeRemoved(const QModelIndex &index, int start, int end)
{
    Q_D(HbTumbleView);
    d->mNeedScrolling = false;
    HbListView::rowsAboutToBeInserted(index,start,end);
}

void HbTumbleView::rowsRemoved(const QModelIndex &parent, int start, int end)
{
    Q_D(HbTumbleView);
    d->mNeedScrolling = true;
    HbListView::rowsRemoved(parent,start,end);
    scrollTo(currentIndex(),PositionAtCenter);
}

#include "moc_hbtumbleview.cpp"