ganeswidgets/src/hgwidgets_p.cpp
changeset 0 89c329efa980
child 1 e48454f237ca
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ganeswidgets/src/hgwidgets_p.cpp	Mon Apr 19 14:40:06 2010 +0300
@@ -0,0 +1,693 @@
+/*
+* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+#include <QGraphicsLinearLayout>
+#include <QApplication>
+#include <hbscrollbar_p.h>
+#include <hgwidgets/hgwidgets.h>
+
+#include "hgwidgets_p.h"
+#include "hgcontainer.h"
+#include "hgcoverflowcontainer.h"
+#include "hgscrollbuffermanager.h"
+#include "hgwidgetitem.h"
+#include "trace.h"
+#include "hgindexfeedback.h"
+
+static const int INITIAL_SCROLLBAR_HIDE_TIMEOUT(4000);
+static const int DEFAULT_BUFFER_SIZE(40);
+
+HgWidgetPrivate::HgWidgetPrivate() :
+    mLayout(0),
+    mContainer(0),
+    mBufferManager(0),
+    mModel(0),
+    mSelectionModel(0),
+    mDefaultSelectionModel(0),
+    mScrollBar(0),
+    mAbleToScroll(false),
+    mHandleLongPress(false),
+    mBufferSize(DEFAULT_BUFFER_SIZE)
+{
+    FUNC_LOG;
+}
+
+HgWidgetPrivate::~HgWidgetPrivate()
+{
+    FUNC_LOG;
+
+    delete mDefaultSelectionModel;
+    delete mBufferManager;
+}
+
+void HgWidgetPrivate::init(HgContainer *container)
+{
+    FUNC_LOG;
+    Q_Q(HgWidget);
+
+    mScrollBarHideTimer.setParent(q);
+    mScrollBarHideTimer.setSingleShot(true);
+
+    q->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+    q->setFlag( QGraphicsItem::ItemClipsChildrenToShape, true );
+    q->setFocusPolicy(Qt::StrongFocus);
+
+    createScrollBar(container->orientation());
+    
+    QObject::connect(&(mScrollBarHideTimer), SIGNAL(timeout()), q, SLOT(_q_hideScrollBars()));
+
+    mContainer = container;
+
+    mScrollBarPolicy = HgWidget::ScrollBarAutoHide;
+
+    q->connect(mContainer, SIGNAL(scrollPositionChanged(qreal,bool)),
+               q, SLOT(_q_scrollPositionChanged(qreal,bool)));
+    q->connect(mContainer, SIGNAL(activated(const QModelIndex&)),
+               q, SIGNAL(activated(const QModelIndex&)));
+    q->connect(mContainer, SIGNAL(longPressed(const QModelIndex&, const QPointF &)),
+               q, SIGNAL(longPressed(const QModelIndex&, const QPointF &)));
+    q->connect(mContainer, SIGNAL(scrollingStarted()), q, SIGNAL(scrollingStarted()));
+    q->connect(mContainer, SIGNAL(scrollingEnded()), q, SIGNAL(scrollingEnded()));
+    
+    mIndexFeedback = new HgIndexFeedback(q);
+    mIndexFeedback->setWidget(q);
+    
+}
+
+void HgWidgetPrivate::setModel( QAbstractItemModel *model )
+{
+    FUNC_LOG;
+
+    if (model != mModel) {
+        clearCurrentModel();
+        mModel = model;
+        initializeNewModel();
+    }
+}
+
+void HgWidgetPrivate::setSelectionModel(QItemSelectionModel *selectionModel)
+{
+    FUNC_LOG;
+    
+    Q_Q(HgWidget);
+
+    if (mContainer) {
+        if (selectionModel == 0) {
+            QItemSelectionModel *oldSelectionModel = mDefaultSelectionModel;
+            mDefaultSelectionModel = 0;
+            mDefaultSelectionModel = new QItemSelectionModel(mModel);
+            mContainer->setSelectionModel(mDefaultSelectionModel);
+            delete oldSelectionModel;
+        }
+        else if (selectionModel != mContainer->selectionModel()) {
+            QItemSelectionModel *oldSelectionModel = mDefaultSelectionModel;
+            mDefaultSelectionModel = 0;
+            mContainer->setSelectionModel(selectionModel);
+            delete oldSelectionModel;
+        }
+        if (mContainer->selectionModel()) {
+            if (mIndexFeedback) {
+                delete mIndexFeedback;
+                mIndexFeedback = 0;
+            }
+            mIndexFeedback = new HgIndexFeedback(q);
+            mIndexFeedback->setWidget(q);
+        }            
+    }
+}
+
+QItemSelectionModel *HgWidgetPrivate::selectionModel() const
+{
+    FUNC_LOG;
+
+    if (mContainer) {
+        return mContainer->selectionModel();
+    }
+    return 0;
+}
+
+HgWidget::SelectionMode HgWidgetPrivate::selectionMode() const
+{
+    FUNC_LOG;
+
+    if (mContainer) {
+        return mContainer->selectionMode();
+    }
+    return HgWidget::NoSelection;
+}
+
+void HgWidgetPrivate::setSelectionMode(HgWidget::SelectionMode mode, bool resetSelection)
+{
+    FUNC_LOG;
+
+    if (mContainer) {
+        mContainer->setSelectionMode(mode, resetSelection);
+    }
+}
+
+void HgWidgetPrivate::selectAll()
+{
+    FUNC_LOG;
+    Q_Q(HgWidget);
+
+    if (mContainer &&
+        (mContainer->selectionMode() == HgWidget::MultiSelection ||
+         mContainer->selectionMode() == HgWidget::ContiguousSelection) &&
+        mModel && mModel->columnCount() > 0 && mModel->rowCount() > 0) {
+        QItemSelection selection(
+                mModel->index(0, 0),
+                mModel->index(mModel->rowCount()-1, mModel->columnCount()-1));
+        mContainer->selectionModel()->select(selection, QItemSelectionModel::Select);
+        q->update();
+    }
+}
+
+void HgWidgetPrivate::clearSelection()
+{
+    FUNC_LOG;
+    Q_Q(HgWidget);
+
+    if (mContainer) {
+        mContainer->selectionModel()->clearSelection();
+        q->update();
+    }
+}
+
+QModelIndex HgWidgetPrivate::currentIndex() const
+{
+    FUNC_LOG;
+
+    if (mContainer && mContainer->selectionModel()) {
+        return mContainer->selectionModel()->currentIndex();
+    }
+    return QModelIndex();
+}
+
+void HgWidgetPrivate::setCurrentIndex(
+    const QModelIndex &index, QItemSelectionModel::SelectionFlags selectionFlag)
+{
+    FUNC_LOG;
+
+    if (mContainer && mContainer->selectionModel()) {
+        mContainer->selectionModel()->setCurrentIndex(index, selectionFlag);
+    }
+}
+
+void HgWidgetPrivate::scrollTo(const QModelIndex &index)
+{
+    FUNC_LOG;
+
+    if (index.isValid()) {
+        if (mContainer) {
+            mContainer->scrollTo(index);
+        }
+        if (mBufferManager) {
+            mBufferManager->scrollPositionChanged(index.row());
+        }
+    }
+}
+
+void HgWidgetPrivate::initializeNewModel()
+{
+    FUNC_LOG;
+    Q_Q(HgWidget);
+
+    if (mModel) {
+        // These asserts do basic sanity checking of the model
+        Q_ASSERT_X(mModel->index(0,0) == mModel->index(0,0),
+                   "HbAbstractItemView::setModel",
+                   "A model should return the exact same index "
+                   "(including its internal id/pointer) when asked for it twice in a row.");
+        Q_ASSERT_X(mModel->index(0,0).parent() == QModelIndex(),
+                   "HbAbstractItemView::setModel",
+                   "The parent of a top level index should be invalid");
+
+        q->connect(mModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
+                           SLOT( dataChanged(QModelIndex,QModelIndex)));
+        q->connect(mModel, SIGNAL(rowsInserted(QModelIndex, int, int)),
+                           SLOT(_q_insertRows(QModelIndex, int, int)));
+        q->connect(mModel, SIGNAL(rowsRemoved(QModelIndex, int, int)),
+                           SLOT(_q_removeRows(QModelIndex, int, int)));
+        q->connect(mModel, SIGNAL(rowsMoved(QModelIndex, int, int, QModelIndex, int)),
+                           SLOT(_q_moveRows(QModelIndex, int, int, QModelIndex, int)));
+
+        mContainer->setItemCount(mModel->rowCount(QModelIndex()));
+        QList<HgWidgetItem*> items = mContainer->items();
+
+        // set model indexes for the items firsts
+        int itemCount = items.count();
+        for( int i=0; i<itemCount; i++)
+        {
+            items.at(i)->setModelIndex(mModel->index(i, 0, QModelIndex()));
+        }
+
+        if( mBufferManager )
+        {
+            delete mBufferManager;
+            mBufferManager = 0;
+        }
+
+        mBufferManager = new HgScrollBufferManager(mBufferSize,mBufferSize/4,0,itemCount);
+        q->connect( mBufferManager, SIGNAL(releaseItems(int,int)), q, SLOT(_q_releaseItems(int,int)));
+        q->connect( mBufferManager, SIGNAL(requestItems(int,int)), q, SLOT(_q_requestItems(int,int)));
+        mBufferManager->resetBuffer(0, itemCount);
+
+        setSelectionModel(0); // Default
+
+        if (mModel->rowCount() > 0)
+        {
+            setCurrentIndex(mModel->index(0, 0));
+            scrollTo(mModel->index(0, 0));
+        }
+    }
+}
+
+void HgWidgetPrivate::clearCurrentModel()
+{
+    FUNC_LOG;
+    Q_Q(HgWidget);
+
+    if (mModel) {
+        mModel->disconnect(q, SLOT(dataChanged(QModelIndex, QModelIndex)));
+        mModel->disconnect(q, SLOT(_q_insertRows(QModelIndex, int, int)));
+        mModel->disconnect(q, SLOT(_q_removeRows(QModelIndex, int, int)));
+        mModel->disconnect(q, SLOT(_q_moveRows(QModelIndex, int, int, QModelIndex, int)));
+        mModel = 0;
+    }
+
+//  TODO: setSelectionModel(0);
+
+}
+void HgWidgetPrivate::_q_releaseItems( int releaseStart, int releaseEnd )
+{
+    FUNC_LOG;
+
+    QList<HgWidgetItem*> items = mContainer->items();
+    const int itemCount = items.count();
+
+    int start = qBound(0, releaseStart, itemCount-1);
+    int end = qBound(0, releaseEnd, itemCount-1);
+
+    INFO("Release items:" << start << "-" << end);
+    // request data for items
+    for (int i = start; i <= end; i++) {
+        HgWidgetItem* item = items.at(i);
+        if (item) {
+            item->releaseItemData();
+        }
+    }
+}
+
+void HgWidgetPrivate::_q_requestItems(int requestStart, int requestEnd)
+{
+    FUNC_LOG;
+
+    QList<HgWidgetItem*> items = mContainer->items();
+    const int itemCount = items.count();
+
+    int start = qBound(0, requestStart, itemCount-1);
+    int end = qBound(0, requestEnd, itemCount-1);
+
+    // variables to track which items are really updated.
+    int firstUpdated = -1;
+    int lastUpdated = -1;
+
+    INFO("Request items:" << start << "-" << end);
+    // request data for items
+    for (int i = start; i <= end; i++) {
+        HgWidgetItem* item = items.at(i);
+        if (item && item->updateItemData()) {
+            if (firstUpdated == -1) firstUpdated = i;
+            lastUpdated = i;
+        }
+    }
+
+    // notify container which items have updated data available.
+    // container is responsible to redraw view if some of the items
+    // is visible.
+    if (firstUpdated != -1 && lastUpdated != -1) {
+        mContainer->itemDataChanged(firstUpdated, lastUpdated);
+    }
+}
+
+void HgWidgetPrivate::_q_scrollPositionChanged(qreal index,bool scrollBarAnimation)
+{
+    int newPos = index;
+    newPos *= mContainer->rowCount();
+    if (mBufferManager) {
+        mBufferManager->scrollPositionChanged(newPos);
+    }
+
+    if (!scrollBarAnimation)
+        updateScrollMetrics(index);
+}
+
+void HgWidgetPrivate::replaceScrollBar(HbScrollBar *scrollBar)
+{
+    Q_Q(HgWidget);
+    delete mScrollBar;
+    mScrollBar = scrollBar;
+
+    scrollBar->setParentItem(q);
+
+    // make sure the scrollbar is on top
+    scrollBar->setZValue(q->zValue() + 1);
+
+    prepareScrollBars();
+
+    QObject::connect(scrollBar, SIGNAL(valueChanged(qreal, Qt::Orientation)), q, SLOT(_q_thumbPositionChanged(qreal, Qt::Orientation)));
+}
+
+void HgWidgetPrivate::prepareScrollBars( qreal pos )
+{
+    Q_Q( HgWidget );
+
+    QRectF boundingRect = q->boundingRect();
+
+    bool scrollBarsVisible(false);
+
+    if ((mAbleToScroll && mScrollBarPolicy != HgWidget::ScrollBarAlwaysOff) ||
+        mScrollBarPolicy == HgWidget::ScrollBarAlwaysOn) {
+
+        displayScrollBar(pos );
+        scrollBarsVisible = true;
+    }
+    else if((!mAbleToScroll || mScrollBarPolicy == HgWidget::ScrollBarAlwaysOff)
+               && mScrollBar->isVisible()){
+            mScrollBar->setVisible(false);
+    }
+
+    if (scrollBarsVisible && !mScrollBarHideTimer.isActive()) {
+        mScrollBarHideTimer.start(INITIAL_SCROLLBAR_HIDE_TIMEOUT);
+    }
+}
+
+void HgWidgetPrivate::displayScrollBar(qreal pos)
+{
+    Q_Q(HgWidget);
+    // Layout the scrollbar
+    setScrollBarMetrics(pos);
+
+    // Activate the scrollbar
+    if ( !mScrollBar->isVisible() && q->isVisible()) {
+        mScrollBar->setVisible(true);
+    }
+}
+
+void HgWidgetPrivate::setScrollBarPolicy(HgWidget::ScrollBarPolicy policy)
+{
+    mScrollBarPolicy = policy;
+
+    if (mScrollBarPolicy == HgWidget::ScrollBarAlwaysOff &&
+        mScrollBar->isVisible()){
+        mScrollBar->setVisible(false);
+    }
+
+    if(policy != HgWidget::ScrollBarAlwaysOff){
+        updateScrollMetrics();
+    }
+}
+
+void HgWidgetPrivate::_q_hideScrollBars()
+{
+    if (mScrollBar && mScrollBarPolicy == HgWidget::ScrollBarAutoHide) {
+
+        // TODO, do we need to know if scrollbar was pressed? we cannot access the private methods, since
+        // only scrollareaprivate is a friend class.
+        if (false/*scrollBarPressed(mHorizontalScrollBar) ||
+                scrollBarPressed(mVerticalScrollBar)*/) {
+            mScrollBarHideTimer.start();
+        } else if(mScrollBarPolicy != HgWidget::ScrollBarAlwaysOn
+                  && mScrollBar->isVisible()){
+            mScrollBar->setVisible(false);
+
+        }
+    }
+}
+
+/**
+ *
+ */
+void HgWidgetPrivate::_q_thumbPositionChanged(qreal value, Qt::Orientation orientation)
+{
+    Q_UNUSED(orientation)
+
+    mContainer->scrollToPosition( value, true );
+
+    // TODO, stop all scrolling and animations
+
+    if (mScrollBarHideTimer.isActive()) {
+        mScrollBarHideTimer.stop();
+        mScrollBarHideTimer.start();
+    }
+}
+
+void HgWidgetPrivate::_q_insertRows(const QModelIndex &parent, int start, int end)
+{
+    FUNC_LOG;
+    INFO("Insert rows" << start << "-" << end);
+    Q_UNUSED(parent)
+    Q_Q(HgWidget);
+
+    if (mContainer) {
+        mContainer->addItems(start, end);
+        // re-set model indexes for the items including and after the added indexes
+        QList<HgWidgetItem *> items = mContainer->items();
+        int newItemCount = items.count();
+        for (int i = start; i < newItemCount; i++) {
+            items.at(i)->setModelIndex(mModel->index(i, 0, QModelIndex()));
+        }
+        mBufferManager->addItems(start, end, newItemCount);
+        q->update();
+    }
+}
+
+void HgWidgetPrivate::_q_removeRows(const QModelIndex &parent, int start, int end)
+{
+    FUNC_LOG;
+    INFO("Remove rows" << start << "-" << end);
+    Q_UNUSED(parent)
+    Q_Q(HgWidget);
+
+    if (mContainer && mBufferManager) {
+        mContainer->removeItems(start, end);
+        // re-set model indexes for the items after the removed indexes
+        QList<HgWidgetItem *> items = mContainer->items();
+        int newItemCount = items.count();
+        for (int i = start; i < newItemCount; i++) {
+            items.at(i)->setModelIndex(mModel->index(i, 0, QModelIndex()));
+        }
+        mBufferManager->removeItems(start, end, newItemCount);
+        q->update();
+    }
+}
+
+void HgWidgetPrivate::_q_moveRows(const QModelIndex &sourceParent,
+    int sourceStart, int sourceEnd,
+    const QModelIndex &destinationParent, int destinationRow)
+{
+    FUNC_LOG;
+    INFO("Move rows" << sourceStart << "-" << sourceEnd << "to" << destinationRow);
+    Q_UNUSED(sourceParent)
+    Q_UNUSED(destinationParent)
+    Q_Q(HgWidget);
+
+    if (mContainer) {
+        mContainer->moveItems(sourceStart, sourceEnd, destinationRow);
+        // re-set model indexes for the items after the removed indexes
+        QList<HgWidgetItem *> items = mContainer->items();
+        int itemCount = items.count();
+        for (int i = qMin(sourceStart, destinationRow); i < itemCount; i++) {
+            items.at(i)->setModelIndex(mModel->index(i, 0, QModelIndex()));
+        }
+        mBufferManager->moveItems(sourceStart, sourceEnd, destinationRow, itemCount);
+        q->update();
+    }
+}
+
+void HgWidgetPrivate::setScrollBarMetrics(qreal pos)
+{
+    Q_Q( HgWidget );
+
+    if (!mContainer)
+        return;
+
+    qreal screenSize, worldSize;
+    mContainer->dimensions(screenSize,worldSize);
+
+    const qreal start(0.0);
+    const qreal end(1.0);
+    const qreal page(screenSize/worldSize);
+
+    // Set handle size
+    mScrollBar->setPageSize( qBound(start,page,end ) );
+
+    updateScrollBar(pos);
+}
+
+void HgWidgetPrivate::updateScrollBar(qreal pos)
+{
+    Q_Q( HgWidget );
+
+    if (pos < 0.0)
+        pos = 0.0;
+    else if (pos > 1.0)
+        pos = 1.0;
+        // The scrollbar "thumb" position is the current position of the contents widget divided
+        // by the difference between the height of the contents widget and the height of the scroll area.
+        // This formula assumes that the "thumb" of the the scroll bar is sized proportionately to
+        // the height of the contents widget.
+    mScrollBar->setValue(pos);
+}
+
+/*
+ * updateScrollMetrics() is a private function called when
+ * scrolling starts to set the metrics needed in scrolling.
+ */
+void HgWidgetPrivate::updateScrollMetrics( qreal pos)
+{
+    Q_Q(HgWidget);
+
+    QRectF scrollAreaBoundingRect = q->boundingRect();
+
+    if (!mContainer || scrollAreaBoundingRect.isNull() ||
+         !scrollAreaBoundingRect.isValid())
+        return;
+
+    qreal screenSize, worldSize;
+    mContainer->dimensions(screenSize,worldSize);
+
+    mAbleToScroll = false;
+    if (worldSize > screenSize) {
+        mAbleToScroll = true;
+    }
+
+    prepareScrollBars( pos/worldSize );
+}
+
+void HgWidgetPrivate::adjustGeometry()
+{
+    Q_Q(HgWidget);
+    
+    QRectF scrollAreaBoundingRect = q->boundingRect();
+    if( scrollAreaBoundingRect.isNull() ||
+            !scrollAreaBoundingRect.isValid() ||
+            !mContainer ||
+            scrollAreaBoundingRect == mContainer->boundingRect() )
+        return;
+
+    mContainer->resize(scrollAreaBoundingRect.size());
+
+    updateScrollMetrics(0);
+}
+
+
+void HgWidgetPrivate::lostForeground()
+{
+    if( !mForeground ) return;
+    
+    mForeground = false;
+    QList<HgWidgetItem*> list = mContainer->items();
+    foreach(HgWidgetItem* item, list){
+        item->releaseItemData();
+    }
+}
+
+void HgWidgetPrivate::gainedForeground()
+{
+    if( mForeground ) return;
+    
+    mForeground = true;
+    QList<HgWidgetItem*> list = mContainer->items();
+    int bufferStart = 0;
+    int bufferEnd = 0;
+    mBufferManager->currentBuffer(bufferStart,bufferEnd);
+    for(;bufferStart<=bufferEnd;bufferStart++){
+        list.at(bufferStart)->updateItemData();
+    }
+}
+
+bool HgWidgetPrivate::getItemOutline(const QModelIndex& index, QPolygonF& points)
+{
+    return mContainer->getItemPoints(index.row(), points);
+}
+
+void HgWidgetPrivate::aboutToChangeOrientation()
+{
+
+}
+
+void HgWidgetPrivate::orientationChanged(Qt::Orientation orientation)
+{
+    Q_Q(HgWidget);
+    if (mContainer->orientation() != orientation) {
+        createScrollBar(orientation);
+        q->repolish();
+        mContainer->setOrientation(orientation);
+        adjustGeometry();
+    }
+}
+
+void HgWidgetPrivate::_q_groovePressed(qreal value, Qt::Orientation orientation)
+{
+    Q_UNUSED(value);
+    Q_UNUSED(orientation);
+}
+
+Qt::Orientation HgWidgetPrivate::scrollDirection() const
+{
+    return mContainer->orientation();
+}
+
+void HgWidgetPrivate::createScrollBar(Qt::Orientation orientation)
+{
+    Q_Q(HgWidget);
+    
+    delete mScrollBar;
+    mScrollBar = 0;
+    mScrollBar = new HbScrollBar(orientation,q);
+    if (orientation == Qt::Vertical) {
+        HbStyle::setItemName(mScrollBar, "scrollbar-vertical");
+    }
+    else {
+        HbStyle::setItemName(mScrollBar, "scrollbar-horizontal");    
+    }
+
+    mScrollBar->setZValue(q->zValue() + 1);
+    QObject::connect(mScrollBar, SIGNAL(valueChanged(qreal, Qt::Orientation)),
+                     q, SLOT(_q_thumbPositionChanged(qreal, Qt::Orientation)));
+    QObject::connect(mScrollBar, SIGNAL(valueChangeRequested(qreal, Qt::Orientation)),
+                     q, SLOT(_q_groovePressed(qreal, Qt::Orientation)));
+    mScrollBar->setVisible(false);
+}
+
+QList<QModelIndex> HgWidgetPrivate::getVisibleItemIndices() const
+{
+    return mContainer->getVisibleItemIndices();
+}
+
+void HgWidgetPrivate::setIndexFeedbackPolicy( HgWidget::IndexFeedbackPolicy policy)
+{
+    mIndexFeedback->setIndexFeedbackPolicy(policy);
+}
+
+HgWidget::IndexFeedbackPolicy HgWidgetPrivate::indexFeedbackPolicy() const
+{
+    return mIndexFeedback->indexFeedbackPolicy();
+}
+
+#include "moc_hgwidgets.cpp"