diff -r f3d95d9c00ab -r 46974bebc798 radioapp/radiowidgets/src/radiostripbase.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/radioapp/radiowidgets/src/radiostripbase.cpp Fri Mar 19 09:29:04 2010 +0200 @@ -0,0 +1,543 @@ +/* +* Copyright (c) 2009 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: +* +*/ + +// System includes +#include + +// User includes +#include "radiostripbase.h" +#include "radiologger.h" + +// Constants + +/*! + * + */ +RadioStripBase::RadioStripBase( QGraphicsItem* parent ) : + HbScrollArea( parent ), + mAutoScrollTime( 0 ), + mStripContainer( new HbWidget( this ) ), + mModel( 0 ), + mIsCyclic( true ), + mAutoCenter( false ), + mSpacing( 0 ), + mItemPoolParent( new QGraphicsWidget( NULL ) ), + mCurrentIndex( 0 ), + mPressedIndex( 0 ), + mStripLength( 0 ), + mContentsLength( 0 ), + mBackgroundImage( 0 ) +{ + setClampingStyle( HbScrollArea::NoClamping ); + setScrollDirections( Qt::Horizontal ); + setFlag( QGraphicsItem::ItemClipsChildrenToShape, true ); + setContentWidget( mStripContainer ); + setFrictionEnabled( true ); + setLongPressEnabled( false ); + setHorizontalScrollBarPolicy( HbScrollArea::ScrollBarAlwaysOff ); + setVerticalScrollBarPolicy( HbScrollArea::ScrollBarAlwaysOff ); + + // mItemParent is used to hold the unused QGraphicsItem's in the pool. It's visibility is set to false + // so the visibility of the items doesn't need to be modified. + mItemPoolParent->setVisible( false ); + + connectAndTest( this, SIGNAL(scrollPositionChanged(QPointF)), + this, SLOT(scrollPositionChanged(QPointF))); +} + +/*! + * + */ +RadioStripBase::~RadioStripBase() +{ +} + +/*! + * + */ +void RadioStripBase::setBackground( const HbIcon& background ) +{ + // Create background image for the entire widget + mBackground = background; + if ( !mBackgroundImage ) { + mBackgroundImage = new QGraphicsPixmapItem( this ); + } + mBackgroundImage->setPixmap( mBackground.pixmap() ); + mBackgroundImage->setOffset( QPointF( 0.0, 0.0 ) ); + mBackgroundImage->setZValue( -10.0 ); +} + +/*! + * + */ +HbIcon RadioStripBase::background() const +{ + return mBackground; +} + +/*! + * + */ +void RadioStripBase::setAutoScrollTime( const int time ) +{ + mAutoScrollTime = time; +} + +/*! + * + */ +int RadioStripBase::autoScrollTime() const +{ + return mAutoScrollTime; +} + +/*! + * + */ +void RadioStripBase::setModel( QAbstractItemModel* model ) +{ + if ( mModel != model ) + { + // if ( mModel ) + // { + // disconnectDataModel(); + // } + + mModel = model; + + if ( mModel ) + { + // connectDataModel(); + + mCurrentIndex = 0; + populateAndLayout(); + } + } +} + +/*! + * + */ +QAbstractItemModel* RadioStripBase::model() const +{ + return mModel; +} + +/*! + * + */ +void RadioStripBase::setCyclic( bool isCyclic ) +{ + mIsCyclic = isCyclic; +} + +/*! + * + */ +void RadioStripBase::setSpacing( qreal spacing ) +{ + if ( mSpacing != spacing ) + { + mSpacing = spacing; + + prepareGeometryChange(); + + populateAndLayout(); + + update(); + updateGeometry(); + } +} + +/*! + * + */ +void RadioStripBase::setAutoCenter( bool autoCenter ) +{ + mAutoCenter = autoCenter; +} + +/*! + * + */ +void RadioStripBase::setItemSize( const QSizeF& size ) +{ + if ( mItemSize != size ) { + mItemSize = size; + + prepareGeometryChange(); + + populateAndLayout(); + + update(); + updateGeometry(); + } +} + +/*! + * + */ +void RadioStripBase::setIndex( int index, bool animateToCenter ) +{ + Q_UNUSED( animateToCenter ) + // Sanity checks + if ( !mModel || ( !mIsCyclic && ( index < 0 || index >= mModel->rowCount() ) ) ) { + return; + } + + const int oldIndex = mCurrentIndex; + if ( mIsCyclic ) + { + int numRows = mModel->rowCount(); + index = (index + numRows) % numRows; + } + Q_ASSERT( index >= 0 ); + + mCurrentIndex = index; + + updateItemWithIndex( mCurrentIndex ); + updateItemWithIndex( oldIndex ); +} + +/*! + * \reimp + */ +void RadioStripBase::resizeEvent( QGraphicsSceneResizeEvent* event ) +{ + QPixmap background = mBackgroundImage->pixmap().scaledToWidth( event->newSize().width() ); + mBackgroundImage->setPixmap( background ); + populateAndLayout(); +} + +/*! + * \reimp + */ +void RadioStripBase::mousePressEvent( QGraphicsSceneMouseEvent* event ) +{ + if ( event->button() != Qt::LeftButton || !mModel || !mModel->rowCount() ) + { + event->ignore(); + return; + } + + HbScrollArea::mousePressEvent( event ); +} + +/*! + * \reimp + */ +void RadioStripBase::mouseReleaseEvent( QGraphicsSceneMouseEvent* event ) +{ + if ( event->button() != Qt::LeftButton ) + { + event->ignore(); + return; + } + + HbScrollArea::mouseReleaseEvent( event ); +} + +/*! + * Private slot + */ +void RadioStripBase::scrollPositionChanged( QPointF newPosition ) +{ + adjustItems(); + scrollPosChanged( newPosition ); +} + +/*! + * + */ +void RadioStripBase::scrollPosChanged( QPointF newPosition ) +{ + Q_UNUSED( newPosition ); +} + +/*! + * + */ +void RadioStripBase::moveAllItemsToPool() +{ + // set parent of all items to pool + foreach( QGraphicsItem* item, mItemAtSlot ) + { + item->setParentItem( mItemPoolParent ); + } + + // move all items to pool + mItemPool += mItemAtSlot; + mItemAtSlot.clear(); + mIndexAtSlot.clear(); +} + +/*! + * + */ +void RadioStripBase::populateAndLayout() +{ + moveAllItemsToPool(); + + if ( !mModel || mModel->rowCount() == 0 ) + { + return; + } + + mStripLength = boundingRect().width(); + qreal itemSize = mItemSize.width(); + mContentsLength = mModel->rowCount() * (itemSize + mSpacing) + mSpacing; + + if ( mIsCyclic ) + { + // if treating the items cyclically, double the content area so it can + // be shifted back and forth as you scroll + mContentsLength = mModel->rowCount() * (itemSize + mSpacing); + mContentsLength *= 2.0; + } + + qreal currPos = mSpacing; + for ( int i = 0; i < mModel->rowCount(); ++i ) { + if ( currPos > mStripLength ) + { + break; + } + + QGraphicsItem* item = constructItem( i, true ); + if ( item ) + { + item->setPos( QPointF( currPos, mSpacing ) ); + currPos += itemSize + mSpacing; + } + } + + QRectF contentsRect(0,0,0,0); + contentsRect.setBottom( itemSize + 2 * mSpacing ); + contentsRect.setRight( mContentsLength ); + + mStripContainer->setGeometry( contentsRect ); + + if ( mCurrentIndex >= 0 ) + { + setIndex( mCurrentIndex, false ); + } +} + +/*! + * + */ +QGraphicsItem* RadioStripBase::constructItem( int index, bool append ) +{ + QGraphicsItem* item = getFromPool(); + + if ( mIsCyclic ) + { + Q_ASSERT( index >= 0 && index < 2 * mModel->rowCount() ); + + updateItemPrimitive( item, index % mModel->rowCount() ); + } + else + { + Q_ASSERT( index >= 0 && index < mModel->rowCount() ); + + updateItemPrimitive( item, index ); + } + + item->setParentItem( mStripContainer ); + + if ( append ) + { + mItemAtSlot.append( item ); + mIndexAtSlot.append( index ); + } + else + { + mItemAtSlot.prepend( item ); + mIndexAtSlot.prepend( index ); + } + + return item; +} + +/*! + * + */ +QGraphicsItem* RadioStripBase::getFromPool() +{ + QGraphicsItem* item = 0; + + if ( mItemPool.isEmpty() ) + { + item = createItemPrimitive( this ); + } + else + { + item = mItemPool.takeFirst(); + } + + return item; +} + +/*! + * + */ +void RadioStripBase::returnToPool( QGraphicsItem* item ) +{ + // Unparent the item so it doesn't get deleted + item->setParentItem( mItemPoolParent ); + mItemPool.append( item ); +} + +/*! + * Returns starting coordinate of the item with the specified index + */ +qreal RadioStripBase::indexToOffset( int index ) +{ + return index * ( mItemSize.width() + mSpacing ) + mSpacing; +} + +/*! + * Returns item index for specified offset amount into the content + */ +int RadioStripBase::offsetToIndex( qreal offset ) +{ + const int rows = mModel->rowCount(); + int index = (int)( ( offset - mSpacing) / ( mItemSize.width() + mSpacing ) ); + + if ( mIsCyclic ) + { + return qBound( 0, index, 2 * rows - 1 ); + } + + return qBound( 0, index, rows - 1 ); +} + +/*! + * updates items with specified index value + */ +void RadioStripBase::updateItemWithIndex( int index ) +{ + if( index >= 0 ) + { + QList::const_iterator item = mItemAtSlot.constBegin(); + QList::const_iterator itemsEnd = mItemAtSlot.constEnd(); + QList::const_iterator itemIndex = mIndexAtSlot.constBegin(); + const int rowCount = mModel->rowCount(); + + // Find all items with this index (can be 2 in special cycling case) + for( ; item != itemsEnd; ++item, ++itemIndex ) + { + if( index == *itemIndex || index == *itemIndex - rowCount ) + { + // update those items + updateItemPrimitive( *item, index ); + } + } + } +} + +/*! + * Updates items during scrolling: removing invisible items and adding items that became visible + */ +void RadioStripBase::adjustItems() +{ + qreal contentPos = mStripContainer->pos().x(); + + if ( mIsCyclic ) + { + if ( -contentPos < 0 ) + { + // trying to display off the left end of the strip, so + // shift the strip one length to the left + contentPos -= mContentsLength * 0.5; + mStripContainer->setPos( QPointF ( contentPos, mStripContainer->pos().y() ) ); + } else if (-contentPos > mContentsLength * 0.5) { + // trying to display off the right end of the strip, so + // shift the strip one length to the right + contentPos += mContentsLength * 0.5; + mStripContainer->setPos( QPointF ( contentPos, mStripContainer->pos().y() ) ); + } + } + + // find the first and last indices of the visible items + int firstVisibleIndex = offsetToIndex( -contentPos ); + int lastVisibleIndex = offsetToIndex( -contentPos + mStripLength ); + + // remove items at the start that are no longer visible + while ( !mIndexAtSlot.isEmpty() ) + { + int firstSlotIndex = mIndexAtSlot.first(); + if ( firstVisibleIndex <= firstSlotIndex ) + { + break; + } + + returnToPool( mItemAtSlot.first() ); + mItemAtSlot.removeFirst(); + mIndexAtSlot.removeFirst(); + } + + // remove items at the end that are no longer visible + while ( !mIndexAtSlot.isEmpty() ) + { + int lastSlotIndex = mIndexAtSlot.last(); + if ( lastVisibleIndex >= lastSlotIndex ) + { + break; + } + + returnToPool( mItemAtSlot.last() ); + mItemAtSlot.removeLast(); + mIndexAtSlot.removeLast(); + } + + if ( mItemAtSlot.isEmpty() ) + { + // fill area with all needed items + for ( int i = firstVisibleIndex; i <= lastVisibleIndex; ++i ) + { + QGraphicsItem* item = constructItem( i, true ); + if ( item ) + { + item->setPos( QPointF( indexToOffset( i ), mSpacing ) ); + } + } + } + else + { + // add missing items at the front + int firstItemToCreate = mIndexAtSlot.first()-1; + for ( int i = firstItemToCreate; i >= firstVisibleIndex; --i ) + { + QGraphicsItem* item = constructItem( i, false ); + if ( item ) + { + item->setPos( QPointF( indexToOffset( i ), mSpacing ) ); + } + } + + // add missing items at the end + firstItemToCreate = mIndexAtSlot.last()+1; + for ( int i = firstItemToCreate; i <= lastVisibleIndex; ++i ) + { + QGraphicsItem* item = constructItem( i, true ); + if ( item ) + { + item->setPos( QPointF( indexToOffset( i ), mSpacing ) ); + } + } + } +}