src/hbwidgets/itemviews/hbtreeitemcontainer_p.cpp
changeset 0 16d8024aca5e
child 1 f7ac710697a9
equal deleted inserted replaced
-1:000000000000 0:16d8024aca5e
       
     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 
       
    26 #include "hbtreeitemcontainer_p.h"
       
    27 #include "hbtreeitemcontainer_p_p.h"
       
    28 #include "hbtreelayout_p.h"
       
    29 #include "hbabstractitemcontainer_p.h"
       
    30 #include "hbabstractitemview.h"
       
    31 #include "hbtreeviewitem.h"
       
    32 #include "hbapplication.h"
       
    33 #include "hbmodeliterator.h"
       
    34 
       
    35 #include <qmath.h>
       
    36 
       
    37 const int Hb_Recycle_Buffer_Shrink_Threshold = 2;
       
    38 
       
    39 HbTreeItemContainerPrivate::HbTreeItemContainerPrivate() :
       
    40     HbAbstractItemContainerPrivate(),
       
    41     mLayout(0),
       
    42     mUserIndentation(-1.0),
       
    43     mStyleIndentation(15.0)
       
    44 {
       
    45 }
       
    46 
       
    47 HbTreeItemContainerPrivate::~HbTreeItemContainerPrivate()
       
    48 {
       
    49 }
       
    50 
       
    51 void HbTreeItemContainerPrivate::init()
       
    52 {   
       
    53     Q_Q(HbTreeItemContainer);
       
    54 
       
    55     mLayout = new HbTreeLayout();
       
    56     mLayout->setContentsMargins(0, 0, 0, 0);
       
    57     
       
    58     q->setLayout(mLayout);
       
    59 }
       
    60 
       
    61 int HbTreeItemContainerPrivate::levelForItem(HbAbstractViewItem *item) const
       
    62 {
       
    63     int level = 0;
       
    64     if (mItemView) {
       
    65         QModelIndex parentIndex = item->modelIndex().parent();
       
    66         QModelIndex rootIndex = mItemView->rootIndex();
       
    67 
       
    68         while (parentIndex != rootIndex && parentIndex.isValid()) {
       
    69             level++;
       
    70             parentIndex = parentIndex.parent();
       
    71         }
       
    72     }
       
    73     return level;
       
    74 }
       
    75 
       
    76 /*!
       
    77     \private
       
    78 
       
    79     Changes first abstract view item from item buffer to be the last one.
       
    80 */
       
    81 HbAbstractViewItem *HbTreeItemContainerPrivate::shiftDownItem(QPointF& delta)
       
    82 {
       
    83     Q_Q(HbTreeItemContainer);
       
    84 
       
    85     HbAbstractViewItem *item = 0;
       
    86     HbAbstractViewItem *lastItem = mItems.last();
       
    87 
       
    88     QModelIndex nextIndex = mItemView->modelIterator()->nextIndex(lastItem->modelIndex());
       
    89     if (nextIndex.isValid()) {
       
    90         item = mItems.takeFirst();
       
    91 
       
    92         q->itemRemoved(item);
       
    93 
       
    94         delta.setY(delta.y() - item->size().height());
       
    95 
       
    96         mItems.append(item);
       
    97 
       
    98         q->setItemModelIndex(item, nextIndex);
       
    99 
       
   100         q->itemAdded(mItems.count() - 1, item);
       
   101     }
       
   102 
       
   103     return item;
       
   104 }
       
   105 
       
   106 /*!
       
   107     \private
       
   108 
       
   109     Changes last view item from item buffer to be the first one.
       
   110 */
       
   111 HbAbstractViewItem *HbTreeItemContainerPrivate::shiftUpItem(QPointF& delta)
       
   112 {
       
   113     Q_Q(HbTreeItemContainer);
       
   114 
       
   115     HbAbstractViewItem *item = 0;
       
   116     HbAbstractViewItem *firstItem = mItems.first();
       
   117 
       
   118     QModelIndex previousIndex = mItemView->modelIterator()->previousIndex(firstItem->modelIndex());
       
   119     if (previousIndex.isValid()) {
       
   120         item = mItems.takeLast();
       
   121 
       
   122         q->itemRemoved(item);
       
   123 
       
   124         mItems.insert(0, item);
       
   125 
       
   126         q->setItemModelIndex(item, previousIndex);
       
   127 
       
   128         qreal itemHeight=0;
       
   129         if (q->uniformItemSizes()) {
       
   130             itemHeight = mItems.last()->preferredHeight();
       
   131         } else {
       
   132             //This is time consuming and causes backwards srolling to be slower than forwards.
       
   133             //The sizehint of the item is dirty.
       
   134             itemHeight = item->preferredHeight();
       
   135         }
       
   136         
       
   137         delta.setY(delta.y() + itemHeight);
       
   138 
       
   139         q->itemAdded(0, item);
       
   140     }
       
   141     return item;
       
   142 }
       
   143 
       
   144 void HbTreeItemContainerPrivate::resolveIndentation()
       
   145 {
       
   146     Q_Q(const HbTreeItemContainer);
       
   147     qreal indentation = mUserIndentation;
       
   148     if (indentation < 0.0) {
       
   149         q->style()->parameter("hb-param-margin-gene-left", indentation);
       
   150         if (indentation >= 0.0) {
       
   151             mStyleIndentation = indentation;
       
   152         } else {
       
   153             indentation = mStyleIndentation;
       
   154         }
       
   155     }
       
   156     mLayout->setIndentation(indentation);
       
   157 }
       
   158 
       
   159 qreal HbTreeItemContainerPrivate::getSmallestItemHeight() const
       
   160 {
       
   161     Q_Q(const HbTreeItemContainer);
       
   162 
       
   163     qreal minHeight = 0;
       
   164     if (mItems.count() > 0) {
       
   165         minHeight = mLayout->smallestItemHeight();
       
   166     }
       
   167 
       
   168     if (minHeight == 0) {
       
   169         QModelIndex index;
       
   170         while (mItems.isEmpty()) {
       
   171             // in practise following conditions must apply: itemview is empty and scrollTo() has been called.
       
   172             // Starts populating items from given mFirstItemIndex
       
   173             if ( mFirstItemIndex.isValid()) {
       
   174                 index = mFirstItemIndex;
       
   175                 const_cast<QPersistentModelIndex&>(mFirstItemIndex) = QModelIndex();
       
   176             } else {
       
   177                 index = mItemView->modelIterator()->nextIndex(index);
       
   178             }
       
   179             if (!index.isValid()) {
       
   180                 break;
       
   181             }
       
   182             const_cast<HbTreeItemContainer *>(q)->insertItem(0, index);
       
   183         }
       
   184 
       
   185         if (!mItems.isEmpty()) {
       
   186             minHeight = mItems.first()->preferredHeight();
       
   187         }
       
   188     }
       
   189     return minHeight;
       
   190 }
       
   191 
       
   192 
       
   193 HbTreeItemContainer::HbTreeItemContainer(QGraphicsItem *parent) :
       
   194     HbAbstractItemContainer(*new HbTreeItemContainerPrivate, parent)
       
   195 {
       
   196     Q_D(HbTreeItemContainer);
       
   197 
       
   198     d->q_ptr = this;
       
   199     d->init();
       
   200 }
       
   201 
       
   202 HbTreeItemContainer::HbTreeItemContainer( HbTreeItemContainerPrivate &dd, QGraphicsItem *parent) :
       
   203     HbAbstractItemContainer(dd, parent)
       
   204 {
       
   205     Q_D(HbTreeItemContainer);
       
   206 
       
   207     d->q_ptr = this;
       
   208     d->init();
       
   209 }
       
   210 
       
   211 /*!
       
   212     Destructor.
       
   213  */
       
   214 HbTreeItemContainer::~HbTreeItemContainer()
       
   215 {
       
   216 }
       
   217 
       
   218 /*!
       
   219     \reimp
       
   220 */
       
   221 void HbTreeItemContainer::itemRemoved(HbAbstractViewItem *item, bool animate)
       
   222 {
       
   223     Q_UNUSED(animate);
       
   224     Q_D(HbTreeItemContainer);
       
   225 
       
   226     d->mLayout->removeItem(item);
       
   227 }
       
   228 
       
   229 /*!
       
   230     \reimp
       
   231 */
       
   232 void HbTreeItemContainer::itemAdded(int index, HbAbstractViewItem *item, bool animate)
       
   233 {
       
   234     Q_UNUSED(animate);
       
   235     Q_D(HbTreeItemContainer);
       
   236 
       
   237     d->mLayout->insertItem(index, item, d->levelForItem(item));
       
   238 }
       
   239 
       
   240 /*!
       
   241     \reimp
       
   242 */
       
   243 void HbTreeItemContainer::viewResized(const QSizeF &size)
       
   244 {
       
   245     Q_D(HbTreeItemContainer);
       
   246     d->mLayout->setMinimumWidth(size.width());
       
   247 }
       
   248 
       
   249 void HbTreeItemContainer::setItemModelIndex(HbAbstractViewItem *item, const QModelIndex &index)
       
   250 {
       
   251     if (item && item->modelIndex() != index) {
       
   252         HbAbstractItemContainer::setItemModelIndex(item, index);
       
   253 
       
   254         Q_D(HbTreeItemContainer);
       
   255         d->mLayout->setItemLevel(item, d->levelForItem(item));
       
   256     }
       
   257 }
       
   258 
       
   259 
       
   260 qreal HbTreeItemContainer::indentation() const
       
   261 {
       
   262     Q_D(const HbTreeItemContainer);
       
   263     return d->mUserIndentation;
       
   264 }
       
   265 
       
   266 void HbTreeItemContainer::setIndentation(qreal indentation)
       
   267 {
       
   268     Q_D(HbTreeItemContainer);
       
   269     if (d->mUserIndentation != indentation) {
       
   270         d->mUserIndentation = indentation;
       
   271         d->resolveIndentation();
       
   272     }
       
   273 }
       
   274 
       
   275 /*!
       
   276     \reimp
       
   277 */
       
   278 QPointF HbTreeItemContainer::recycleItems(const QPointF &delta)
       
   279 {
       
   280     Q_D(HbTreeItemContainer);
       
   281 
       
   282     if (d->mPrototypes.count() != 1) {
       
   283         return delta;
       
   284     }
       
   285 
       
   286     QRectF viewRect(d->itemBoundingRect(d->mItemView));
       
   287     viewRect.moveTopLeft(viewRect.topLeft() + delta);
       
   288 
       
   289     int firstVisibleBufferIndex = -1;
       
   290     int lastVisibleBufferIndex = -1;
       
   291     d->firstAndLastVisibleBufferIndex(firstVisibleBufferIndex, lastVisibleBufferIndex, viewRect, false);
       
   292 
       
   293     int hiddenAbove = firstVisibleBufferIndex;
       
   294     int hiddenBelow = d->mItems.count() - lastVisibleBufferIndex - 1;
       
   295 
       
   296     if (d->mItems.count()
       
   297         && (firstVisibleBufferIndex == -1 || lastVisibleBufferIndex == -1)) {
       
   298         // All items out of sight.
       
   299         if (d->itemBoundingRect(d->mItems.first()).top() < 0) {
       
   300             // All items above view.
       
   301             hiddenAbove = d->mItems.count();
       
   302             hiddenBelow = 0;
       
   303         } else {
       
   304             // All items below view.
       
   305             hiddenAbove = 0;
       
   306             hiddenBelow = d->mItems.count();
       
   307         }
       
   308     }
       
   309 
       
   310     QPointF newDelta(delta);
       
   311 
       
   312     while (hiddenAbove > hiddenBelow + 1) {
       
   313         HbAbstractViewItem *item = d->shiftDownItem(newDelta);
       
   314         if (!item){
       
   315             break;
       
   316         }
       
   317 
       
   318         if (!d->visible(item, viewRect)) {
       
   319             hiddenBelow++;
       
   320         }
       
   321         hiddenAbove--;
       
   322     }
       
   323 
       
   324     while (hiddenBelow > hiddenAbove + 1) {
       
   325         HbAbstractViewItem *item = d->shiftUpItem(newDelta);
       
   326         if (!item) {
       
   327             break;
       
   328         }
       
   329 
       
   330         if (!d->visible( item, viewRect)) {
       
   331             hiddenAbove++;
       
   332         }
       
   333         hiddenBelow--;
       
   334     }
       
   335 
       
   336     // during scrolling the x-coordinate of the container is moved to match the 
       
   337     // indentation level of the visible items. 
       
   338     if (!itemView()->isDragging() ) {
       
   339         int minIndent = d->levelForItem(d->mItems[hiddenAbove]);
       
   340         for (int i = hiddenAbove + 1; i < d->mItems.count() - hiddenBelow - 1; i++) {
       
   341             minIndent = qMin(minIndent,d->levelForItem(d->mItems[i]));
       
   342         }
       
   343         // if the indentation level is bigger than the current position, container is moved to right.
       
   344         // pixel amount of one indentation is added to the current position, in deep indentation 
       
   345         // levels this will make the container to show some empty on the left side to indicate that 
       
   346         // the items are not on the root level. This is just a visual trick
       
   347         if (HbApplication::layoutDirection() == Qt::LeftToRight) {
       
   348             if ( minIndent * d->mLayout->indentation() > -pos().x() + d->mLayout->indentation() + 1) {
       
   349                 newDelta.setX( newDelta.x() + 1 );
       
   350             } else if ( minIndent * d->mLayout->indentation() < -pos().x() + d->mLayout->indentation()){
       
   351                 newDelta.setX( newDelta.x() - 1 );
       
   352             }
       
   353         } 
       
   354     }
       
   355 
       
   356     return newDelta;
       
   357 }
       
   358 
       
   359 
       
   360 /*!
       
   361     Calculates the optimal view item buffer size. When recycling is turned off
       
   362     returned buffer same is same as the amount of rows within the model. When 
       
   363     recycling is enabled the amount is calculated by dividing the view area with
       
   364     average item height. Average item height is taken from the average of existing
       
   365     view item's heights or by creating item using prototype for the first row.
       
   366 */
       
   367 int HbTreeItemContainer::maxItemCount() const
       
   368 {
       
   369     Q_D(const HbTreeItemContainer);
       
   370 
       
   371     int targetCount = HbAbstractItemContainer::maxItemCount();
       
   372     if (targetCount > 0
       
   373         && d->mItemRecycling
       
   374         && d->mPrototypes.count() <= 1) {
       
   375         qreal minHeight = d->getSmallestItemHeight();
       
   376 
       
   377         if (minHeight > 0) {
       
   378             int countEstimate = qCeil(d->mItemView->boundingRect().height() / minHeight)
       
   379                 + d->mBufferSize;
       
   380 
       
   381             int currentCount = d->mItems.count();
       
   382             if (countEstimate < currentCount) {
       
   383                 // Shrink the recycle buffer only if change is more than
       
   384                 // buffer shrink threshold. 
       
   385                 if (countEstimate > (currentCount - Hb_Recycle_Buffer_Shrink_Threshold)) {
       
   386                     countEstimate = currentCount;
       
   387                 }
       
   388             }
       
   389 
       
   390             // This limits the targetCount not to be larger
       
   391             // than row count inside model.
       
   392             targetCount = qMin(targetCount, countEstimate);
       
   393         } else {
       
   394             targetCount = 0;
       
   395         }
       
   396     } 
       
   397     return targetCount;
       
   398 }
       
   399 
       
   400 bool  HbTreeItemContainer::event(QEvent *e)
       
   401 {
       
   402     Q_D(HbTreeItemContainer);
       
   403     if (e->type() == QEvent::LayoutRequest) {
       
   404         // User indentation overrides style indentation.
       
   405         // Resolving is here not to do it too often or too seldom
       
   406         d->resolveIndentation();
       
   407     }
       
   408     return HbAbstractItemContainer::event(e);
       
   409 }
       
   410 
       
   411 HbAbstractViewItem *HbTreeItemContainer::createDefaultPrototype() const
       
   412 {
       
   413     return new HbTreeViewItem();
       
   414 }
       
   415 
       
   416 bool HbTreeItemContainer::isExpanded(const QModelIndex &index) const
       
   417 {
       
   418     QVariant flags = itemState(index).value(HbTreeViewItem::ExpansionKey);
       
   419     if (flags.isValid() && flags.toBool() == true) {
       
   420         return true;
       
   421     } else {
       
   422         return false;
       
   423     }
       
   424 }
       
   425 
       
   426 void HbTreeItemContainer::setExpanded(const QModelIndex &index, bool expanded) 
       
   427 {
       
   428     Q_D(HbTreeItemContainer);
       
   429 
       
   430     HbTreeViewItem *item = qobject_cast<HbTreeViewItem *>(itemByIndex(index));
       
   431     if (item) {
       
   432         item->setExpanded(expanded);
       
   433     } 
       
   434 
       
   435     setItemStateValue(index, HbTreeViewItem::ExpansionKey, expanded);
       
   436     setModelIndexes();
       
   437     d->updateItemBuffer(); // Expanding/Collapsing might resize the buffer.
       
   438 }
       
   439 
       
   440 #include "moc_hbtreeitemcontainer_p.cpp"
       
   441