/**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtMultimedia module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the Technology Preview License Agreement accompanying ** this package. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** ** ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include "qmediaobject_p.h" #include QT_BEGIN_NAMESPACE class QMediaPlaylistNullProvider : public QMediaPlaylistProvider { public: QMediaPlaylistNullProvider() :QMediaPlaylistProvider() {} virtual ~QMediaPlaylistNullProvider() {} virtual int mediaCount() const {return 0;} virtual QMediaContent media(int) const { return QMediaContent(); } }; Q_GLOBAL_STATIC(QMediaPlaylistNullProvider, _q_nullMediaPlaylist) class QMediaPlaylistNavigatorPrivate { Q_DECLARE_NON_CONST_PUBLIC(QMediaPlaylistNavigator) public: QMediaPlaylistNavigatorPrivate() :playlist(0), currentPos(-1), lastValidPos(-1), playbackMode(QMediaPlaylist::Linear), randomPositionsOffset(-1) { } QMediaPlaylistProvider *playlist; int currentPos; int lastValidPos; //to be used with CurrentItemOnce playback mode QMediaPlaylist::PlaybackMode playbackMode; QMediaContent currentItem; mutable QList randomModePositions; mutable int randomPositionsOffset; int nextItemPos(int steps = 1) const; int previousItemPos(int steps = 1) const; void _q_mediaInserted(int start, int end); void _q_mediaRemoved(int start, int end); void _q_mediaChanged(int start, int end); QMediaPlaylistNavigator *q_ptr; }; int QMediaPlaylistNavigatorPrivate::nextItemPos(int steps) const { if (playlist->mediaCount() == 0) return -1; if (steps == 0) return currentPos; switch (playbackMode) { case QMediaPlaylist::CurrentItemOnce: return /*currentPos == -1 ? lastValidPos :*/ -1; case QMediaPlaylist::CurrentItemInLoop: return currentPos; case QMediaPlaylist::Linear: { int nextPos = currentPos+steps; return nextPos < playlist->mediaCount() ? nextPos : -1; } case QMediaPlaylist::Loop: return (currentPos+steps) % playlist->mediaCount(); case QMediaPlaylist::Random: { //TODO: limit the history size if (randomPositionsOffset == -1) { randomModePositions.clear(); randomModePositions.append(currentPos); randomPositionsOffset = 0; } while (randomModePositions.size() < randomPositionsOffset+steps+1) randomModePositions.append(-1); int res = randomModePositions[randomPositionsOffset+steps]; if (res<0 || res >= playlist->mediaCount()) { res = qrand() % playlist->mediaCount(); randomModePositions[randomPositionsOffset+steps] = res; } return res; } } return -1; } int QMediaPlaylistNavigatorPrivate::previousItemPos(int steps) const { if (playlist->mediaCount() == 0) return -1; if (steps == 0) return currentPos; switch (playbackMode) { case QMediaPlaylist::CurrentItemOnce: return /*currentPos == -1 ? lastValidPos :*/ -1; case QMediaPlaylist::CurrentItemInLoop: return currentPos; case QMediaPlaylist::Linear: { int prevPos = currentPos == -1 ? playlist->mediaCount() - steps : currentPos - steps; return prevPos>=0 ? prevPos : -1; } case QMediaPlaylist::Loop: { int prevPos = currentPos - steps; while (prevPos<0) prevPos += playlist->mediaCount(); return prevPos; } case QMediaPlaylist::Random: { //TODO: limit the history size if (randomPositionsOffset == -1) { randomModePositions.clear(); randomModePositions.append(currentPos); randomPositionsOffset = 0; } while (randomPositionsOffset-steps < 0) { randomModePositions.prepend(-1); randomPositionsOffset++; } int res = randomModePositions[randomPositionsOffset-steps]; if (res<0 || res >= playlist->mediaCount()) { res = qrand() % playlist->mediaCount(); randomModePositions[randomPositionsOffset-steps] = res; } return res; } } return -1; } /*! \class QMediaPlaylistNavigator \preliminary \brief The QMediaPlaylistNavigator class provides navigation for a media playlist. \sa QMediaPlaylist, QMediaPlaylistProvider */ /*! Constructs a media playlist navigator for a \a playlist. The \a parent is passed to QObject. */ QMediaPlaylistNavigator::QMediaPlaylistNavigator(QMediaPlaylistProvider *playlist, QObject *parent) : QObject(parent) , d_ptr(new QMediaPlaylistNavigatorPrivate) { d_ptr->q_ptr = this; setPlaylist(playlist ? playlist : _q_nullMediaPlaylist()); } /*! Destroys a media playlist navigator. */ QMediaPlaylistNavigator::~QMediaPlaylistNavigator() { delete d_ptr; } /*! \property QMediaPlaylistNavigator::playbackMode Contains the playback mode. */ QMediaPlaylist::PlaybackMode QMediaPlaylistNavigator::playbackMode() const { return d_func()->playbackMode; } /*! Sets the playback \a mode. */ void QMediaPlaylistNavigator::setPlaybackMode(QMediaPlaylist::PlaybackMode mode) { Q_D(QMediaPlaylistNavigator); if (d->playbackMode == mode) return; if (mode == QMediaPlaylist::Random) { d->randomPositionsOffset = 0; d->randomModePositions.append(d->currentPos); } else if (d->playbackMode == QMediaPlaylist::Random) { d->randomPositionsOffset = -1; d->randomModePositions.clear(); } d->playbackMode = mode; emit playbackModeChanged(mode); emit surroundingItemsChanged(); } /*! Returns the playlist being navigated. */ QMediaPlaylistProvider *QMediaPlaylistNavigator::playlist() const { return d_func()->playlist; } /*! Sets the \a playlist to navigate. */ void QMediaPlaylistNavigator::setPlaylist(QMediaPlaylistProvider *playlist) { Q_D(QMediaPlaylistNavigator); if (d->playlist == playlist) return; if (d->playlist) { d->playlist->disconnect(this); } if (playlist) { d->playlist = playlist; } else { //assign to shared readonly null playlist d->playlist = _q_nullMediaPlaylist(); } connect(d->playlist, SIGNAL(mediaInserted(int,int)), SLOT(_q_mediaInserted(int,int))); connect(d->playlist, SIGNAL(mediaRemoved(int,int)), SLOT(_q_mediaRemoved(int,int))); connect(d->playlist, SIGNAL(mediaChanged(int,int)), SLOT(_q_mediaChanged(int,int))); d->randomPositionsOffset = -1; d->randomModePositions.clear(); if (d->currentPos != -1) { d->currentPos = -1; emit currentIndexChanged(-1); } if (!d->currentItem.isNull()) { d->currentItem = QMediaContent(); emit activated(d->currentItem); //stop playback } } /*! \property QMediaPlaylistNavigator::currentItem Contains the media at the current position in the playlist. \sa currentIndex() */ QMediaContent QMediaPlaylistNavigator::currentItem() const { return itemAt(d_func()->currentPos); } /*! \fn QMediaContent QMediaPlaylistNavigator::nextItem(int steps) const Returns the media that is \a steps positions ahead of the current position in the playlist. \sa nextIndex() */ QMediaContent QMediaPlaylistNavigator::nextItem(int steps) const { return itemAt(nextIndex(steps)); } /*! Returns the media that is \a steps positions behind the current position in the playlist. \sa previousIndex() */ QMediaContent QMediaPlaylistNavigator::previousItem(int steps) const { return itemAt(previousIndex(steps)); } /*! Returns the media at a \a position in the playlist. */ QMediaContent QMediaPlaylistNavigator::itemAt(int position) const { return d_func()->playlist->media(position); } /*! \property QMediaPlaylistNavigator::currentIndex Contains the position of the current media. If no media is current, the property contains -1. \sa nextIndex(), previousIndex() */ int QMediaPlaylistNavigator::currentIndex() const { return d_func()->currentPos; } /*! Returns a position \a steps ahead of the current position accounting for the playbackMode(). If the position is beyond the end of the playlist, this value returned is -1. \sa currentIndex(), previousIndex(), playbackMode() */ int QMediaPlaylistNavigator::nextIndex(int steps) const { return d_func()->nextItemPos(steps); } /*! Returns a position \a steps behind the current position accounting for the playbackMode(). If the position is prior to the beginning of the playlist this will return -1. \sa currentIndex(), nextIndex(), playbackMode() */ int QMediaPlaylistNavigator::previousIndex(int steps) const { return d_func()->previousItemPos(steps); } /*! Advances to the next item in the playlist. \sa previous(), jump(), playbackMode() */ void QMediaPlaylistNavigator::next() { Q_D(QMediaPlaylistNavigator); int nextPos = d->nextItemPos(); if ( playbackMode() == QMediaPlaylist::Random ) d->randomPositionsOffset++; jump(nextPos); } /*! Returns to the previous item in the playlist, \sa next(), jump(), playbackMode() */ void QMediaPlaylistNavigator::previous() { Q_D(QMediaPlaylistNavigator); int prevPos = d->previousItemPos(); if ( playbackMode() == QMediaPlaylist::Random ) d->randomPositionsOffset--; jump(prevPos); } /*! Jumps to a new \a position in the playlist. */ void QMediaPlaylistNavigator::jump(int position) { Q_D(QMediaPlaylistNavigator); if (position<-1 || position>=d->playlist->mediaCount()) { qWarning() << "QMediaPlaylistNavigator: Jump outside playlist range"; position = -1; } if (position != -1) d->lastValidPos = position; if (playbackMode() == QMediaPlaylist::Random) { if (d->randomModePositions[d->randomPositionsOffset] != position) { d->randomModePositions.clear(); d->randomModePositions.append(position); d->randomPositionsOffset = 0; } } if (position != -1) d->currentItem = d->playlist->media(position); else d->currentItem = QMediaContent(); if (position != d->currentPos) { d->currentPos = position; emit currentIndexChanged(d->currentPos); emit surroundingItemsChanged(); } emit activated(d->currentItem); } /*! \internal */ void QMediaPlaylistNavigatorPrivate::_q_mediaInserted(int start, int end) { Q_Q(QMediaPlaylistNavigator); if (currentPos >= start) { currentPos = end-start+1; q->jump(currentPos); } //TODO: check if they really changed emit q->surroundingItemsChanged(); } /*! \internal */ void QMediaPlaylistNavigatorPrivate::_q_mediaRemoved(int start, int end) { Q_Q(QMediaPlaylistNavigator); if (currentPos > end) { currentPos = currentPos - end-start+1; q->jump(currentPos); } else if (currentPos >= start) { //current item was removed currentPos = qMin(start, playlist->mediaCount()-1); q->jump(currentPos); } //TODO: check if they really changed emit q->surroundingItemsChanged(); } /*! \internal */ void QMediaPlaylistNavigatorPrivate::_q_mediaChanged(int start, int end) { Q_Q(QMediaPlaylistNavigator); if (currentPos >= start && currentPos<=end) { QMediaContent src = playlist->media(currentPos); if (src != currentItem) { currentItem = src; emit q->activated(src); } } //TODO: check if they really changed emit q->surroundingItemsChanged(); } /*! \fn QMediaPlaylistNavigator::activated(const QMediaContent &media) Signals that the current \a media has changed. */ /*! \fn QMediaPlaylistNavigator::currentIndexChanged(int position) Signals the \a position of the current media has changed. */ /*! \fn QMediaPlaylistNavigator::playbackModeChanged(QMediaPlaylist::PlaybackMode mode) Signals that the playback \a mode has changed. */ /*! \fn QMediaPlaylistNavigator::surroundingItemsChanged() Signals that media immediately surrounding the current position has changed. */ #include "moc_qmediaplaylistnavigator.cpp" QT_END_NAMESPACE