summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--coverdatabase.cpp112
-rw-r--r--coverdatabase.h62
-rw-r--r--qtplaylist.cpp51
-rw-r--r--qtplaylist.h30
-rw-r--r--qtspotify.pro8
-rw-r--r--qtspotify.ui243
-rw-r--r--qtspotifymain.cpp332
-rw-r--r--qtspotifymain.h3
-rw-r--r--storedplaylistmodel.cpp68
-rw-r--r--storedplaylistmodel.h51
10 files changed, 712 insertions, 248 deletions
diff --git a/coverdatabase.cpp b/coverdatabase.cpp
new file mode 100644
index 0000000..89a1d60
--- /dev/null
+++ b/coverdatabase.cpp
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** $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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ Logic paraphrased from Andreas Kling's repository at
+
+ http://qt.gitorious.org/~andreaskling/qt-labs/andreasklings-qtspotify
+*/
+
+#include "coverdatabase.h"
+#include "despotify_cpp.h"
+
+#include <QtCore/QFuture>
+#include <QtCore/QtConcurrentRun>
+#include <QtCore/QFutureWatcher>
+#include <QtGui/QPixmap>
+
+CoverDatabase::CoverDatabase(despotify_session *session, QObject *parent)
+ : QObject(parent), m_session(session)
+{
+}
+
+CoverDatabase::~CoverDatabase()
+{
+}
+
+void CoverDatabase::loadCover(const QByteArray &coverId)
+{
+ Q_ASSERT(m_session != 0);
+
+ m_currentCoverId = coverId;
+ if (m_pendingCovers.contains(coverId))
+ return;
+
+ {
+ QMutexLocker locker(&m_coverCacheLock);
+ QImage img = m_coverCache.value(coverId);
+
+ if (!img.isNull()) {
+ emit coverLoaded(QPixmap::fromImage(img));
+ return;
+ }
+ }
+
+ QFuture<QByteArray> imageDataFetcher = QtConcurrent::run(this, &CoverDatabase::fetchImageData,
+ coverId);
+ QFutureWatcher<QByteArray> *imageDataFetcherWatcher = new QFutureWatcher<QByteArray>(this);
+ connect(imageDataFetcherWatcher, SIGNAL(finished()), this, SLOT(imageDataFetched()));
+ imageDataFetcherWatcher->setFuture(imageDataFetcher);
+}
+
+QByteArray CoverDatabase::fetchImageData(const QByteArray &coverId)
+{
+ Q_ASSERT(m_session != 0);
+
+ void *data;
+ int len;
+
+ {
+ /* despotify doesn't like having multiple get_image calls running */
+ static QMutex despotify_get_image_mutex;
+ QMutexLocker locker(&despotify_get_image_mutex);
+ data = despotify_get_image(m_session, const_cast<char *>(coverId.data()), &len);
+ }
+
+ free(data);
+
+ QImage img = data != 0 ? QImage::fromData(static_cast<const uchar *>(data), len, "JPEG") : QImage();
+ {
+ QMutexLocker locker(&m_coverCacheLock);
+ m_coverCache.insert(coverId, img);
+ }
+
+ return coverId;
+}
+
+void CoverDatabase::imageDataFetched()
+{
+ QFutureWatcher<QByteArray> *watcher = static_cast<QFutureWatcher<QByteArray> *>(sender());
+ Q_ASSERT(watcher != 0);
+
+ QByteArray coverId = watcher->result();
+ m_pendingCovers.remove(coverId);
+ {
+ QMutexLocker locker(&m_coverCacheLock);
+ if (coverId == m_currentCoverId)
+ emit coverLoaded(QPixmap::fromImage(m_coverCache.value(m_currentCoverId)));
+ }
+
+}
diff --git a/coverdatabase.h b/coverdatabase.h
new file mode 100644
index 0000000..8484cdc
--- /dev/null
+++ b/coverdatabase.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** $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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef COVERDATABASE_H
+#define COVERDATABASE_H
+
+#include <QtCore/QObject>
+#include <QtCore/QHash>
+#include <QtCore/QMutex>
+#include <QtCore/QSet>
+
+#include <QtGui/QImage>
+
+class QLabel;
+struct despotify_session;
+class CoverDatabase: public QObject
+{
+ Q_OBJECT
+public:
+ CoverDatabase(despotify_session *session, QObject *parent = 0);
+ ~CoverDatabase();
+
+ void loadCover(const QByteArray &coverId);
+
+private slots:
+ void imageDataFetched();
+
+signals:
+ void coverLoaded(const QPixmap &pm);
+
+private:
+ QByteArray fetchImageData(const QByteArray &coverId);
+
+ despotify_session *m_session;
+ QByteArray m_currentCoverId;
+ QSet<QByteArray> m_pendingCovers;
+ QHash<QByteArray, QImage> m_coverCache;
+ QMutex m_coverCacheLock;
+};
+
+#endif // COVERDATABASE_H
diff --git a/qtplaylist.cpp b/qtplaylist.cpp
index 6534200..6232164 100644
--- a/qtplaylist.cpp
+++ b/qtplaylist.cpp
@@ -1,3 +1,28 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** $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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
#include "qtplaylist.h"
#include "despotify_cpp.h"
@@ -12,6 +37,32 @@ QtPlaylist::~QtPlaylist()
cleanUp();
}
+QString QtPlaylist::name() const
+{
+ switch (m_type) {
+ case Playlist:
+ if (m_playlist != 0)
+ return QString::fromUtf8(m_playlist->name);
+ break;
+ case Search:
+ if (m_searchResult != 0)
+ return QString::fromUtf8(reinterpret_cast<char *>(m_searchResult->query));
+ break;
+ case Album:
+ if (m_album != 0)
+ return tr("Songs from %1 by %2").arg(QString::fromUtf8(m_album->name));
+ break;
+ case Artist:
+ if (m_artist != 0)
+ return tr("Songs by %1").arg(QString::fromUtf8(m_artist->name));
+ break;
+ default:
+ break;
+ }
+
+ return tr("Empty list");
+}
+
void QtPlaylist::cleanUp()
{
switch (m_type) {
diff --git a/qtplaylist.h b/qtplaylist.h
index d43ceef..0480c7f 100644
--- a/qtplaylist.h
+++ b/qtplaylist.h
@@ -1,7 +1,33 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** $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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
#ifndef QTPLAYLIST_H
#define QTPLAYLIST_H
#include <QtCore/QObject>
+#include <QtCore/QMetaType>
struct despotify_session;
struct playlist;
@@ -24,6 +50,8 @@ public:
QList<track *> tracks() const;
+ QString name() const;
+
private:
void cleanUp();
QList<track *> makeList(track *firstTrack) const;
@@ -48,4 +76,6 @@ private:
};
+Q_DECLARE_METATYPE(QtPlaylist *)
+
#endif // QTPLAYLIST_H
diff --git a/qtspotify.pro b/qtspotify.pro
index 2159f3a..711d738 100644
--- a/qtspotify.pro
+++ b/qtspotify.pro
@@ -1,11 +1,15 @@
HEADERS += qtspotifymain.h \
logindialog.h \
despotify_cpp.h \
- qtplaylist.h
+ qtplaylist.h \
+ storedplaylistmodel.h \
+ coverdatabase.h
SOURCES += qtspotifymain.cpp \
main.cpp \
logindialog.cpp \
- qtplaylist.cpp
+ qtplaylist.cpp \
+ storedplaylistmodel.cpp \
+ coverdatabase.cpp
FORMS += qtspotify.ui \
logindialog.ui
LIBS += -ldespotify
diff --git a/qtspotify.ui b/qtspotify.ui
index 6f665b0..72bfb57 100644
--- a/qtspotify.ui
+++ b/qtspotify.ui
@@ -16,78 +16,170 @@
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
- <widget class="QLabel" name="artistLabel">
- <property name="toolTip">
- <string>Currently playing artist</string>
+ <widget class="QTabWidget" name="topLevelTabs">
+ <property name="currentIndex">
+ <number>2</number>
</property>
- <property name="text">
- <string>Artist</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QLabel" name="songLabel">
- <property name="toolTip">
- <string>Currently playing song</string>
- </property>
- <property name="text">
- <string>Song</string>
- </property>
- </widget>
- </item>
- <item row="0" column="2">
- <widget class="QLabel" name="albumLabel">
- <property name="toolTip">
- <string>Currently playing album</string>
- </property>
- <property name="text">
- <string>Album</string>
- </property>
- </widget>
- </item>
- <item row="1" column="0" colspan="3">
- <widget class="QTreeWidget" name="playList">
- <column>
- <property name="text">
- <string>Artist</string>
- </property>
- </column>
- <column>
- <property name="text">
- <string>Song</string>
- </property>
- </column>
- <column>
- <property name="text">
- <string>Album</string>
- </property>
- </column>
+ <widget class="QWidget" name="tab">
+ <attribute name="title">
+ <string>Current playlist</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="0">
+ <widget class="QTreeWidget" name="playList">
+ <property name="font">
+ <font>
+ <pointsize>12</pointsize>
+ </font>
+ </property>
+ <column>
+ <property name="text">
+ <string>Artist</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Song</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Album</string>
+ </property>
+ </column>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_2">
+ <attribute name="title">
+ <string>Playlists</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <item row="0" column="0">
+ <widget class="QGroupBox" name="storedPlaylistsGroupBox">
+ <property name="title">
+ <string>Stored playlists</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_6">
+ <item row="0" column="0">
+ <widget class="QListView" name="storedPlaylists">
+ <property name="font">
+ <font>
+ <pointsize>12</pointsize>
+ </font>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Searches</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_5">
+ <item row="0" column="0">
+ <widget class="QListView" name="listView"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_3">
+ <attribute name="title">
+ <string>Now playing</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="0" column="0" colspan="2">
+ <widget class="QLabel" name="coverArtLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Cover art</string>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" colspan="2">
+ <widget class="QLabel" name="songLabel">
+ <property name="font">
+ <font>
+ <pointsize>22</pointsize>
+ </font>
+ </property>
+ <property name="text">
+ <string>Song</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="artistLabel">
+ <property name="text">
+ <string>Artist</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="albumLabel">
+ <property name="text">
+ <string>(Album)</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
</widget>
</item>
- <item row="2" column="0" colspan="3">
+ <item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
- <widget class="QLabel" name="label">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="playButton">
<property name="text">
- <string>Search:</string>
+ <string>Play</string>
</property>
</widget>
</item>
<item>
- <widget class="QComboBox" name="searchComboBox">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
+ <widget class="QPushButton" name="skipSongButton">
+ <property name="text">
+ <string>Skip</string>
</property>
- <property name="toolTip">
- <string>Select playlist or enter search term</string>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
</property>
- <property name="editable">
- <bool>true</bool>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
</property>
- </widget>
+ </spacer>
</item>
</layout>
</item>
@@ -116,7 +208,6 @@
<string>&amp;Playlist</string>
</property>
<addaction name="actionSearch"/>
- <addaction name="actionRetrieveStoredPlaylists"/>
</widget>
<widget class="QMenu" name="menuPlayback">
<property name="title">
@@ -154,14 +245,6 @@
<string>Ctrl+S</string>
</property>
</action>
- <action name="actionLogOut">
- <property name="text">
- <string>Log &amp;out</string>
- </property>
- <property name="shortcut">
- <string>Ctrl+X</string>
- </property>
- </action>
<action name="actionPlayOrStop">
<property name="text">
<string>&amp;Play / stop</string>
@@ -178,14 +261,6 @@
<string>Space</string>
</property>
</action>
- <action name="actionRetrieveStoredPlaylists">
- <property name="text">
- <string>&amp;Retrieve stored playlists</string>
- </property>
- <property name="shortcut">
- <string>Ctrl+R</string>
- </property>
- </action>
<action name="actionDebuggingOutput">
<property name="checkable">
<bool>true</bool>
@@ -216,21 +291,5 @@
</hint>
</hints>
</connection>
- <connection>
- <sender>actionSearch</sender>
- <signal>triggered()</signal>
- <receiver>searchComboBox</receiver>
- <slot>setFocus()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>-1</x>
- <y>-1</y>
- </hint>
- <hint type="destinationlabel">
- <x>195</x>
- <y>641</y>
- </hint>
- </hints>
- </connection>
</connections>
</ui>
diff --git a/qtspotifymain.cpp b/qtspotifymain.cpp
index 32176c6..1e563ec 100644
--- a/qtspotifymain.cpp
+++ b/qtspotifymain.cpp
@@ -25,6 +25,8 @@
#include "qtspotifymain.h"
#include "logindialog.h"
#include "qtplaylist.h"
+#include "storedplaylistmodel.h"
+#include "coverdatabase.h"
#include <QtCore/QtConcurrentRun>
#include <QtCore/QFuture>
@@ -32,6 +34,7 @@
#include <QtCore/QStateMachine>
#include <QtCore/QState>
#include <QtCore/QSettings>
+#include <QtCore/QHistoryState>
#include <QtGui/QMenuBar>
#include <QtGui/QMenu>
@@ -41,9 +44,9 @@
#include "despotify_cpp.h"
-Q_DECLARE_METATYPE(QtPlaylist *)
Q_DECLARE_METATYPE(track *)
+
namespace {
class SelectTrackEvent: public QEvent
{
@@ -57,15 +60,34 @@ namespace {
};
}
+static void callback(despotify_session *session, int signal, void *data, void *callbackData)
+{
+ if (callbackData == 0 || data == 0 || session == 0)
+ return;
+
+ switch (signal) {
+ case DESPOTIFY_TRACK_CHANGE:
+ {
+ track *t = reinterpret_cast<track *>(data);
+ QtSpotifyMain *qsm = reinterpret_cast<QtSpotifyMain*>(session->client_callback_data);
+ QCoreApplication::postEvent(qsm, new SelectTrackEvent(t));
+ }
+ };
+}
QtSpotifyMain::QtSpotifyMain(QWidget *parent)
- : QMainWindow(parent), m_session(0), m_rootPlaylist(0),
- m_machine(new QStateMachine), m_debugging(false),
+ : QMainWindow(parent), m_session(0), m_machine(new QStateMachine), m_debugging(false),
m_authenticationWatcher(0), m_retrievingPlayListWatcher(0)
{
- initUi();
- initWatchers();
- initMachine();
+ m_session = despotify_init_client(callback, this, true);
+
+ if (m_session != 0) {
+ initUi();
+ initWatchers();
+ initMachine();
+ } else {
+ qWarning("Couldn't initialize despotify");
+ }
}
QtSpotifyMain::~QtSpotifyMain()
@@ -105,6 +127,10 @@ void QtSpotifyMain::initUi()
}
setStatusBar(new MyStatusBar(this));
+
+ m_coverDatabase = new CoverDatabase(m_session, this);
+ connect(m_coverDatabase, SIGNAL(coverLoaded(QPixmap)),
+ m_ui.coverArtLabel, SLOT(setPixmap(QPixmap)));
}
void QtSpotifyMain::initWatchers()
@@ -135,6 +161,7 @@ void QtSpotifyMain::selectTrack(track *newTrack)
track *t = data.value<track *>();
if (t == newTrack) {
m_ui.playList->setCurrentItem(m_ui.playList->topLevelItem(i));
+ setNewTrack(t);
return;
}
}
@@ -145,61 +172,34 @@ void QtSpotifyMain::selectTrack(track *newTrack)
void QtSpotifyMain::retrievePlayLists()
{
debug(tr("Retrieving playlists"));
- m_ui.searchComboBox->clear();
- m_ui.searchComboBox->clearEditText();
-
- if (m_rootPlaylist != 0) {
- despotify_free_playlist(m_rootPlaylist);
- m_rootPlaylist = 0;
- }
-
QFuture<playlist *> retrieving = QtConcurrent::run(despotify_get_stored_playlists, m_session);
m_retrievingPlayListWatcher->setFuture(retrieving);
}
void QtSpotifyMain::selectPlayList()
{
- QString searchTerm = m_ui.searchComboBox->currentText();
- m_ui.playList->clear();
+ m_ui.playList->clear();
- int idx = m_ui.searchComboBox->currentIndex();
- if (idx >= 0) {
- QVariant data = m_ui.searchComboBox->itemData(idx);
+ QModelIndex idx = m_ui.storedPlaylists->currentIndex();
+ if (idx.isValid()) {
+ QVariant data = idx.data(Qt::UserRole);
QtPlaylist *pl = data.value<QtPlaylist *>();
if (pl != 0) {
- debug(tr("Selecting playlist '%1'").arg(m_ui.searchComboBox->itemText(idx)));
+ debug(tr("Selecting playlist '%1'").arg(idx.data(Qt::DisplayRole).toString()));
setPlaylist(pl->tracks());
return;
}
}
-
- QtPlaylist *newSearch = new QtPlaylist(m_session, this);
- newSearch->setSearchTerm(searchTerm);
- m_searches.append(newSearch);
-
- debug(tr("Searching for '%1'").arg(searchTerm));
- setPlaylist(newSearch->tracks());
}
void QtSpotifyMain::populateSearchBox()
{
debug(tr("Populating search box"));
- m_rootPlaylist = m_retrievingPlayListWatcher->result();
- if (m_rootPlaylist == 0)
+ playlist *rootPlaylist = m_retrievingPlayListWatcher->result();
+ if (rootPlaylist == 0)
debug(tr("Cannot get stored playlists, error==%1").arg(QString::fromUtf8(m_session->last_error)));
- playlist *pl = m_rootPlaylist;
- while (pl != 0) {
- QtPlaylist *playlist = new QtPlaylist(m_session, this);
- playlist->setPlaylist(pl);
-
- m_ui.searchComboBox->addItem(QString::fromUtf8(pl->name), QVariant::fromValue(playlist));
- debug(tr("Adding '%1'").arg(QString::fromUtf8(pl->name)));
-
- pl = pl->next;
- }
-
- m_ui.searchComboBox->setEditText(QString());
+ m_ui.storedPlaylists->setModel(new StoredPlaylistModel(m_session, rootPlaylist, m_ui.storedPlaylists));
}
void QtSpotifyMain::stop()
@@ -245,16 +245,24 @@ void QtSpotifyMain::play()
void QtSpotifyMain::initPlayingState(QState *playingState)
{
QState *notPaused = new QState(playingState);
- notPaused->setObjectName("notPaused");
- playingState->setInitialState(notPaused);
+ {
+ notPaused->setObjectName("notPaused");
+ notPaused->assignProperty(m_ui.playButton, "text", tr("Pause"));
+ playingState->setInitialState(notPaused);
+ }
QState *paused = new QState(playingState);
- paused->setObjectName("paused");
- connect(paused, SIGNAL(entered()), this, SLOT(pause()));
- connect(paused, SIGNAL(exited()), this, SLOT(resume()));
+ {
+ paused->assignProperty(m_ui.playButton, "text", tr("Play"));
+ paused->setObjectName("paused");
+ connect(paused, SIGNAL(entered()), this, SLOT(pause()));
+ connect(paused, SIGNAL(exited()), this, SLOT(resume()));
+ }
notPaused->addTransition(m_ui.actionPauseOrResume, SIGNAL(triggered()), paused);
paused->addTransition(m_ui.actionPauseOrResume, SIGNAL(triggered()), notPaused);
+ notPaused->addTransition(m_ui.playButton, SIGNAL(clicked()), paused);
+ paused->addTransition(m_ui.playButton, SIGNAL(clicked()), notPaused);
}
void QtSpotifyMain::decideLoginResult()
@@ -271,26 +279,35 @@ void QtSpotifyMain::decideLoginResult()
void QtSpotifyMain::initPlayBackHandlingState(QState *playBackHandlingState)
{
QState *stoppedState = new QState(playBackHandlingState);
- stoppedState->assignProperty(m_ui.actionPauseOrResume, "enabled", false);
- stoppedState->assignProperty(m_ui.actionPlayOrStop, "enabled", true);
- stoppedState->assignProperty(m_ui.albumLabel, "text", QString());
- stoppedState->assignProperty(m_ui.artistLabel, "text", QString());
- stoppedState->assignProperty(m_ui.songLabel, "text", QString());
- stoppedState->setObjectName("stoppedState");
- connect(stoppedState, SIGNAL(entered()), this, SLOT(stop()));
- playBackHandlingState->setInitialState(stoppedState);
+ {
+ stoppedState->assignProperty(m_ui.actionPauseOrResume, "enabled", false);
+ stoppedState->assignProperty(m_ui.actionPlayOrStop, "enabled", true);
+ stoppedState->assignProperty(m_ui.albumLabel, "text", QString());
+ stoppedState->assignProperty(m_ui.artistLabel, "text", QString());
+ stoppedState->assignProperty(m_ui.songLabel, "text", QString());
+ stoppedState->assignProperty(m_ui.coverArtLabel, "pixmap", QPixmap());
+ stoppedState->assignProperty(m_ui.coverArtLabel, "text", QString());
+ stoppedState->assignProperty(m_ui.playButton, "enabled", true);
+ stoppedState->assignProperty(m_ui.skipSongButton, "enabled", false);
+ stoppedState->setObjectName("stoppedState");
+ connect(stoppedState, SIGNAL(entered()), this, SLOT(stop()));
+ playBackHandlingState->setInitialState(stoppedState);
+ }
QState *playingState = new QState(playBackHandlingState);
- playingState->setObjectName("playingState");
- playingState->assignProperty(m_ui.actionPlayOrStop, "enabled", true);
- connect(playingState, SIGNAL(entered()), this, SLOT(play()));
- stoppedState->assignProperty(m_ui.actionPauseOrResume, "enabled", true);
- initPlayingState(playingState);
+ {
+ playingState->setObjectName("playingState");
+ playingState->assignProperty(m_ui.actionPlayOrStop, "enabled", true);
+ connect(playingState, SIGNAL(entered()), this, SLOT(play()));
+ stoppedState->assignProperty(m_ui.actionPauseOrResume, "enabled", true);
+ stoppedState->assignProperty(m_ui.playButton, "enabled", true);
+ stoppedState->assignProperty(m_ui.skipSongButton, "enabled", true);
+ initPlayingState(playingState);
+ }
stoppedState->addTransition(m_ui.actionPlayOrStop, SIGNAL(triggered()), playingState);
playingState->addTransition(m_ui.actionPlayOrStop, SIGNAL(triggered()), stoppedState);
-
stoppedState->addTransition(m_ui.playList, SIGNAL(doubleClicked(QModelIndex)),
playingState);
playingState->addTransition(m_ui.playList, SIGNAL(doubleClicked(QModelIndex)),
@@ -300,47 +317,67 @@ void QtSpotifyMain::initPlayBackHandlingState(QState *playBackHandlingState)
void QtSpotifyMain::initLoggedInState(QState *loggedIn)
{
QState *playListHandlingState = new QState(loggedIn);
- playListHandlingState->setObjectName("playListHandlingState");
- initPlayListHandlingState(playListHandlingState);
+ {
+ playListHandlingState->setObjectName("playListHandlingState");
+ initPlayListHandlingState(playListHandlingState);
+ }
QState *playBackHandlingState = new QState(loggedIn);
- playBackHandlingState->setObjectName("playBackHandlingState");
- initPlayBackHandlingState(playBackHandlingState);
+ {
+ playBackHandlingState->setObjectName("playBackHandlingState");
+ initPlayBackHandlingState(playBackHandlingState);
+ }
+}
+
+void QtSpotifyMain::initIdleState(QState *idle)
+{
+ QState *requestedRetrieve = new QState(idle);
+ {
+ requestedRetrieve->assignProperty(m_ui.storedPlaylists, "enabled", false);
+ connect(requestedRetrieve, SIGNAL(entered()), this, SLOT(retrievePlayLists()));
+ requestedRetrieve->setObjectName("requestedRetrieve");
+ }
+
+ QHistoryState *historyState = new QHistoryState(idle);
+ {
+ historyState->setDefaultState(requestedRetrieve);
+ idle->setInitialState(historyState);
+ }
+
+ QState *retrieved = new QState(idle);
+ {
+ retrieved->assignProperty(m_ui.storedPlaylists, "enabled", true);
+ retrieved->setObjectName("retrieved");
+ }
+
+ requestedRetrieve->addTransition(m_retrievingPlayListWatcher, SIGNAL(finished()), retrieved);
}
void QtSpotifyMain::initPlayListHandlingState(QState *playListHandlingState)
{
QState *idle = new QState(playListHandlingState);
- idle->assignProperty(m_ui.actionSearch, "enabled", true);
- idle->assignProperty(m_ui.searchComboBox, "enabled", true);
- idle->assignProperty(m_ui.playList, "enabled", true);
- idle->assignProperty(m_ui.actionRetrieveStoredPlaylists, "enabled", true);
- idle->setObjectName("idle");
- idle->assignProperty(statusBar(), "statusMessage", tr("Go ahead..."));
- playListHandlingState->setInitialState(idle);
-
- QState *requestedRetrieve = new QState(playListHandlingState);
- requestedRetrieve->assignProperty(m_ui.actionSearch, "enabled", false);
- requestedRetrieve->assignProperty(m_ui.searchComboBox, "enabled", false);
- requestedRetrieve->assignProperty(m_ui.playList, "enabled", true);
- requestedRetrieve->assignProperty(m_ui.actionRetrieveStoredPlaylists, "enabled", false);
- requestedRetrieve->assignProperty(statusBar(), "statusMessage", tr("Retrieving playlists"));
- connect(requestedRetrieve, SIGNAL(entered()), this, SLOT(retrievePlayLists()));
- requestedRetrieve->setObjectName("requestedRetrieve");
+ {
+ idle->setObjectName("idle");
- QState *playListSelected = new QState(playListHandlingState);
- playListSelected->setObjectName("playListSelected");
- playListSelected->assignProperty(statusBar(), "statusMessage", tr("Fetching playlist contents"));
- connect(playListSelected, SIGNAL(entered()), this, SLOT(selectPlayList()));
+ idle->assignProperty(m_ui.actionSearch, "enabled", true);
+ idle->assignProperty(m_ui.playList, "enabled", true);
+ idle->assignProperty(statusBar(), "statusMessage", tr("Go ahead..."));
- idle->addTransition(m_ui.searchComboBox, SIGNAL(activated(QString)),
- playListSelected);
- idle->addTransition(m_ui.actionRetrieveStoredPlaylists, SIGNAL(triggered()),
- requestedRetrieve);
+ initIdleState(idle);
+ playListHandlingState->setInitialState(idle);
+ }
+ QState *playListSelected = new QState(playListHandlingState);
+ {
+ playListSelected->setObjectName("playListSelected");
+ playListSelected->assignProperty(statusBar(), "statusMessage", tr("Fetching playlist contents"));
+ connect(playListSelected, SIGNAL(entered()), this, SLOT(selectPlayList()));
+ }
+
+ idle->addTransition(m_ui.storedPlaylists, SIGNAL(activated(QModelIndex)), playListSelected);
playListSelected->addTransition(idle);
- requestedRetrieve->addTransition(m_retrievingPlayListWatcher, SIGNAL(finished()), idle);
+
}
void QtSpotifyMain::debug(const QString &text)
@@ -352,15 +389,19 @@ void QtSpotifyMain::debug(const QString &text)
void QtSpotifyMain::initLoggingInState(QState *loggingInState)
{
QState *logInDialogShown = new QState(loggingInState);
- connect(logInDialogShown, SIGNAL(entered()), m_logInDialog, SLOT(exec()));
- logInDialogShown->assignProperty(statusBar(), "statusMessage", tr("Waiting for input"));
- logInDialogShown->setObjectName("logInDialogShown");
- loggingInState->setInitialState(logInDialogShown);
+ {
+ connect(logInDialogShown, SIGNAL(entered()), m_logInDialog, SLOT(exec()));
+ logInDialogShown->assignProperty(statusBar(), "statusMessage", tr("Waiting for input"));
+ logInDialogShown->setObjectName("logInDialogShown");
+ loggingInState->setInitialState(logInDialogShown);
+ }
QState *logInDialogAccepted = new QState(loggingInState);
- logInDialogAccepted->assignProperty(statusBar(), "statusMessage", tr("Trying to log in..."));
- logInDialogAccepted->setObjectName("logInDialogAccepted");
- connect(logInDialogAccepted, SIGNAL(entered()), this, SLOT(logIn()));
+ {
+ logInDialogAccepted->assignProperty(statusBar(), "statusMessage", tr("Trying to log in..."));
+ logInDialogAccepted->setObjectName("logInDialogAccepted");
+ connect(logInDialogAccepted, SIGNAL(entered()), this, SLOT(logIn()));
+ }
logInDialogShown->addTransition(m_logInDialog, SIGNAL(accepted()), logInDialogAccepted);
}
@@ -368,37 +409,45 @@ void QtSpotifyMain::initLoggingInState(QState *loggingInState)
void QtSpotifyMain::initMachine()
{
QState *notLoggedInState = new QState(m_machine);
- notLoggedInState->assignProperty(m_ui.actionLogIn, "enabled", true);
- notLoggedInState->assignProperty(m_ui.actionLogOut, "enabled", false);
- notLoggedInState->assignProperty(m_ui.actionSearch, "enabled", false);
- notLoggedInState->assignProperty(m_ui.centralwidget, "enabled", false);
- notLoggedInState->assignProperty(m_ui.actionPauseOrResume, "enabled", false);
- notLoggedInState->assignProperty(m_ui.actionPlayOrStop, "enabled", false);
- notLoggedInState->assignProperty(m_ui.actionRetrieveStoredPlaylists, "enabled", false);
- notLoggedInState->assignProperty(statusBar(), "statusMessage", tr("Not logged in"));
- notLoggedInState->assignProperty(this, "debuggingMessage", tr("Entered 'notLoggedInState'"));
- notLoggedInState->setObjectName("notLoggedInState");
- m_machine->setInitialState(notLoggedInState);
+ {
+ notLoggedInState->assignProperty(m_ui.actionLogIn, "enabled", true);
+
+ notLoggedInState->assignProperty(m_ui.actionSearch, "enabled", false);
+ notLoggedInState->assignProperty(m_ui.centralwidget, "enabled", false);
+ notLoggedInState->assignProperty(m_ui.actionPauseOrResume, "enabled", false);
+ notLoggedInState->assignProperty(m_ui.actionPlayOrStop, "enabled", false);
+
+ notLoggedInState->assignProperty(statusBar(), "statusMessage", tr("Select 'log in' from menu to log in"));
+ notLoggedInState->assignProperty(this, "debuggingMessage", tr("Entered 'notLoggedInState'"));
+ notLoggedInState->setObjectName("notLoggedInState");
+
+ m_machine->setInitialState(notLoggedInState);
+ }
QState *loggingInState = new QState(m_machine);
- loggingInState->assignProperty(m_ui.actionLogIn, "enabled", false);
- loggingInState->assignProperty(m_ui.actionLogOut, "enabled", false);
- loggingInState->assignProperty(m_ui.actionSearch, "enabled", false);
- loggingInState->assignProperty(m_ui.centralwidget, "enabled", false);
- loggingInState->assignProperty(m_ui.actionPauseOrResume, "enabled", false);
- loggingInState->assignProperty(m_ui.actionPlayOrStop, "enabled", false);
- loggingInState->assignProperty(m_ui.actionRetrieveStoredPlaylists, "enabled", false);
- loggingInState->setObjectName("loggingInState");
- loggingInState->assignProperty(this, "debuggingMessage", tr("Entered 'loggingInState'"));
- initLoggingInState(loggingInState);
+ {
+ loggingInState->assignProperty(m_ui.actionLogIn, "enabled", false);
+ loggingInState->assignProperty(m_ui.actionSearch, "enabled", false);
+ loggingInState->assignProperty(m_ui.centralwidget, "enabled", false);
+ loggingInState->assignProperty(m_ui.actionPauseOrResume, "enabled", false);
+ loggingInState->assignProperty(m_ui.actionPlayOrStop, "enabled", false);
+
+ loggingInState->setObjectName("loggingInState");
+ loggingInState->assignProperty(this, "debuggingMessage", tr("Entered 'loggingInState'"));
+
+ initLoggingInState(loggingInState);
+ }
QState *loggedInState = new QState(QState::ParallelStates, m_machine);
- loggedInState->assignProperty(m_ui.actionLogIn, "enabled", false);
- loggedInState->assignProperty(m_ui.actionLogOut, "enabled", true);
- loggedInState->assignProperty(m_ui.centralwidget, "enabled", true);
- loggedInState->setObjectName("loggedInState");
- loggedInState->assignProperty(this, "debuggingMessage", tr("Entered 'loggedInState'"));
- initLoggedInState(loggedInState);
+ {
+ loggedInState->assignProperty(m_ui.actionLogIn, "enabled", false);
+ loggedInState->assignProperty(m_ui.centralwidget, "enabled", true);
+
+ loggedInState->setObjectName("loggedInState");
+ loggedInState->assignProperty(this, "debuggingMessage", tr("Entered 'loggedInState'"));
+
+ initLoggedInState(loggedInState);
+ }
notLoggedInState->addTransition(m_ui.actionLogIn, SIGNAL(triggered()), loggingInState);
loggingInState->addTransition(m_logInDialog, SIGNAL(rejected()), notLoggedInState);
@@ -424,47 +473,19 @@ void QtSpotifyMain::setPlaylist(QList<track *> tracks)
}
}
-static void callback(despotify_session *session, int signal, void *data, void *callbackData)
-{
- if (callbackData == 0 || data == 0 || session == 0)
- return;
-
- switch (signal) {
- case DESPOTIFY_TRACK_CHANGE:
- {
- track *t = reinterpret_cast<track *>(data);
- QtSpotifyMain *qsm = reinterpret_cast<QtSpotifyMain*>(session->client_callback_data);
- QCoreApplication::postEvent(qsm, new SelectTrackEvent(t));
- }
- };
-}
void QtSpotifyMain::endSession()
{
debug("Ending session");
- if (m_rootPlaylist != 0)
- despotify_free_playlist(m_rootPlaylist);
-
qDeleteAll(m_searches);
-
- for (int i=0; i<m_ui.searchComboBox->count(); ++i) {
- QVariant v = m_ui.searchComboBox->itemData(i);
- QtPlaylist *pl = v.value<QtPlaylist *>();
- delete pl;
- }
- m_ui.searchComboBox->clear();
-
despotify_exit(m_session);
}
void QtSpotifyMain::logIn()
{
if (m_session == 0) {
- m_session = despotify_init_client(callback, this, true);
- if (m_session == 0) {
- emit loginFailed();
- return;
- }
+ emit loginFailed();
+ return;
}
QString userName = m_logInDialog->userName();
@@ -493,6 +514,9 @@ void QtSpotifyMain::setNewTrack(track *t)
{
debug(tr("Setting new track '%1'").arg(QString::fromUtf8(t->title)));
m_ui.artistLabel->setText(QString::fromUtf8(t->artist->name));
- m_ui.albumLabel->setText(QString::fromUtf8(t->album));
+ m_ui.albumLabel->setText(QLatin1Char('(') + QString::fromUtf8(t->album) + QLatin1Char(')'));
m_ui.songLabel->setText(QString::fromUtf8(t->title));
+ m_ui.coverArtLabel->setPixmap(QPixmap());
+ m_ui.coverArtLabel->setText(QString());
+ m_coverDatabase->loadCover(reinterpret_cast<const char *>(t->cover_id));
}
diff --git a/qtspotifymain.h b/qtspotifymain.h
index aaa7376..f4b11b0 100644
--- a/qtspotifymain.h
+++ b/qtspotifymain.h
@@ -67,6 +67,7 @@ class QStateMachine;
class QState;
class LogInDialog;
+class CoverDatabase;
class QtSpotifyMain: public QMainWindow
{
Q_OBJECT
@@ -114,6 +115,7 @@ private:
void initPlayListHandlingState(QState *);
void initPlayBackHandlingState(QState *);
void initPlayingState(QState *);
+ void initIdleState(QState *);
void setNewTrack(track *t);
void selectTrack(track *t);
@@ -122,6 +124,7 @@ private:
playlist *m_rootPlaylist;
QStateMachine *m_machine;
+ CoverDatabase *m_coverDatabase;
QList<QtPlaylist *> m_searches;
diff --git a/storedplaylistmodel.cpp b/storedplaylistmodel.cpp
new file mode 100644
index 0000000..2dd94d7
--- /dev/null
+++ b/storedplaylistmodel.cpp
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** $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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "storedplaylistmodel.h"
+#include "qtplaylist.h"
+
+#include "despotify_cpp.h"
+
+StoredPlaylistModel::StoredPlaylistModel(despotify_session *session, playlist *rootPlaylist,
+ QObject *parent)
+ : QAbstractListModel(parent), m_session(session), m_rootPlaylist(rootPlaylist)
+{
+ playlist *pl = m_rootPlaylist;
+ while (pl != 0) {
+ QtPlaylist *playlist = new QtPlaylist(m_session, this);
+ playlist->setPlaylist(pl);
+ m_playlists.append(playlist);
+ pl = pl->next;
+ }
+
+}
+
+StoredPlaylistModel::~StoredPlaylistModel()
+{
+ despotify_free_playlist(m_rootPlaylist);
+}
+
+int StoredPlaylistModel::rowCount(const QModelIndex &parent) const
+{
+ Q_ASSERT(!parent.isValid());
+ return m_playlists.size();
+}
+
+QVariant StoredPlaylistModel::data(const QModelIndex &index, int role) const
+{
+ Q_ASSERT(index.column() == 0);
+
+ QtPlaylist *pl = m_playlists.at(index.row());
+ if (role == Qt::DisplayRole)
+ return pl->name();
+ else if (role == Qt::UserRole)
+ return QVariant::fromValue(pl);
+ else
+ return QVariant();
+}
+
diff --git a/storedplaylistmodel.h b/storedplaylistmodel.h
new file mode 100644
index 0000000..e6a0b66
--- /dev/null
+++ b/storedplaylistmodel.h
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** $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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef STOREDPLAYLISTMODEL_H
+#define STOREDPLAYLISTMODEL_H
+
+#include <QAbstractListModel>
+
+struct playlist;
+class QtPlaylist;
+struct despotify_session;
+class StoredPlaylistModel: public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ StoredPlaylistModel(despotify_session *session, playlist *rootPlaylist, QObject *parent = 0);
+ ~StoredPlaylistModel();
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+
+private:
+ despotify_session *m_session;
+ playlist *m_rootPlaylist;
+
+ QList<QtPlaylist *> m_playlists;
+};
+
+#endif // STOREDPLAYLISTMODEL_H