summaryrefslogtreecommitdiffstats
path: root/src/multimediakit/qmediaplaylistnavigator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/multimediakit/qmediaplaylistnavigator.cpp')
-rw-r--r--src/multimediakit/qmediaplaylistnavigator.cpp568
1 files changed, 568 insertions, 0 deletions
diff --git a/src/multimediakit/qmediaplaylistnavigator.cpp b/src/multimediakit/qmediaplaylistnavigator.cpp
new file mode 100644
index 000000000..c3e358716
--- /dev/null
+++ b/src/multimediakit/qmediaplaylistnavigator.cpp
@@ -0,0 +1,568 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmediaplaylistnavigator.h"
+#include "qmediaplaylistprovider.h"
+#include "qmediaplaylist.h"
+#include "qmediaobject_p.h"
+
+#include <QtCore/qdebug.h>
+
+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::Sequential),
+ randomPositionsOffset(-1)
+ {
+ }
+
+ QMediaPlaylistProvider *playlist;
+ int currentPos;
+ int lastValidPos; //to be used with CurrentItemOnce playback mode
+ QMediaPlaylist::PlaybackMode playbackMode;
+ QMediaContent currentItem;
+
+ mutable QList<int> 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::Sequential:
+ {
+ 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::Sequential:
+ {
+ 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
+
+ \brief The QMediaPlaylistNavigator class provides navigation for a media playlist.
+ \inmodule QtMultimediaKit
+ \ingroup multimedia
+ \since 1.0
+
+ \sa QMediaPlaylist, QMediaPlaylistProvider
+*/
+
+
+/*!
+ Constructs a media playlist navigator for a \a playlist.
+
+ The \a parent is passed to QObject.
+ \since 1.0
+ */
+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.
+ \since 1.0
+ */
+QMediaPlaylist::PlaybackMode QMediaPlaylistNavigator::playbackMode() const
+{
+ return d_func()->playbackMode;
+}
+
+/*!
+ Sets the playback \a mode.
+ \since 1.0
+ */
+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.
+ \since 1.0
+*/
+
+QMediaPlaylistProvider *QMediaPlaylistNavigator::playlist() const
+{
+ return d_func()->playlist;
+}
+
+/*!
+ Sets the \a playlist to navigate.
+ \since 1.0
+*/
+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.
+
+ \since 1.0
+ \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.
+
+ \since 1.0
+ \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.
+
+ \since 1.0
+ \sa previousIndex()
+ */
+QMediaContent QMediaPlaylistNavigator::previousItem(int steps) const
+{
+ return itemAt(previousIndex(steps));
+}
+
+/*!
+ Returns the media at a \a position in the playlist.
+ \since 1.0
+ */
+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.
+
+ \since 1.0
+ \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.
+
+ \since 1.0
+ \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.
+
+ \since 1.0
+ \sa currentIndex(), nextIndex(), playbackMode()
+*/
+int QMediaPlaylistNavigator::previousIndex(int steps) const
+{
+ return d_func()->previousItemPos(steps);
+}
+
+/*!
+ Advances to the next item in the playlist.
+
+ \since 1.0
+ \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,
+
+ \since 1.0
+ \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.
+ \since 1.0
+ */
+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
+ \since 1.0
+*/
+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
+ \since 1.0
+*/
+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
+ \since 1.0
+*/
+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.
+ \since 1.0
+*/
+
+/*!
+ \fn QMediaPlaylistNavigator::currentIndexChanged(int position)
+
+ Signals the \a position of the current media has changed.
+ \since 1.0
+*/
+
+/*!
+ \fn QMediaPlaylistNavigator::playbackModeChanged(QMediaPlaylist::PlaybackMode mode)
+
+ Signals that the playback \a mode has changed.
+ \since 1.0
+*/
+
+/*!
+ \fn QMediaPlaylistNavigator::surroundingItemsChanged()
+
+ Signals that media immediately surrounding the current position has changed.
+ \since 1.0
+*/
+
+#include "moc_qmediaplaylistnavigator.cpp"
+QT_END_NAMESPACE
+