src/hbwidgets/itemviews/hbabstractitemcontainer_p.cpp
changeset 2 06ff229162e9
child 5 627c4a0fd0e7
equal deleted inserted replaced
1:f7ac710697a9 2:06ff229162e9
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (developer.feedback@nokia.com)
       
     6 **
       
     7 ** This file is part of the HbWidgets module of the UI Extensions for Mobile.
       
     8 **
       
     9 ** GNU Lesser General Public License Usage
       
    10 ** This file may be used under the terms of the GNU Lesser General Public
       
    11 ** License version 2.1 as published by the Free Software Foundation and
       
    12 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
       
    13 ** Please review the following information to ensure the GNU Lesser General
       
    14 ** Public License version 2.1 requirements will be met:
       
    15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    16 **
       
    17 ** In addition, as a special exception, Nokia gives you certain additional
       
    18 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    20 **
       
    21 ** If you have questions regarding the use of this file, please contact
       
    22 ** Nokia at developer.feedback@nokia.com.
       
    23 **
       
    24 ****************************************************************************/
       
    25 #include "hbabstractitemcontainer_p.h"
       
    26 #include "hbabstractitemcontainer_p_p.h"
       
    27 
       
    28 #include "hbabstractviewitem.h"
       
    29 #include "hbabstractitemview.h"
       
    30 #include "hbabstractitemview_p.h"
       
    31 #include "hbmodeliterator.h"
       
    32 #include <hbapplication.h>
       
    33 
       
    34 #include <QGraphicsLayout>
       
    35 #include <QGraphicsSceneResizeEvent>
       
    36 #include <QEvent>
       
    37 #include <QDebug>
       
    38 
       
    39 
       
    40 /*!   
       
    41     HbAbstractItemContainer is used in HbAbstractItemView to hold the layout and view items.
       
    42     Container should have a layout, otherwise its size is zero always.
       
    43     HbAbstractItemContainer can have any kind of layout as it child.
       
    44 */
       
    45 
       
    46 /*!
       
    47     Function is called after new \a item was added into \a index position into
       
    48     container.
       
    49 */
       
    50 
       
    51 /*!
       
    52     Function is called after the \a item was removed from the container. 
       
    53 */
       
    54 
       
    55 /*!
       
    56     Function is called when container needs to be resized.
       
    57 */
       
    58 
       
    59 /*!
       
    60     Returns the default prototype.
       
    61 
       
    62     Subclasses of this class must implement this function to introduce their own default prototype. 
       
    63     Default prototype is used to create view items unless the class user excplicitly sets prototype with setItemPrototype method. 
       
    64     Implementation of this method must construct and return a new view item widget. 
       
    65 */
       
    66 
       
    67 const int HB_DEFAULT_BUFFERSIZE = 4;
       
    68 const int UpdateItemBufferEvent = QEvent::registerEventType();
       
    69 
       
    70 HbAbstractItemContainerPrivate::HbAbstractItemContainerPrivate() : 
       
    71     HbWidgetPrivate(),
       
    72     mItemView(0),
       
    73     mBufferSize(HB_DEFAULT_BUFFERSIZE),
       
    74     mItemRecycling(false),
       
    75     mUniformItemSizes(false)
       
    76 {
       
    77 }
       
    78 
       
    79 HbAbstractItemContainerPrivate::~HbAbstractItemContainerPrivate()
       
    80 {
       
    81 }
       
    82 
       
    83 
       
    84 void HbAbstractItemContainerPrivate::init()
       
    85 {
       
    86     Q_Q(HbAbstractItemContainer);
       
    87     q->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
       
    88 }
       
    89 
       
    90 /*!
       
    91    Returns given item's bounding rectangle in scene coordinates.
       
    92  */
       
    93 QRectF HbAbstractItemContainerPrivate::itemBoundingRect(const QGraphicsItem *item) const
       
    94 {
       
    95     Q_Q(const HbAbstractItemContainer);
       
    96     if (q->layout()) {
       
    97         q->layout()->activate();
       
    98     }
       
    99     return item->mapToItem(mItemView, item->boundingRect()).boundingRect();
       
   100 }
       
   101 
       
   102 void HbAbstractItemContainerPrivate::firstAndLastVisibleBufferIndex(
       
   103         int& firstVisibleBufferIndex,
       
   104         int& lastVisibleBufferIndex,
       
   105         const QRectF &viewRect,
       
   106         bool fullyVisible) const
       
   107 {
       
   108     Q_Q(const HbAbstractItemContainer);
       
   109 
       
   110     if (q->layout() && !q->layout()->isActivated()) {
       
   111         q->layout()->activate();
       
   112     }
       
   113 
       
   114     firstVisibleBufferIndex = -1;
       
   115     lastVisibleBufferIndex = -1;
       
   116 
       
   117     int count = mItems.count();
       
   118     for (int i = 0; i < count; ++i) {
       
   119         if (visible(mItems.at(i), viewRect, fullyVisible)) {
       
   120             if (firstVisibleBufferIndex == -1) {
       
   121                 firstVisibleBufferIndex = i;
       
   122             }
       
   123             lastVisibleBufferIndex = i;
       
   124         } else if ( lastVisibleBufferIndex != -1 ) {
       
   125             // no need to check the remaining ones.
       
   126             break;
       
   127         }
       
   128     }
       
   129 }
       
   130 
       
   131 /*!
       
   132     \private
       
   133 
       
   134     Returns true if given item is located within viewport (i.e.  view), otherwise
       
   135     returns false. If fullyVisible parameter is true method will return true only
       
   136     for item that is shown fully. In this case for partially visible items false is returned.
       
   137 */
       
   138 bool HbAbstractItemContainerPrivate::visible(HbAbstractViewItem* item, const QRectF &viewRect, bool fullyVisible) const
       
   139 {
       
   140     if (item) {
       
   141         QRectF itemRect(itemBoundingRect(item));
       
   142         if (fullyVisible) {
       
   143             if (viewRect.contains(itemRect)) {
       
   144                 return true;
       
   145             }
       
   146         } else {
       
   147             if (viewRect.intersects(itemRect)) {
       
   148                 return true;
       
   149             }
       
   150         }
       
   151     }
       
   152 
       
   153     return false;
       
   154 }
       
   155 
       
   156 /*!
       
   157   Clears the prototype list and deletes the prototypes.
       
   158 */
       
   159 void HbAbstractItemContainerPrivate::deletePrototypes()
       
   160 {
       
   161     qDeleteAll(mPrototypes);
       
   162     mPrototypes.clear();
       
   163 }
       
   164 
       
   165 int HbAbstractItemContainerPrivate::findStateItem(const QModelIndex &index) const
       
   166 {
       
   167     for (int current = 0; current < mItemStateList.count(); ++current) {
       
   168         if (mItemStateList.at(current).index == index) {
       
   169             return current;
       
   170         }
       
   171     }
       
   172     return -1;
       
   173 }
       
   174 
       
   175 void HbAbstractItemContainerPrivate::initPrototype(HbAbstractViewItem *prototype) const
       
   176 {
       
   177     prototype->setParentItem(mItemView);
       
   178     prototype->setItemView(mItemView);
       
   179     prototype->resize(QSizeF(0, 0));
       
   180     prototype->hide();
       
   181 }
       
   182 
       
   183 HbAbstractViewItem* HbAbstractItemContainerPrivate::createItem(const QModelIndex& index)
       
   184 {
       
   185     Q_Q(HbAbstractItemContainer);
       
   186 
       
   187     HbAbstractViewItem *result = 0;
       
   188     HbAbstractViewItem *prototype = itemPrototype(index);
       
   189     if (prototype) {
       
   190         result = q_check_ptr(prototype->createItem());
       
   191         Q_ASSERT_X(result->prototype() == prototype, "HbAbstractItemContainerPrivate::createItem", "Copy constructor must be used for creating concrete view items in createItem(). Create your custom view item with 'new YourCustomViewItem(*this)' instead of 'new YourCustomViewItem(this)'");
       
   192         result->setParentItem(q);
       
   193 
       
   194         emit q->itemCreated(result);
       
   195     }
       
   196     return result;
       
   197 }
       
   198 
       
   199 HbAbstractViewItem* HbAbstractItemContainerPrivate::itemPrototype(const QModelIndex& index) const
       
   200 {
       
   201     Q_Q(const HbAbstractItemContainer);
       
   202 
       
   203     if (mPrototypes.isEmpty()) {
       
   204         HbAbstractViewItem *defaultPrototype = q->createDefaultPrototype();
       
   205         if (defaultPrototype) {
       
   206             initPrototype(defaultPrototype);
       
   207 
       
   208             mPrototypes.append(defaultPrototype);
       
   209         }
       
   210     }
       
   211 
       
   212     HbAbstractViewItem *result = 0;
       
   213     int count = mPrototypes.count() - 1;
       
   214     for (int i = count; i >= 0; i--) {
       
   215         if (mPrototypes[i]->canSetModelIndex(index)) {
       
   216             result = mPrototypes[i];
       
   217             break;
       
   218         }
       
   219     }
       
   220     return result;
       
   221 }
       
   222 
       
   223 
       
   224 /*!
       
   225     \private
       
   226     
       
   227     Updates item buffer to contain correct amount of items for the current situation.
       
   228 */
       
   229 void HbAbstractItemContainerPrivate::updateItemBuffer()
       
   230 {
       
   231     Q_Q(HbAbstractItemContainer);
       
   232 
       
   233     if (!mItemView) {
       
   234         return;
       
   235     }
       
   236 
       
   237     int targetCount = q->maxItemCount();
       
   238     int itemCount = mItems.count();
       
   239 
       
   240     if (itemCount == targetCount) {
       
   241         return;
       
   242     }
       
   243     
       
   244     // Store the first item position related to view.
       
   245     QPointer<HbAbstractViewItem> firstItem = mItems.value(qMax(0, itemCount - targetCount));
       
   246     QPointF firstItemPos;
       
   247     if (firstItem) {
       
   248         firstItemPos = itemBoundingRect(firstItem).topLeft();
       
   249     }
       
   250 
       
   251     // Perform the incresing/decreasing
       
   252     if (itemCount < targetCount) {
       
   253         increaseBufferSize(targetCount - itemCount);
       
   254     } else {
       
   255         decreaseBufferSize(itemCount - targetCount);
       
   256     }
       
   257 
       
   258     restoreItemPosition(firstItem, firstItemPos);
       
   259 }
       
   260 
       
   261 /*!
       
   262     Increases the item buffer size with given \a amount.
       
   263 
       
   264     First tries to add as many items after the last item with valid
       
   265     index as it can. If there is not enough valid indexes then inserts items
       
   266     before the first item with valid index.
       
   267 */
       
   268 void HbAbstractItemContainerPrivate::increaseBufferSize(int amount)
       
   269 {
       
   270     Q_Q(HbAbstractItemContainer);
       
   271 
       
   272     // Append new items.
       
   273     QModelIndex index;
       
   274     for (int i = mItems.count() - 1; i >= 0; --i) {
       
   275         index = mItems.at(i)->modelIndex();
       
   276         if (index.isValid()) {
       
   277             break;
       
   278         }
       
   279     }
       
   280 
       
   281     int itemsAdded = 0;
       
   282     // in practise following conditions must apply: itemview is empty and scrollTo() has been called.
       
   283     // Starts populating items from given mFirstItemIndex
       
   284     if (    !index.isValid()
       
   285         &&  mFirstItemIndex.isValid()) {
       
   286         index = mFirstItemIndex;
       
   287         q->insertItem(mItems.count(), index);
       
   288         itemsAdded++;
       
   289 
       
   290         mFirstItemIndex = QModelIndex();
       
   291     }
       
   292  
       
   293     while (itemsAdded < amount) {
       
   294         index = mItemView->modelIterator()->nextIndex(index);
       
   295         if (!index.isValid()) {
       
   296             break;
       
   297         }
       
   298         
       
   299         q->insertItem(mItems.count(), index);
       
   300         itemsAdded++;
       
   301     }
       
   302 
       
   303     if (itemsAdded == amount) {
       
   304         return;
       
   305     } 
       
   306 
       
   307     // Prepend new items.
       
   308     for (int i = 0; i < mItems.count(); ++i) {
       
   309         index = mItems.at(i)->modelIndex();
       
   310         if (index.isValid()) {
       
   311             break;
       
   312         }
       
   313     }
       
   314 
       
   315     while (itemsAdded < amount) {
       
   316         index = mItemView->modelIterator()->previousIndex(index);
       
   317         if (!index.isValid()) {
       
   318             break;
       
   319         }
       
   320 
       
   321         q->insertItem(0, index);
       
   322         itemsAdded++;
       
   323     }
       
   324 }
       
   325 
       
   326 /*!
       
   327     Decreases the item buffer size with given \a amount.
       
   328 
       
   329     Tries to avoid deleting visible items and keep the buffer balanced.
       
   330 */
       
   331 void HbAbstractItemContainerPrivate::decreaseBufferSize(int amount)
       
   332 {
       
   333     Q_UNUSED(amount)
       
   334 
       
   335     int firstVisible = 0;
       
   336     int lastVisible = 0;
       
   337     firstAndLastVisibleBufferIndex(firstVisible, lastVisible, mItemView->geometry(), false);
       
   338 
       
   339     int deletableItemsOnTop = firstVisible - 1;
       
   340     int deletableItemsOnBottom = mItems.count() - lastVisible - 1;
       
   341     int itemsDeleted = 0;
       
   342     HbAbstractViewItem* item = 0;
       
   343 
       
   344     // in decreasing the buffer we try to keep it balanced
       
   345     while (itemsDeleted < amount ) {
       
   346         if (deletableItemsOnTop > deletableItemsOnBottom) {
       
   347             item = mItems.takeFirst();
       
   348             deletableItemsOnTop--;
       
   349         } else {
       
   350             item = mItems.takeLast();
       
   351             deletableItemsOnBottom--;
       
   352         }
       
   353         deleteItem(item);
       
   354         ++itemsDeleted;
       
   355      }
       
   356 
       
   357 }
       
   358 
       
   359 /*!
       
   360     \private
       
   361 */
       
   362 HbAbstractViewItem* HbAbstractItemContainerPrivate::item(const QModelIndex &index) const
       
   363 {
       
   364     int itemCount = mItems.count();
       
   365     for (int i = 0; i < itemCount; ++i) {
       
   366         // This could use binary search as model indexes are in sorted.
       
   367         if (mItems.at(i)->modelIndex() == index) {
       
   368             return mItems.at(i);
       
   369         }
       
   370     }
       
   371 
       
   372     // TODO: The lower (commented out) part of the code is an optimized version of the above.
       
   373     // However, there are problems with TreeView's deep models concerning the optimized version.
       
   374     // The optimized version should be fixed and taken into use later on.
       
   375 
       
   376     /*
       
   377     int itemCount = mItems.count();
       
   378     if (itemCount > 0) {
       
   379         if (index.isValid()) {
       
   380             int itemIndex = mItemView->indexPosition(index) - mItemView->indexPosition(mItems.first()->modelIndex());
       
   381             return mItems.value(itemIndex);
       
   382         } else {
       
   383             for (int i = 0; i < itemCount; ++i) {
       
   384                 // This could use binary search as model indexes are in sorted.
       
   385                 HbAbstractViewItem *item = mItems.at(i);
       
   386                 if (item->modelIndex() == index) {
       
   387                     return item;
       
   388                 }
       
   389             }
       
   390         }
       
   391     }
       
   392     */
       
   393 
       
   394     return 0;
       
   395 }
       
   396 
       
   397 /*!
       
   398     \private
       
   399 
       
   400     Removes \a item with model index \a index.
       
   401 */
       
   402 void HbAbstractItemContainerPrivate::doRemoveItem(HbAbstractViewItem *item, const QModelIndex &index, bool animate)
       
   403 {
       
   404     if (item) {
       
   405         deleteItem(item, animate);
       
   406         if (!index.isValid()) {
       
   407             mItemStates.remove(index);
       
   408         }
       
   409     }
       
   410 }
       
   411 
       
   412 
       
   413 /*!
       
   414     \private
       
   415 
       
   416     Deletes \a item.
       
   417 */
       
   418 void HbAbstractItemContainerPrivate::deleteItem(HbAbstractViewItem *item, bool animate)
       
   419 {
       
   420     Q_Q(HbAbstractItemContainer);
       
   421 
       
   422     q->setItemTransientState(item->modelIndex(), item->transientState());
       
   423     mItems.removeOne(item);
       
   424     q->itemRemoved(item, animate);
       
   425 
       
   426 #ifndef HB_EFFECTS
       
   427     delete item;
       
   428 #else
       
   429     if (!HbEffect::effectRunning(item, "disappear") 
       
   430         && !HbEffect::effectRunning(item, "collapse")) {
       
   431         delete item;
       
   432     }
       
   433 #endif
       
   434 }
       
   435 
       
   436 /*
       
   437     \private
       
   438     The previous or the next index must be in the buffer. We cannot assume in this base class 
       
   439     that the container is a list we just have to loop through the items and find if the previous
       
   440     or next exist in the buffer. Next index should not be the first one in the buffer and the
       
   441     previous index should not be the last one in the buffer in order this index to the buffer.
       
   442 */
       
   443 bool HbAbstractItemContainerPrivate::intoContainerBuffer(const QModelIndex &index) const
       
   444 {    
       
   445     QModelIndex nextIndex = mItemView->modelIterator()->nextIndex(index);
       
   446     QModelIndex previousIndex = mItemView->modelIterator()->previousIndex(index);
       
   447 
       
   448     int itemCount = mItems.count();
       
   449     for (int i = 0; i < itemCount; ++i) {
       
   450         QModelIndex currentIndex = mItems.at(i)->modelIndex();
       
   451         if (currentIndex == nextIndex && i != 0){
       
   452             return true;
       
   453         } else if (currentIndex == previousIndex && i != (itemCount - 1)) {
       
   454             return true;
       
   455         }
       
   456     }
       
   457     return false;
       
   458 } 
       
   459 
       
   460 int HbAbstractItemContainerPrivate::containerBufferIndexForModelIndex(const QModelIndex &index) const
       
   461 {   
       
   462     int bufferIndex = 0;
       
   463     QModelIndex nextIndex = mItemView->modelIterator()->nextIndex(index);
       
   464     QModelIndex previousIndex = mItemView->modelIterator()->previousIndex(index);
       
   465 
       
   466     while (bufferIndex < mItems.count()) {
       
   467         QModelIndex currentIndex = mItems.at(bufferIndex)->modelIndex();
       
   468         if (currentIndex == nextIndex) {
       
   469             break;
       
   470         }
       
   471 
       
   472         ++bufferIndex;
       
   473 
       
   474         if (currentIndex == previousIndex) {
       
   475             break;
       
   476         }
       
   477     }
       
   478     return bufferIndex;
       
   479 }
       
   480 
       
   481 void HbAbstractItemContainerPrivate::restoreItemPosition(HbAbstractViewItem *item, const QPointF &position)
       
   482 {
       
   483     Q_Q(HbAbstractItemContainer);
       
   484 
       
   485     if (item) {
       
   486         QPointF delta = itemBoundingRect(item).topLeft() - position;
       
   487         if (!delta.isNull()) {
       
   488             q->setPos(q->pos() - delta);
       
   489 
       
   490             if (mItemView) {
       
   491                 // this will force the HbScrollArea to adjust the content correctly. Adjustment
       
   492                 // is not done in the setPos generated event handling by default to speed up scrolling.
       
   493                 HbAbstractItemViewPrivate::d_ptr(mItemView)->adjustContent();
       
   494             }
       
   495         }
       
   496     }
       
   497 }
       
   498 
       
   499 void HbAbstractItemContainerPrivate::insertItem(HbAbstractViewItem *item, int pos, const QModelIndex &index, bool animate)
       
   500 {
       
   501     Q_Q(HbAbstractItemContainer);
       
   502 
       
   503     if (item) {
       
   504         mItems.insert(pos, item);
       
   505         q->itemAdded(pos, item, animate);
       
   506 
       
   507         q->setItemModelIndex(item, index);
       
   508     }
       
   509 }
       
   510 
       
   511 qreal HbAbstractItemContainerPrivate::getDiffWithoutScrollareaCompensation(const QPointF &delta) const
       
   512 {
       
   513     Q_Q( const HbAbstractItemContainer);
       
   514     const QSizeF containerSize(q->size());
       
   515     const QPointF containerPos(q->pos());
       
   516     qreal diff = 0.0;
       
   517     qreal invisibleArea = 0.0;
       
   518     if (delta.y() > 0) {
       
   519         // space at the bottom
       
   520         QSizeF viewSize = mItemView->size();
       
   521         invisibleArea = containerSize.height() - viewSize.height() + containerPos.y();
       
   522         if (invisibleArea < delta.y()) {
       
   523             diff = delta.y() - invisibleArea;
       
   524         }
       
   525     } else {
       
   526         // space at the top
       
   527         invisibleArea = -containerPos.y();
       
   528         if (containerPos.y() > delta.y()) {
       
   529             diff = delta.y() + invisibleArea;
       
   530         }
       
   531     }
       
   532 
       
   533     return diff;
       
   534 }
       
   535 
       
   536 /*!  
       
   537     Constructs a new HbAbstractItemContainer with \a parent.
       
   538 */
       
   539 HbAbstractItemContainer::HbAbstractItemContainer(QGraphicsItem *parent) : 
       
   540     HbWidget(*new HbAbstractItemContainerPrivate(), parent) 
       
   541 {
       
   542     Q_D(HbAbstractItemContainer);
       
   543 
       
   544 	d->q_ptr = this;
       
   545     d->init();
       
   546 }
       
   547 
       
   548 /*!
       
   549     Constructs an item with private class object \a dd and \a parent. 
       
   550 */
       
   551 HbAbstractItemContainer::HbAbstractItemContainer(HbAbstractItemContainerPrivate &dd, QGraphicsItem *parent) :
       
   552     HbWidget(dd, parent)
       
   553 {
       
   554     Q_D(HbAbstractItemContainer);
       
   555 
       
   556     d->q_ptr = this;
       
   557     d->init();
       
   558 }
       
   559 
       
   560 /*!
       
   561     Destroys the container.
       
   562 */
       
   563 HbAbstractItemContainer::~HbAbstractItemContainer()
       
   564 {
       
   565 }
       
   566 
       
   567 /*!
       
   568     \reimp
       
   569 */
       
   570 bool HbAbstractItemContainer::event(QEvent *e)
       
   571 {
       
   572     if (e->type() == QEvent::LayoutRequest) {
       
   573         QGraphicsWidget *parentWid = parentWidget();
       
   574         if (!parentLayoutItem() 
       
   575             && parentWid) {
       
   576             HbApplication::postEvent(parentWid, new QEvent(QEvent::LayoutRequest));
       
   577         }
       
   578         Q_D(HbAbstractItemContainer);
       
   579         d->updateItemBuffer();
       
   580     } else if (e->type() == UpdateItemBufferEvent) {
       
   581         Q_D(HbAbstractItemContainer);
       
   582         d->updateItemBuffer();
       
   583     }
       
   584 
       
   585     return HbWidget::event(e);
       
   586 }
       
   587 
       
   588 /*!
       
   589     Returns view item object corresponding given model \a index. This might be 0 pointer if
       
   590     there is no view item representing given index or given \a index is invalid.
       
   591 */
       
   592 HbAbstractViewItem* HbAbstractItemContainer::itemByIndex(const QModelIndex &index) const
       
   593 {
       
   594     Q_D(const HbAbstractItemContainer);
       
   595     
       
   596     if (!index.isValid()) {
       
   597         return 0;
       
   598     }
       
   599     return d->item(index);
       
   600 }
       
   601 
       
   602 /*!
       
   603     This function is provided for convenience. 
       
   604     
       
   605     It removes all the items from the container and clears the internal state model.
       
   606 */
       
   607 void HbAbstractItemContainer::removeItems()
       
   608 {
       
   609     Q_D(HbAbstractItemContainer);
       
   610 
       
   611     qDeleteAll(d->mItems);
       
   612     d->mItems.clear();
       
   613     d->mItemStateList.clear();
       
   614     d->mItemStates.clear();
       
   615 }
       
   616 
       
   617 /*!
       
   618     Returns the item view that container is connected.
       
   619 */
       
   620 HbAbstractItemView *HbAbstractItemContainer::itemView() const
       
   621 {
       
   622     Q_D(const HbAbstractItemContainer);
       
   623     return d->mItemView;
       
   624 }
       
   625 
       
   626 /*!
       
   627     Sets item \a view that container is connected.
       
   628 */
       
   629 void HbAbstractItemContainer::setItemView(HbAbstractItemView *view)
       
   630 {
       
   631     Q_D(HbAbstractItemContainer);
       
   632 
       
   633     if (view != d->mItemView) {
       
   634         if (d->mItemView) {
       
   635             d->mItemView->removeEventFilter(this);
       
   636         }
       
   637 
       
   638         d->mItemView = view;
       
   639 
       
   640         foreach (HbAbstractViewItem *prototype, d->mPrototypes) {
       
   641             d->initPrototype(prototype);
       
   642         }
       
   643 
       
   644         if (d->mItemView) {
       
   645             setParentItem(view);
       
   646             d->mItemView->installEventFilter(this);
       
   647         }
       
   648     }
       
   649 }
       
   650 
       
   651 /*!
       
   652     Assigns new model \a index to the given \a item. Item's current state is saved
       
   653     and state for \a index is restored to item.
       
   654 */
       
   655 void HbAbstractItemContainer::setItemModelIndex(HbAbstractViewItem *item, const QModelIndex &index)
       
   656 {
       
   657 
       
   658     if (item && item->modelIndex() != index) { 
       
   659 
       
   660         setItemTransientState(item->modelIndex(), item->transientState());
       
   661 
       
   662         // Transfer the state from item currently representing index to new item, if such exists.
       
   663         HbAbstractViewItem *oldItem = itemByIndex(index);
       
   664 
       
   665         if (oldItem) {
       
   666             item->setTransientState(oldItem->transientState());
       
   667         } else {
       
   668             item->setTransientState(itemTransientState(index));
       
   669         }
       
   670 
       
   671 
       
   672         item->setModelIndex(index);
       
   673     }
       
   674 }
       
   675 
       
   676 /*!
       
   677     Sets item's model indexes starting from given \a startIndex. If \a startIndex is
       
   678     QModelIndex() then startIndex is taken from the first item. 
       
   679 
       
   680     \note If there are not enough model indexes from \a startIndex to last model index 
       
   681     for all the items then QModelIndex() is assigned to rest of the view items.
       
   682 */
       
   683 void HbAbstractItemContainer::setModelIndexes(const QModelIndex &startIndex)
       
   684 {
       
   685     Q_D(HbAbstractItemContainer);
       
   686 
       
   687     if (!d->mItemView || !d->mItemView->model()) {
       
   688         return;
       
   689     }
       
   690 
       
   691     QModelIndex index = startIndex;
       
   692     if (!index.isValid()) {
       
   693         if (!d->mItems.isEmpty()) {
       
   694             index = d->mItems.first()->modelIndex();
       
   695         } 
       
   696         
       
   697         if (!index.isValid()) {
       
   698             index = d->mItemView->modelIterator()->nextIndex(index);
       
   699         }
       
   700     }
       
   701 
       
   702     QModelIndexList indexList;
       
   703 
       
   704     int itemCount(d->mItems.count());
       
   705 
       
   706     if (itemCount != 0 && index.isValid()) {
       
   707         indexList.append(index);
       
   708     }
       
   709 
       
   710     for (int i = indexList.count(); i < itemCount; ++i) {
       
   711         index = d->mItemView->modelIterator()->nextIndex(indexList.last());
       
   712 
       
   713         if (index.isValid()) {
       
   714             indexList.append(index);
       
   715         } else {
       
   716             break;
       
   717         }
       
   718     }
       
   719 
       
   720     for (int i = indexList.count(); i < itemCount; ++i) {
       
   721         index = d->mItemView->modelIterator()->previousIndex(indexList.first());
       
   722 
       
   723         if (index.isValid()) {
       
   724             indexList.prepend(index);
       
   725         } else {
       
   726             break;
       
   727         }
       
   728     }
       
   729 
       
   730     // if items have been added/removed in the middle of the buffer, there might be items 
       
   731 	// that can be reused at the end of the buffer. The following block will scan for 
       
   732 	// those items and move them in correct position 
       
   733     int lastUsedItem = -1;
       
   734     for (int indexCounter = 0; indexCounter < indexList.count(); ++indexCounter) {
       
   735         HbAbstractViewItem *item = d->mItems.at(indexCounter);
       
   736         if (item && item->modelIndex() == indexList.at(indexCounter)) { 
       
   737             lastUsedItem = indexCounter;
       
   738         } else {
       
   739             for (int itemCounter = lastUsedItem + 1; itemCounter < d->mItems.count(); itemCounter++) {
       
   740                 HbAbstractViewItem *item2 = d->mItems.at(itemCounter);
       
   741                 if (item2->modelIndex() == indexList.at(indexCounter)) { 
       
   742                     d->mItems.swap(indexCounter, itemCounter);
       
   743                     itemRemoved(d->mItems.at(indexCounter), false);
       
   744                     itemRemoved(d->mItems.at(itemCounter), false);
       
   745                     itemAdded(qMin(indexCounter, itemCounter), d->mItems.at(qMin(indexCounter, itemCounter)), false);
       
   746                     itemAdded(qMax(indexCounter, itemCounter), d->mItems.at(qMax(indexCounter, itemCounter)), false);
       
   747                     lastUsedItem = itemCounter;
       
   748                     break;
       
   749                 }
       
   750             }
       
   751         }
       
   752     }
       
   753 
       
   754     int indexCount(indexList.count());
       
   755     for (int i = 0; i < itemCount; ++i) {
       
   756         HbAbstractViewItem *item = d->mItems.at(i);
       
   757         HbAbstractViewItem *prototype = 0;
       
   758         // setItemModelIndex() is considered recycling. It is called only, if recycling is permitted
       
   759         if (i < indexCount) {
       
   760             prototype = d->itemPrototype(indexList.at(i));
       
   761         }
       
   762         if (prototype) {
       
   763             if (    prototype == item->prototype()
       
   764                 &&  d->mItemRecycling) {
       
   765                 setItemModelIndex(item, indexList.at(i));
       
   766             } else if (     d->mItemRecycling
       
   767                        ||   (   !d->mItemRecycling
       
   768                             &&  item->modelIndex() != indexList.at(i))) {
       
   769                 d->deleteItem(item);
       
   770                 insertItem(i, indexList.at(i));
       
   771             } // else: !d->mItemRecycling && item->modelIndex().isValid() == indexList.at(i))
       
   772         } else {
       
   773             d->deleteItem(item);
       
   774             itemCount--;
       
   775             i--;
       
   776         }
       
   777     }
       
   778 }
       
   779 
       
   780 /*!
       
   781     Returns list of items inside item buffer.
       
   782 */
       
   783 QList<HbAbstractViewItem *> HbAbstractItemContainer::items() const
       
   784 {
       
   785     Q_D(const HbAbstractItemContainer);
       
   786     return d->mItems;
       
   787 }
       
   788 
       
   789 /*!
       
   790     Adds item for model \a index to container.
       
   791 */
       
   792 void HbAbstractItemContainer::addItem(const QModelIndex &index, bool animate)
       
   793 {
       
   794     Q_D(HbAbstractItemContainer);
       
   795 
       
   796     // Add new item if maximum item count allows it or item falls within the range of
       
   797     // item buffer items. 
       
   798     if (d->intoContainerBuffer(index)) {
       
   799         int bufferIndex = d->containerBufferIndexForModelIndex(index); 
       
   800 
       
   801         if (bufferIndex >= d->mItems.count()
       
   802             || d->mItems.at(bufferIndex)->modelIndex() != index) {
       
   803             // Store the second item position related to view.
       
   804             HbAbstractViewItem *referenceItem = d->mItems.value(1);
       
   805             QPointF referenceItemPos;
       
   806             if (referenceItem) {
       
   807                 referenceItemPos = d->itemBoundingRect(referenceItem).topLeft();
       
   808             }
       
   809 
       
   810             HbAbstractViewItem *recycledItem = 0;
       
   811             QRectF viewRect = d->itemBoundingRect(d->mItemView);
       
   812 
       
   813             if (d->mItemRecycling && !viewRect.isEmpty()) {
       
   814                 // Recycling allowed. Try recycling the items from buffer.
       
   815                 int firstVisible = 0;
       
   816                 int lastVisible = 0;
       
   817                 d->firstAndLastVisibleBufferIndex(firstVisible, lastVisible, viewRect, false);
       
   818 
       
   819                 int itemsOnTop = firstVisible - 1;
       
   820                 int itemsOnBottom = d->mItems.count() - lastVisible - 1;
       
   821 
       
   822                 if (itemsOnBottom > 0) {
       
   823                     recycledItem = d->mItems.takeLast();
       
   824                 } else if (itemsOnTop > 0) {
       
   825                     recycledItem = d->mItems.takeFirst();
       
   826                     bufferIndex--;
       
   827                     bufferIndex = qMax(0, bufferIndex);
       
   828                 }
       
   829 
       
   830                 if (recycledItem) {
       
   831                     itemRemoved(recycledItem, false);
       
   832                     d->insertItem(recycledItem, bufferIndex, index, animate);
       
   833                 }
       
   834             }
       
   835 
       
   836             if (!recycledItem) {
       
   837                 // No recycling has happened. Insert completely new item.
       
   838                 insertItem(bufferIndex, index, animate);
       
   839             }
       
   840 
       
   841             // Restore second item position.
       
   842             d->restoreItemPosition(referenceItem, referenceItemPos);
       
   843             
       
   844             if (!recycledItem && d->mItemRecycling) {
       
   845                 // Resize the buffer.
       
   846                 d->updateItemBuffer();
       
   847             }
       
   848         }
       
   849     } else if (d->mItems.count() < maxItemCount()) {
       
   850         d->updateItemBuffer();
       
   851     }
       
   852 }
       
   853 
       
   854 /*!
       
   855     Removes item representing \a index from container.
       
   856 */
       
   857 void HbAbstractItemContainer::removeItem(const QModelIndex &index, bool animate)
       
   858 {
       
   859     Q_D(HbAbstractItemContainer);
       
   860 
       
   861     HbAbstractViewItem *item = d->item(index);
       
   862     d->doRemoveItem(item, index, animate);
       
   863 }
       
   864 
       
   865 /*!
       
   866     Removes item from \a pos.
       
   867 */
       
   868 void HbAbstractItemContainer::removeItem(int pos, bool animate)
       
   869 {
       
   870     Q_D(HbAbstractItemContainer);
       
   871 
       
   872     Q_ASSERT(pos >= 0 && pos < d->mItems.count());        
       
   873 
       
   874     HbAbstractViewItem *item = d->mItems.at(pos);
       
   875     QModelIndex index = item->modelIndex();
       
   876     d->doRemoveItem(item, index, animate);
       
   877 }
       
   878 
       
   879 /*!
       
   880     Derived class should implement this function to perform item recycling based on container \a delta.
       
   881     Given \a delta is the distance between container's current position and desired new position. Recycling
       
   882     should be done based on the new position and function should return the actual delta. Actual delta could
       
   883     be modified value from \a delta. Delta modification can be useful e.g. to compensate the item changes
       
   884     that are caused by relayouting of items after changing the item position within layout.
       
   885 
       
   886     Default implementation does not recycle items and returns the \a delta unchanged.
       
   887 */
       
   888 QPointF HbAbstractItemContainer::recycleItems(const QPointF &delta)
       
   889 {
       
   890     return delta;
       
   891 }
       
   892 
       
   893 /*!
       
   894     \reimp
       
   895 */
       
   896 bool HbAbstractItemContainer::eventFilter(QObject *obj, QEvent *event)
       
   897 {
       
   898     Q_D(HbAbstractItemContainer);
       
   899 
       
   900     if (obj == d->mItemView) {
       
   901         switch (event->type()){
       
   902             case QEvent::GraphicsSceneResize: {
       
   903                 viewResized(d->mItemView->size());
       
   904                 d->updateItemBuffer();
       
   905                 break;
       
   906             }
       
   907             default:
       
   908                 break;
       
   909         }
       
   910     }
       
   911 
       
   912     return HbWidget::eventFilter(obj, event);
       
   913 }
       
   914 
       
   915 /*!
       
   916     Returns maximum amount of items that item buffer can hold. 
       
   917 
       
   918     Default implementation returns the total number of indexes that can
       
   919     be accessed on current view.
       
   920 
       
   921     Derived class that supports item recycling should define their own implementation
       
   922     for this function.
       
   923 
       
   924     \note Return value should not be more than HbAbstractItemView::indexCount()
       
   925 */
       
   926 int HbAbstractItemContainer::maxItemCount() const
       
   927 {    
       
   928     Q_D(const HbAbstractItemContainer);
       
   929 
       
   930     if (d->mItemView) {
       
   931         return d->mItemView->modelIterator()->indexCount();
       
   932     } else {
       
   933         return 0;
       
   934     }
       
   935 }
       
   936 
       
   937 /*!
       
   938     Deletes other prototypes and sets \a prototype as only prototype.
       
   939 
       
   940     Returns true if the prototype list was changed; otherwise returns false.
       
   941 */
       
   942 bool HbAbstractItemContainer::setItemPrototype(HbAbstractViewItem *prototype)
       
   943 {
       
   944     QList<HbAbstractViewItem *> prototypeList;
       
   945     prototypeList.append(prototype);
       
   946     return setItemPrototypes(prototypeList);
       
   947 }
       
   948 
       
   949 /*!
       
   950     Returns the list of item prototypes.
       
   951 */
       
   952 QList<HbAbstractViewItem *> HbAbstractItemContainer::itemPrototypes() const
       
   953 {
       
   954     Q_D(const HbAbstractItemContainer);
       
   955     
       
   956     if (d->mPrototypes.isEmpty()) {
       
   957         HbAbstractViewItem *defaultPrototype = createDefaultPrototype();
       
   958         if (defaultPrototype) {
       
   959             d->initPrototype(defaultPrototype);
       
   960 
       
   961             d->mPrototypes.append(defaultPrototype);
       
   962         }
       
   963     }
       
   964 
       
   965     return d->mPrototypes;
       
   966 }
       
   967 
       
   968 /*!
       
   969     Sets the list of prototypes. 
       
   970     
       
   971     Returns true if the prototype list was changed; otherwise returns false.
       
   972 */
       
   973 bool HbAbstractItemContainer::setItemPrototypes(const QList<HbAbstractViewItem *> &prototypes)
       
   974 {
       
   975     Q_D(HbAbstractItemContainer);
       
   976     
       
   977     bool changed = false;
       
   978 
       
   979     if (prototypes.count() > 0) {
       
   980         if (d->mPrototypes != prototypes) {
       
   981             foreach (HbAbstractViewItem *prototype, d->mPrototypes) {
       
   982                 if (!prototypes.contains(prototype)) {
       
   983                     delete prototype;
       
   984                 }
       
   985             }
       
   986 
       
   987             foreach (HbAbstractViewItem *prototype, prototypes) {
       
   988                 if (!d->mPrototypes.contains(prototype)) {
       
   989                     d->initPrototype(prototype);
       
   990                 }
       
   991             }
       
   992             changed = true;
       
   993         }
       
   994 
       
   995         d->mPrototypes = prototypes;
       
   996     }
       
   997 
       
   998     return changed;
       
   999     
       
  1000 }
       
  1001 
       
  1002 /*!
       
  1003     Returns transient state of view item with \a index.
       
  1004 */
       
  1005 QHash<QString, QVariant> HbAbstractItemContainer::itemTransientState(const QModelIndex &index) const
       
  1006 {
       
  1007     Q_D(const HbAbstractItemContainer);
       
  1008     return d->mItemStates.value(index);
       
  1009 }
       
  1010 
       
  1011 /*!
       
  1012     This is an overloaded member function, provided for convenience.
       
  1013 
       
  1014     Stores \a key with \a value of a view item with \a index into state model.
       
  1015     \a key is usually name of a Qt property. If \a value is invalid, state item with the \a key is removed.
       
  1016 
       
  1017     Default values of properties should not be added.
       
  1018 */
       
  1019 void HbAbstractItemContainer::setItemTransientStateValue(const QModelIndex &index, const QString &key, const QVariant &value)
       
  1020 {
       
  1021     Q_D(HbAbstractItemContainer);
       
  1022     if (index.isValid()) {
       
  1023         QHash<QString, QVariant> stateItem = d->mItemStates.value(index);
       
  1024         if (!value.isValid()) {
       
  1025             stateItem.remove(key);
       
  1026         } else {
       
  1027             stateItem.insert(key, value);
       
  1028         }
       
  1029         if (stateItem.count()) {
       
  1030             d->mItemStates.insert(index, stateItem);
       
  1031         } else {
       
  1032             d->mItemStates.remove(index);
       
  1033         }
       
  1034     } else {
       
  1035         d->mItemStates.remove(index);
       
  1036     }
       
  1037 }
       
  1038 
       
  1039 
       
  1040 /*!
       
  1041     Stores state of a view item with \a index into item state model. State of the view item is usually 
       
  1042     retrieved by calling HbAbstractViewItem::transientState().
       
  1043     
       
  1044     Existing state is replaced. If \a state is empty, existing state is removed. 
       
  1045     Default values of state items should not be added into \a state.
       
  1046 
       
  1047     \sa HbAbstractViewItem::transientState()
       
  1048 */
       
  1049 void HbAbstractItemContainer::setItemTransientState(const QModelIndex &index, QHash<QString,QVariant> state)
       
  1050 {
       
  1051     Q_D(HbAbstractItemContainer);
       
  1052     if (index.isValid() && state.count()) {
       
  1053         d->mItemStates.insert(index, state);
       
  1054     } else {
       
  1055         d->mItemStates.remove(index);
       
  1056     }
       
  1057 }
       
  1058 
       
  1059 /*!
       
  1060     \reimp
       
  1061 */
       
  1062 QVariant HbAbstractItemContainer::itemChange(GraphicsItemChange change, const QVariant & value)
       
  1063 {
       
  1064     return HbWidget::itemChange(change, value);
       
  1065 }
       
  1066 
       
  1067 /*!
       
  1068     Returns the model indexes of items that are located on top left and bottom right corners
       
  1069     of visible area.
       
  1070 */
       
  1071 void HbAbstractItemContainer::firstAndLastVisibleModelIndex(
       
  1072         QModelIndex& firstVisibleModelIndex,
       
  1073         QModelIndex& lastVisibleModelIndex,
       
  1074         bool fullyVisible) const
       
  1075 {
       
  1076     Q_D(const HbAbstractItemContainer);
       
  1077 
       
  1078     QRectF viewRect(d->itemBoundingRect(d->mItemView));
       
  1079 
       
  1080     int firstVisibleBufferIndex( -1 );
       
  1081     int lastVisibleBufferIndex( -1 );
       
  1082     d->firstAndLastVisibleBufferIndex( firstVisibleBufferIndex, lastVisibleBufferIndex, viewRect, fullyVisible );
       
  1083     if (firstVisibleBufferIndex != -1) {
       
  1084         firstVisibleModelIndex = d->mItems.at(firstVisibleBufferIndex)->modelIndex();
       
  1085     }
       
  1086     if (lastVisibleBufferIndex != -1) {
       
  1087         lastVisibleModelIndex = d->mItems.at(lastVisibleBufferIndex)->modelIndex();
       
  1088     }
       
  1089 }
       
  1090 
       
  1091 /*!
       
  1092     Clears the state model.
       
  1093 */
       
  1094 void HbAbstractItemContainer::removeItemTransientStates()
       
  1095 {
       
  1096     Q_D(HbAbstractItemContainer);
       
  1097     d->mItemStateList.clear();
       
  1098     d->mItemStates.clear();
       
  1099 }
       
  1100 
       
  1101 /*!
       
  1102     Sets item recycling to \a enabled.
       
  1103     By default recycling is off.
       
  1104  */
       
  1105 void HbAbstractItemContainer::setItemRecycling(bool enabled)
       
  1106 {
       
  1107     Q_D(HbAbstractItemContainer);
       
  1108     if (d->mItemRecycling != enabled) {
       
  1109         d->mItemRecycling = enabled;
       
  1110         if (!enabled) {
       
  1111             removeItemTransientStates();
       
  1112         }
       
  1113 
       
  1114         d->updateItemBuffer();
       
  1115     }
       
  1116 }
       
  1117 
       
  1118 /*!
       
  1119     Returns whether item recycling feature is in use.
       
  1120  */
       
  1121 bool HbAbstractItemContainer::itemRecycling() const
       
  1122 {
       
  1123     Q_D(const HbAbstractItemContainer);
       
  1124     return d->mItemRecycling;
       
  1125 }
       
  1126 
       
  1127 /*!
       
  1128     Sets the feature informing whether all items in the item view have the same size.
       
  1129     In case all the items have the same size, the item view can do some 
       
  1130     optimizations for performance purposes.
       
  1131 */
       
  1132 void HbAbstractItemContainer::setUniformItemSizes(bool enable)
       
  1133 {
       
  1134     Q_D(HbAbstractItemContainer);
       
  1135     d->mUniformItemSizes = enable;
       
  1136 }
       
  1137 
       
  1138 /*!
       
  1139     Returns whether the uniform item sizes feature is in use.
       
  1140  */
       
  1141 bool HbAbstractItemContainer::uniformItemSizes() const
       
  1142 {
       
  1143     Q_D(const HbAbstractItemContainer);
       
  1144     return d->mUniformItemSizes;
       
  1145 }
       
  1146 
       
  1147 /*!
       
  1148     Inserts item for \a index to \a pos.
       
  1149 */
       
  1150 void HbAbstractItemContainer::insertItem(int pos, const QModelIndex &index, bool animate)
       
  1151 {
       
  1152     Q_D(HbAbstractItemContainer);
       
  1153     HbAbstractViewItem *item = d->createItem(index);
       
  1154     d->insertItem(item, pos, index, animate);
       
  1155 }
       
  1156 
       
  1157 /*!
       
  1158     Reset the internal state of the container.
       
  1159 */
       
  1160 void HbAbstractItemContainer::reset()
       
  1161 {
       
  1162     // position need to be reseted while changing model
       
  1163     setPos(0.0, 0.0);
       
  1164     removeItems(); 
       
  1165     QCoreApplication::postEvent(this, new QEvent((QEvent::Type)UpdateItemBufferEvent));
       
  1166 }
       
  1167 
       
  1168 /*!
       
  1169 */
       
  1170 void HbAbstractItemContainer::resizeContainer()
       
  1171 {
       
  1172     Q_D(HbAbstractItemContainer);
       
  1173 
       
  1174     QSizeF newSize = effectiveSizeHint(Qt::PreferredSize);
       
  1175 
       
  1176     if (d->mItemView) {
       
  1177         Qt::Orientations scrollingDirections = d->mItemView->scrollDirections();
       
  1178 
       
  1179         if (!scrollingDirections.testFlag(Qt::Vertical)) {
       
  1180             newSize.setHeight(d->mItemView->size().height());
       
  1181         } 
       
  1182         
       
  1183         if (!scrollingDirections.testFlag(Qt::Horizontal)) {
       
  1184             newSize.setWidth(d->mItemView->size().width());
       
  1185         }
       
  1186     }
       
  1187        
       
  1188     resize(newSize);
       
  1189 }
       
  1190 
       
  1191 #include "moc_hbabstractitemcontainer_p.cpp"
       
  1192