diff -r 000000000000 -r 89c329efa980 ganeswidgets/src/hgwidgets_p.cpp --- /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 +#include +#include +#include + +#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 items = mContainer->items(); + + // set model indexes for the items firsts + int itemCount = items.count(); + for( int i=0; isetModelIndex(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 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 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 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 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 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 list = mContainer->items(); + foreach(HgWidgetItem* item, list){ + item->releaseItemData(); + } +} + +void HgWidgetPrivate::gainedForeground() +{ + if( mForeground ) return; + + mForeground = true; + QList 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 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"