diff options
-rw-r--r-- | coverdatabase.cpp | 112 | ||||
-rw-r--r-- | coverdatabase.h | 62 | ||||
-rw-r--r-- | qtplaylist.cpp | 51 | ||||
-rw-r--r-- | qtplaylist.h | 30 | ||||
-rw-r--r-- | qtspotify.pro | 8 | ||||
-rw-r--r-- | qtspotify.ui | 243 | ||||
-rw-r--r-- | qtspotifymain.cpp | 332 | ||||
-rw-r--r-- | qtspotifymain.h | 3 | ||||
-rw-r--r-- | storedplaylistmodel.cpp | 68 | ||||
-rw-r--r-- | storedplaylistmodel.h | 51 |
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>&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 &out</string> - </property> - <property name="shortcut"> - <string>Ctrl+X</string> - </property> - </action> <action name="actionPlayOrStop"> <property name="text"> <string>&Play / stop</string> @@ -178,14 +261,6 @@ <string>Space</string> </property> </action> - <action name="actionRetrieveStoredPlaylists"> - <property name="text"> - <string>&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 |