src/hbwidgets/itemviews/hbtumbleview.cpp
changeset 3 11d3954df52a
parent 2 06ff229162e9
child 5 627c4a0fd0e7
--- a/src/hbwidgets/itemviews/hbtumbleview.cpp	Fri May 14 16:09:54 2010 +0300
+++ b/src/hbwidgets/itemviews/hbtumbleview.cpp	Thu May 27 13:10:59 2010 +0300
@@ -35,10 +35,11 @@
 
 #include <QGraphicsSceneMouseEvent>
 #include <QStringListModel>
+#include <QItemSelectionModel>
+#include <QTapGesture>
 
 #define HB_TUMBLE_ITEM_ANIMATION_TIME 500
 #define HB_TUMBLE_PREFERRED_ITEMS 3
-#define DELAYED_SELECT_INTERVAL 100
 
 #define HBTUMBLE_DEBUG
 #ifdef HBTUMBLE_DEBUG
@@ -56,6 +57,8 @@
 
     void setLoopingEnabled(bool looping) ;
     bool isLoopingEnabled() const ;
+    void removeItem(const QModelIndex &index, bool animate );
+    void setModelIndexes(const QModelIndex &startIndex);
 };
 
 class HbTumbleViewItemContainerPrivate:public HbListItemContainerPrivate
@@ -84,16 +87,14 @@
 
     void init(QAbstractItemModel *model);
     void calculateItemHeight();
+    void selectCurrentIndex(const QModelIndex& index);
 
     void selectMiddleItem();
+    HbAbstractViewItem* getCenterItem();
 
     void createPrimitives();
-
-    void delayedSelectCurrent(const QModelIndex& index);
-
     void _q_scrollingStarted();//private slot
     void _q_scrollingEnded();//private slot
-    void _q_delayedSelectCurrent();//private slot
 
     void setPressedState(HbAbstractViewItem *item);
 
@@ -188,12 +189,143 @@
 void HbTumbleViewItemContainer::setLoopingEnabled(bool looped) {
     Q_D(HbTumbleViewItemContainer);
     d->mIsLooped = looped;
+    if(looped){
+        recycleItems(QPointF());
+    }
+    else{
+        setModelIndexes(d->mItemView->currentIndex());
+    }
 }
 bool HbTumbleViewItemContainer::isLoopingEnabled() const {
     Q_D(const HbTumbleViewItemContainer);
     return d->mIsLooped;
 }
 
+void HbTumbleViewItemContainer::removeItem(const QModelIndex &index, bool animate )
+{
+    Q_D(HbTumbleViewItemContainer);
+    HbListItemContainer::removeItem(index,animate);
+    if (d->mItems.count() < maxItemCount()) {
+        d->updateItemBuffer();
+    }
+}
+
+void HbTumbleViewItemContainer::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;
+        }
+    }
+    if(isLoopingEnabled() && indexList.count()<itemCount){
+        QModelIndex firstModelIndex =  d->mItemView->modelIterator()->index(0,QModelIndex());
+        indexList.append(firstModelIndex);
+        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);
+                qDebug()<<"containeritemsat("<<itemCounter<<")="<<item2->modelIndex()<<"--indexList.at("
+                        <<indexCounter<<")="<<indexList.at(indexCounter);
+
+                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;
+                }
+            }
+
+        }
+        qDebug()<<"last used item -"<<lastUsedItem;
+        qDebug()<<"-------------------------------------------------------";
+    }
+
+    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--;
+        }
+    }
+}
+
 HbTumbleViewItemContainerPrivate::HbTumbleViewItemContainerPrivate()
     : mIsLooped(false) 
 { 
@@ -319,8 +451,6 @@
     Q_ASSERT(b);
     b = q->connect(q,SIGNAL(scrollingEnded()),q,SLOT(_q_scrollingEnded()));
     Q_ASSERT(b);
-    b = q->connect(&mDelayedSelectTimer,SIGNAL(timeout()),q,SLOT(_q_delayedSelectCurrent()));
-    Q_UNUSED(b);
     createPrimitives();
 }
 
@@ -331,17 +461,27 @@
     if(!q->scene()) {
         return;
     }
-    QPointF centerPt = q->mapToScene(q->boundingRect().center());
-    HbAbstractViewItem *item = itemAt(centerPt);
+    HbAbstractViewItem *item = getCenterItem();
 
     if(item) {
 #ifdef HBTUMBLE_DEBUG  
-    qDebug() << "HbTumbleViewPrivate::selectMiddleItem - " << item->modelIndex().row() ;
+        qDebug() << "HbTumbleViewPrivate::selectMiddleItem - " << item->modelIndex().row() ;
 #endif
-            delayedSelectCurrent(item->modelIndex());
-            mSelected = item->modelIndex().row();
-        }
+        selectCurrentIndex(item->modelIndex());
+        mSelected = item->modelIndex().row();
     }
+}
+
+HbAbstractViewItem* HbTumbleViewPrivate::getCenterItem()
+{
+    Q_Q(HbTumbleView);
+
+    if(!q->scene()) {
+        return 0;
+    }
+    QPointF centerPt = q->mapToScene(q->boundingRect().center());
+    return itemAt(centerPt);
+}
 
 void HbTumbleViewPrivate::scrollTo(const QModelIndex &index, HbAbstractItemView::ScrollHint hint)
 {
@@ -366,6 +506,24 @@
 #endif
 }
 
+void HbTumbleViewPrivate::selectCurrentIndex(const QModelIndex& index)
+{
+    Q_Q(HbTumbleView);
+    if(!mIsAnimating && !mIsScrolling) {
+        if(index == q->currentIndex()){
+            HbAbstractViewItem *item =q->itemByIndex(index);
+            QPointF delta = pixelsToScroll(item,HbAbstractItemView::PositionAtCenter );
+            QPointF newPos = -mContainer->pos() + delta;
+            checkBoundaries(newPos);
+            scrollByAmount(newPos - (-mContents->pos()));
+            mIsScrolling = false;
+        }
+        else{
+            q->setCurrentIndex(index,QItemSelectionModel::SelectCurrent);
+        }
+    }
+}
+
 void HbTumbleView::scrollTo(const QModelIndex &index, ScrollHint)
 {
 #ifdef HBTUMBLE_DEBUG  
@@ -419,21 +577,25 @@
 /*!
     @proto
     \class HbTumbleView 
-    \this is a tumbler widget which lets the user select alphanumeric values from a predefined list of 
-    values via vertical flicking and dragging. Typically widgets such as date picker and time picker use the 
+    \brief HbTumbleView is a tumbler widget which lets the user select alphanumeric values from a predefined list of values via vertical flicking and dragging.<br> 
+
+    Typically widgets such as date picker and time picker use the 
     Tumbler. The Tumbler could also be used to change other values such as number code sequence, 
-    landmark coordinates, country selection, and currency.
+    landmark coordinates, country selection, and currency.<br>
 
     Only strings can be accepted as HbTumbleView's items.
 
-    \this can be used like this:
+    HbTumbleView can be used, as shown in the below code snippet:
     \snippet{ultimatecodesnippet/ultimatecodesnippet.cpp,52}
+
+    \image html hbdatetimepicker_date.png  "Two TumbleViews(tumblers) in a datetime picker widget in d/MMMM format. One tumbler for day and the other for month."
+    <b>Note:</b>Graphics in the above image varies depending on theme.
 */
 
 /*!
     \fn void itemSelected(int index)
 
-    This signal is emitted when an item is selected in date time picker.
+    This signal is emitted when an item is selected in the tumbler.
     \param index  selected item.
 
 */
@@ -457,7 +619,7 @@
 /*!
     HbTumbleView's constructor.
 
-    \param list to be set as data to QStringListModel.
+    \param list String list to be set as data to HbTumbleView's model.
     \parent item to set as parent.
 */
 HbTumbleView::HbTumbleView(const QStringList &list,QGraphicsItem *parent)
@@ -497,7 +659,7 @@
 /*!
     Sets the HbTumbleView's items to the given string \a list.
 
-    \param list Items to be set as tumble view's model.
+    \param list Items to be set as data to HbTumbleView's model.
 
 */
 void HbTumbleView::setItems(const QStringList &list)
@@ -514,7 +676,7 @@
 /*!
     Returns items in QStringList format.
 
-    \return list of items in tumbleview's model in QStringList format.
+    \return List of items set as data to HbTumbleView's model in QStringList format.
 */
 QStringList HbTumbleView::items() const
 {
@@ -528,7 +690,7 @@
 /*!
     Sets the selection to the item at \a index.
 
-    \param index to be selected in the tumble view.
+    \param index of the item to be selected in the tumbler.
 
 */
 void HbTumbleView::setSelected(int index)
@@ -541,7 +703,7 @@
 
     QModelIndex modelIndex = d->mModelIterator->index(index, rootIndex());
     if(modelIndex.isValid()) {
-        d->delayedSelectCurrent(modelIndex);
+        d->selectCurrentIndex(modelIndex);
         emitActivated(modelIndex);
     } 
 }
@@ -549,7 +711,7 @@
 /*!
     Returns the index of the current selected item in integer format.
 
-    \return current index selected in tumble view.
+    \return current index of the item selected in the tumbler.
 */
 int HbTumbleView::selected() const
 {
@@ -557,7 +719,6 @@
 }
 
 /*!
-
     \deprecated HbTumbleView::primitive(HbStyle::Primitive)
         is deprecated.
 
@@ -653,25 +814,17 @@
 */
 void HbTumbleView::rowsInserted(const QModelIndex &parent,int start,int end)
 {
+    Q_D(HbTumbleView);
     HbListView::rowsInserted(parent,start,end);
-    scrollTo(currentIndex(),PositionAtCenter);
-}
 
-void HbTumbleViewPrivate::_q_delayedSelectCurrent()
-{
-    Q_Q(HbTumbleView);
-    if(!mIsAnimating && !mIsScrolling) {
-         if(mDelayedSelectIndex == q->currentIndex()){
-             HbAbstractViewItem *item =q->itemByIndex(mDelayedSelectIndex);
-             QPointF delta = pixelsToScroll(item,HbAbstractItemView::PositionAtCenter );
-             QPointF newPos = -mContainer->pos() + delta;
-             checkBoundaries(newPos);
-             scrollByAmount(newPos - (-mContents->pos()));
-         }
-         else{
-            q->setCurrentIndex(mDelayedSelectIndex);
-        }
+    //Trigger recycle, in the scenario where item's are deleted and reinserted again.
+    QPointF alignedPosition = d->mContents->pos();
+    Q_UNUSED(alignedPosition);
+    if(alignedPosition.y() > 0.0){
+        alignedPosition.setY(0.0);
+        d->HbScrollAreaPrivate::setContentPosition(alignedPosition);
     }
+    scrollTo(currentIndex(),PositionAtCenter);
 }
 
 /*!
@@ -727,9 +880,9 @@
 }
 
 /*!
-    Sets the looping enabled flag to eith true or false, which makes the tumbleview to scroll in a circular way.
+    Sets the looping enabled flag to either true or false, which makes the tumbler to scroll in a circular way.
 
-    \param looped flag to enable curcular view.
+    \param looped flag to enable curcular view for the tumbler.
 
     \sa isLoopingEnabled
 */
@@ -745,7 +898,7 @@
 /*!
     Returns looping enabled flag.
 
-    \return looping enabled flag.
+    \return looping enabled flag as boolean value.
 
     \sa setLoopingEnabled
 
@@ -792,7 +945,7 @@
     QSizeF sh=HbListView::sizeHint(which,constraint);
     switch(which) {
         case Qt::MinimumSize:
-			sh = QSizeF(sh.width(),HB_TUMBLE_PREFERRED_ITEMS*d->mHeight);
+            sh = QSizeF(sh.width(),HB_TUMBLE_PREFERRED_ITEMS*d->mHeight);
             break;
         case Qt::PreferredSize:
             sh = QSizeF(sh.width(),HB_TUMBLE_PREFERRED_ITEMS*d->mHeight);
@@ -833,13 +986,12 @@
         return;
     }
 
-    QPointF pt = q->mapToScene(q->boundingRect().center());
-    HbAbstractViewItem *centerItem=itemAt(pt);
+    HbAbstractViewItem *centerItem=getCenterItem();
     if(centerItem) {
         setPressedState(centerItem);
 
         if(centerItem->modelIndex().isValid()) {
-            delayedSelectCurrent(centerItem->modelIndex());
+            selectCurrentIndex(centerItem->modelIndex());
             q->emitActivated(centerItem->modelIndex());
         } 
     }
@@ -878,10 +1030,60 @@
     HbListView::rowsRemoved(parent,start,end);
     scrollTo(currentIndex(),PositionAtCenter);
 }
-void HbTumbleViewPrivate::delayedSelectCurrent(const QModelIndex& index)
+
+/*!
+    \reimp
+*/
+void HbTumbleView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
+{
+    Q_D(const HbListView);
+
+    QList<HbAbstractViewItem *> items = d->mContainer->items();
+    bool empty = items.isEmpty();
+    QModelIndex rootIndex = d->mModelIterator->rootIndex();
+    QModelIndex firstIndex = items.first()->modelIndex();
+    QModelIndex lastIndex = items.last()->modelIndex();
+
+    if (!empty &&
+        topLeft.parent() == rootIndex
+        /*&& firstIndex.row() <= bottomRight.row()
+        && topLeft.row() <= lastIndex.row()*/) {
+        HbAbstractItemView::dataChanged(topLeft, bottomRight);
+    }
+}
+
+void HbTumbleView::gestureEvent(QGestureEvent *event)
 {
-    mDelayedSelectIndex = index;
-    mDelayedSelectTimer.start(DELAYED_SELECT_INTERVAL);
+    Q_D(HbTumbleView);
+    if(QTapGesture *gesture = static_cast<QTapGesture*>(event->gesture(Qt::TapGesture))){
+        switch(gesture->state()){
+            case Qt::GestureStarted:
+                if(d->mIsAnimating || d->mIsScrolling){
+                    d->mInternalScroll = true;
+                    HbAbstractViewItem* centerItem = d->getCenterItem();
+                    if(centerItem){
+                        d->mDelayedSelectIndex = centerItem->modelIndex();
+                    }
+                }
+                else{
+                    d->mDelayedSelectIndex = QModelIndex();
+                }
+                break;
+            case Qt::GestureCanceled:
+                d->mInternalScroll = false;
+                break;
+            case Qt::GestureFinished:
+                d->mInternalScroll = false;
+                if(d->mDelayedSelectIndex.isValid()){
+                    d->selectCurrentIndex(d->mDelayedSelectIndex);
+                }
+                break;
+            default:
+                break;
+
+        }
+    }
+    HbListView::gestureEvent(event);
 }
 
 #include "moc_hbtumbleview.cpp"