summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@nokia.com>2009-10-23 21:06:09 +0200
committerEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@nokia.com>2009-10-23 21:06:09 +0200
commit95fc9a918f123ec165f1048aa5ea8b522a4a806a (patch)
tree3a778fa417c954920fbf54071abde912236ace43
First commit
-rw-r--r--despotify_cpp.h8
-rw-r--r--logindialog.cpp57
-rw-r--r--logindialog.h31
-rw-r--r--logindialog.ui95
-rw-r--r--main.cpp26
-rw-r--r--qtspotify.pro9
-rw-r--r--qtspotify.ui236
-rw-r--r--qtspotifymain.cpp431
-rw-r--r--qtspotifymain.h106
9 files changed, 999 insertions, 0 deletions
diff --git a/despotify_cpp.h b/despotify_cpp.h
new file mode 100644
index 0000000..8027d92
--- /dev/null
+++ b/despotify_cpp.h
@@ -0,0 +1,8 @@
+#ifndef DESPOTIFY_CPP_H
+#define DESPOTIFY_CPP_H
+
+extern "C" {
+ #include <despotify.h>
+}
+
+#endif // DESPOTIFY_CPP_H
diff --git a/logindialog.cpp b/logindialog.cpp
new file mode 100644
index 0000000..28feb50
--- /dev/null
+++ b/logindialog.cpp
@@ -0,0 +1,57 @@
+#include "logindialog.h"
+#include "ui_logindialog.h"
+
+LogInDialog::LogInDialog(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::LogInDialog)
+{
+ ui->setupUi(this);
+}
+
+LogInDialog::~LogInDialog()
+{
+ delete ui;
+}
+
+void LogInDialog::changeEvent(QEvent *e)
+{
+ QDialog::changeEvent(e);
+ switch (e->type()) {
+ case QEvent::LanguageChange:
+ ui->retranslateUi(this);
+ break;
+ default:
+ break;
+ }
+}
+
+QString LogInDialog::userName() const
+{
+ return ui->userNameLineEdit->text();
+}
+
+QString LogInDialog::password() const
+{
+ return ui->passwordLineEdit->text();
+}
+
+bool LogInDialog::rememberSettings() const
+{
+ return ui->rememberMe->isChecked();
+}
+
+void LogInDialog::setUserName(const QString &userName)
+{
+ ui->userNameLineEdit->setText(userName);
+}
+
+void LogInDialog::setPassword(const QString &password)
+{
+ ui->passwordLineEdit->setText(password);
+}
+
+void LogInDialog::setRememberSettings(bool on)
+{
+ ui->rememberMe->setChecked(on);
+}
+
diff --git a/logindialog.h b/logindialog.h
new file mode 100644
index 0000000..4c2b9a5
--- /dev/null
+++ b/logindialog.h
@@ -0,0 +1,31 @@
+#ifndef LOGINDIALOG_H
+#define LOGINDIALOG_H
+
+#include <QDialog>
+
+namespace Ui {
+ class LogInDialog;
+}
+
+class LogInDialog : public QDialog {
+ Q_OBJECT
+public:
+ LogInDialog(QWidget *parent = 0);
+ ~LogInDialog();
+
+ QString userName() const;
+ QString password() const;
+ bool rememberSettings() const;
+
+ void setUserName(const QString &userName);
+ void setPassword(const QString &password);
+ void setRememberSettings(bool on);
+
+protected:
+ void changeEvent(QEvent *e);
+
+private:
+ Ui::LogInDialog *ui;
+};
+
+#endif // LOGINDIALOG_H
diff --git a/logindialog.ui b/logindialog.ui
new file mode 100644
index 0000000..516ab43
--- /dev/null
+++ b/logindialog.ui
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>LogInDialog</class>
+ <widget class="QDialog" name="LogInDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>165</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Enter log in details (premium account)</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>User name:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="userNameLineEdit"/>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Password</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="passwordLineEdit">
+ <property name="echoMode">
+ <enum>QLineEdit::PasswordEchoOnEdit</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0" colspan="2">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QCheckBox" name="rememberMe">
+ <property name="text">
+ <string>Remember password (stored in clear text)</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>LogInDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>LogInDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..1522118
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,26 @@
+#include "qtspotifymain.h"
+
+#include "despotify_cpp.h"
+
+int main(int argc, char **argv)
+{
+ if (!despotify_init()) {
+ qWarning("Couldn't initialize despotify");
+ return 1;
+ }
+
+ QApplication app(argc, argv);
+
+ QtSpotifyMain qsm;
+ qsm.showMaximized();
+
+ int ret = app.exec();
+
+ if (!despotify_cleanup()) {
+ qWarning("Couldn't clean up despotify");
+ return 2;
+ }
+
+ return ret;
+}
+
diff --git a/qtspotify.pro b/qtspotify.pro
new file mode 100644
index 0000000..b284ff0
--- /dev/null
+++ b/qtspotify.pro
@@ -0,0 +1,9 @@
+HEADERS += qtspotifymain.h \
+ logindialog.h \
+ despotify_cpp.h
+SOURCES += qtspotifymain.cpp \
+ main.cpp \
+ logindialog.cpp
+FORMS += qtspotify.ui \
+ logindialog.ui
+LIBS += -ldespotify
diff --git a/qtspotify.ui b/qtspotify.ui
new file mode 100644
index 0000000..6f665b0
--- /dev/null
+++ b/qtspotify.ui
@@ -0,0 +1,236 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QtSpotifyMain</class>
+ <widget class="QMainWindow" name="QtSpotifyMain">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>340</width>
+ <height>680</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Qt Despotify Client</string>
+ </property>
+ <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>
+ </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>
+ </item>
+ <item row="2" column="0" colspan="3">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Search:</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>
+ </property>
+ <property name="toolTip">
+ <string>Select playlist or enter search term</string>
+ </property>
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QMenuBar" name="menubar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>340</width>
+ <height>24</height>
+ </rect>
+ </property>
+ <widget class="QMenu" name="menu_File">
+ <property name="title">
+ <string>&amp;File</string>
+ </property>
+ <addaction name="actionLogIn"/>
+ <addaction name="actionDebuggingOutput"/>
+ <addaction name="separator"/>
+ <addaction name="actionExit"/>
+ </widget>
+ <widget class="QMenu" name="menuPlaylist">
+ <property name="title">
+ <string>&amp;Playlist</string>
+ </property>
+ <addaction name="actionSearch"/>
+ <addaction name="actionRetrieveStoredPlaylists"/>
+ </widget>
+ <widget class="QMenu" name="menuPlayback">
+ <property name="title">
+ <string>Playback</string>
+ </property>
+ <addaction name="actionPlayOrStop"/>
+ <addaction name="actionPauseOrResume"/>
+ </widget>
+ <addaction name="menu_File"/>
+ <addaction name="menuPlaylist"/>
+ <addaction name="menuPlayback"/>
+ </widget>
+ <widget class="QStatusBar" name="statusbar"/>
+ <action name="actionLogIn">
+ <property name="text">
+ <string>&amp;Log in...</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+L</string>
+ </property>
+ </action>
+ <action name="actionExit">
+ <property name="text">
+ <string>E&amp;xit</string>
+ </property>
+ <property name="shortcut">
+ <string>Alt+X, Ctrl+Q</string>
+ </property>
+ </action>
+ <action name="actionSearch">
+ <property name="text">
+ <string>Search</string>
+ </property>
+ <property name="shortcut">
+ <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>
+ </property>
+ <property name="shortcut">
+ <string>Backspace</string>
+ </property>
+ </action>
+ <action name="actionPauseOrResume">
+ <property name="text">
+ <string>P&amp;ause / resume</string>
+ </property>
+ <property name="shortcut">
+ <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>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Debugging output</string>
+ </property>
+ </action>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>actionExit</sender>
+ <signal>triggered()</signal>
+ <receiver>QtSpotifyMain</receiver>
+ <slot>close()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>-1</x>
+ <y>-1</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>399</x>
+ <y>299</y>
+ </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
new file mode 100644
index 0000000..af8e2a2
--- /dev/null
+++ b/qtspotifymain.cpp
@@ -0,0 +1,431 @@
+#include "qtspotifymain.h"
+#include "logindialog.h"
+
+#include <QtCore/QtConcurrentRun>
+#include <QtCore/QFuture>
+#include <QtCore/QFutureWatcher>
+#include <QtCore/QStateMachine>
+#include <QtCore/QState>
+#include <QtCore/QSettings>
+
+#include <QtGui/QMenuBar>
+#include <QtGui/QMenu>
+#include <QtGui/QMessageBox>
+#include <QtGui/QGridLayout>
+#include <QtGui/QInputDialog>
+
+#include "despotify_cpp.h"
+
+Q_DECLARE_METATYPE(playlist *)
+Q_DECLARE_METATYPE(track *)
+
+QtSpotifyMain::QtSpotifyMain(QWidget *parent)
+ : QMainWindow(parent), m_session(0), m_searchResult(0), m_storedPlaylist(0),
+ m_machine(new QStateMachine), m_debugging(false),
+ m_authenticationWatcher(0), m_retrievingPlayListWatcher(0)
+{
+ initUi();
+ initWatchers();
+ initMachine();
+}
+
+QtSpotifyMain::~QtSpotifyMain()
+{
+ QSettings settings("qtspotify", "qtspotify");
+ settings.setValue(QString::fromLatin1("debuggingOutput"),
+ m_ui.actionDebuggingOutput->isChecked());
+
+ if (m_session != 0) {
+ endSession();
+ }
+
+}
+
+void QtSpotifyMain::initUi()
+{
+ m_ui.setupUi(this);
+
+ m_logInDialog = new LogInDialog(this);
+
+ QSettings settings("qtspotify", "qtspotify");
+
+ bool debuggingOutput = settings.value(QString::fromLatin1("debuggingOutput")).toBool();
+ setDebugging(debuggingOutput);
+ m_ui.actionDebuggingOutput->setChecked(debuggingOutput);
+ connect(m_ui.actionDebuggingOutput, SIGNAL(toggled(bool)), this, SLOT(setDebugging(bool)));
+
+ QString userName = settings.value(QString::fromLatin1("userName")).toString();
+ QString password = settings.value(QString::fromLatin1("password")).toString();
+
+ if (userName.isEmpty() || password.isEmpty()) {
+ m_logInDialog->setRememberSettings(false);
+ } else {
+ m_logInDialog->setRememberSettings(true);
+ m_logInDialog->setUserName(userName);
+ m_logInDialog->setPassword(password);
+ }
+
+ setStatusBar(new MyStatusBar(this));
+}
+
+void QtSpotifyMain::initWatchers()
+{
+ m_authenticationWatcher = new QFutureWatcher<bool>(this);
+ connect(m_authenticationWatcher, SIGNAL(finished()), this, SLOT(decideLoginResult()));
+
+ m_retrievingPlayListWatcher = new QFutureWatcher<playlist *>(this);
+ connect(m_retrievingPlayListWatcher, SIGNAL(finished()), this, SLOT(populateSearchBox()));
+}
+
+void QtSpotifyMain::retrievePlayLists()
+{
+ debug(tr("Retrieving playlists"));
+ m_ui.searchComboBox->clear();
+ m_ui.searchComboBox->clearEditText();
+
+ if (m_storedPlaylist != 0)
+ despotify_free_playlist(m_storedPlaylist);
+
+ QFuture<playlist *> retrieving = QtConcurrent::run(despotify_get_stored_playlists, m_session);
+ m_retrievingPlayListWatcher->setFuture(retrieving);
+}
+
+void QtSpotifyMain::selectPlayList()
+{
+ QString searchTerm = m_ui.searchComboBox->currentText();
+ if (m_searchResult != 0) {
+ despotify_free_search(m_searchResult);
+ m_searchResult = 0;
+ }
+
+ m_ui.playList->clear();
+
+ int idx = m_ui.searchComboBox->currentIndex();
+ if (idx >= 0) {
+ QVariant data = m_ui.searchComboBox->itemData(idx);
+ playlist *pl = data.value<playlist *>();
+ if (pl != 0) {
+ debug(tr("Selecting playlist '%1'").arg(QString::fromUtf8(pl->name)));
+ setPlayList(pl->tracks);
+ return;
+ }
+ }
+
+ m_searchResult = despotify_search(m_session, searchTerm.toUtf8().data(), 50);
+ if (m_searchResult != 0) {
+ debug(tr("Searching for '%1'").arg(searchTerm));
+ setPlayList(m_searchResult->tracks);
+ } else {
+ debug(tr("No results when searching for '%1'").arg(searchTerm));
+ }
+}
+
+void QtSpotifyMain::populateSearchBox()
+{
+ debug(tr("Populating search box"));
+ m_storedPlaylist = m_retrievingPlayListWatcher->result();
+ if (m_storedPlaylist == 0)
+ debug(tr("Cannot get stored playlists, error==%1").arg(QString::fromUtf8(m_session->last_error)));
+
+ playlist *pl = m_storedPlaylist;
+ while (pl != 0) {
+ m_ui.searchComboBox->addItem(QString::fromUtf8(pl->name), QVariant::fromValue(pl));
+ debug(tr("Adding '%1'").arg(QString::fromUtf8(pl->name)));
+ pl = pl->next;
+ }
+
+ m_ui.searchComboBox->setEditText(QString());
+}
+
+void QtSpotifyMain::stop()
+{
+ debug(tr("Stopping"));
+ despotify_stop(m_session);
+}
+
+void QtSpotifyMain::pause()
+{
+ debug(tr("Pausing"));
+ despotify_pause(m_session);
+}
+
+void QtSpotifyMain::resume()
+{
+ debug(tr("Resuming"));
+ despotify_resume(m_session);
+}
+
+void QtSpotifyMain::play()
+{
+ if (m_ui.playList->currentItem() == 0) {
+ qWarning("Nothing to play");
+ return ;
+ }
+
+ QVariant data = m_ui.playList->currentItem()->data(0, Qt::UserRole);
+ track *t = data.value<track *>();
+ if (t != 0) {
+ debug(tr("Playing '%1'").arg(t->title));
+ if (!despotify_play(m_session, t, false)) {
+ QMessageBox::information(this, tr("Error when playing track"),
+ tr("Despotify error: %1").arg(QString::fromUtf8(m_session->last_error)));
+ } else {
+ setNewTrack(t);
+ }
+ } else {
+ qWarning("No track connected to current item");
+ }
+}
+
+void QtSpotifyMain::initPlayingState(QState *playingState)
+{
+ QState *notPaused = new QState(playingState);
+ notPaused->setObjectName("notPaused");
+ 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()));
+
+ notPaused->addTransition(m_ui.actionPauseOrResume, SIGNAL(triggered()), paused);
+ paused->addTransition(m_ui.actionPauseOrResume, SIGNAL(triggered()), notPaused);
+}
+
+void QtSpotifyMain::decideLoginResult()
+{
+ if (m_authenticationWatcher->result()) {
+ debug(tr("Login succeeded"));
+ emit loggedIn();
+ } else {
+ debug(tr("Login failed, error==%1").arg(QString::fromUtf8(m_session->last_error)));
+ emit loginFailed();
+ }
+}
+
+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);
+
+ 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);
+
+ 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)),
+ playingState);
+
+}
+
+void QtSpotifyMain::initLoggedInState(QState *loggedIn)
+{
+ QState *playListHandlingState = new QState(loggedIn);
+ playListHandlingState->setObjectName("playListHandlingState");
+ initPlayListHandlingState(playListHandlingState);
+
+ QState *playBackHandlingState = new QState(loggedIn);
+ playBackHandlingState->setObjectName("playBackHandlingState");
+ initPlayBackHandlingState(playBackHandlingState);
+}
+
+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);
+ connect(requestedRetrieve, SIGNAL(entered()), this, SLOT(retrievePlayLists()));
+ requestedRetrieve->setObjectName("requestedRetrieve");
+
+ QState *playListSelected = new QState(playListHandlingState);
+ playListSelected->setObjectName("playListSelected");
+ connect(playListSelected, SIGNAL(entered()), this, SLOT(selectPlayList()));
+
+ idle->addTransition(m_ui.searchComboBox, SIGNAL(activated(QString)),
+ playListSelected);
+ idle->addTransition(m_ui.actionRetrieveStoredPlaylists, SIGNAL(triggered()),
+ requestedRetrieve);
+
+ playListSelected->addTransition(idle);
+
+ requestedRetrieve->addTransition(m_retrievingPlayListWatcher, SIGNAL(finished()), idle);
+}
+
+void QtSpotifyMain::debug(const QString &text)
+{
+ if (m_debugging)
+ qDebug() << 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);
+
+ QState *logInDialogAccepted = new QState(loggingInState);
+ 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);
+}
+
+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);
+
+ 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);
+
+ 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);
+
+ notLoggedInState->addTransition(m_ui.actionLogIn, SIGNAL(triggered()), loggingInState);
+ loggingInState->addTransition(m_logInDialog, SIGNAL(rejected()), notLoggedInState);
+ loggingInState->addTransition(this, SIGNAL(loginFailed()), notLoggedInState);
+ loggingInState->addTransition(this, SIGNAL(loggedIn()), loggedInState);
+
+ m_machine->start();
+}
+
+void QtSpotifyMain::setPlayList(track *t)
+{
+ debug(tr("Setting new playlist"));
+ m_ui.playList->clear();
+ while (t != 0) {
+ QTreeWidgetItem *w = new QTreeWidgetItem();
+
+ w->setData(0, Qt::DisplayRole, QString::fromUtf8(t->artist->name));
+ w->setData(1, Qt::DisplayRole, QString::fromUtf8(t->title));
+ w->setData(2, Qt::DisplayRole, QString::fromUtf8(t->album));
+ w->setData(0, Qt::UserRole, QVariant::fromValue(t));
+
+ m_ui.playList->addTopLevelItem(w);
+
+ t = t->next;
+ }
+}
+
+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);
+ qsm->setNewTrack(t);
+ }
+ };
+}
+
+void QtSpotifyMain::endSession()
+{
+ debug("Ending session");
+ if (m_storedPlaylist != 0)
+ despotify_free_playlist(m_storedPlaylist);
+
+ if (m_searchResult != 0) {
+ despotify_free_search(m_searchResult);
+ m_searchResult = 0;
+ }
+
+ m_ui.searchComboBox->clear();
+ despotify_exit(m_session);
+}
+
+void QtSpotifyMain::logIn()
+{
+ if (m_session == 0) {
+ m_session = despotify_init_client(callback, this);
+ if (m_session == 0) {
+ emit loginFailed();
+ return;
+ }
+ }
+
+ QString userName = m_logInDialog->userName();
+ QString password = m_logInDialog->password();
+
+ QSettings settings("qtspotify", "qtspotify");
+ if (m_logInDialog->rememberSettings()) {
+ debug("Saving settings");
+ settings.setValue(QString::fromLatin1("userName"), userName);
+ settings.setValue(QString::fromLatin1("password"), password);
+ } else {
+ debug("Clearing settings");
+ settings.remove(QString::fromLatin1("userName"));
+ settings.remove(QString::fromLatin1("password"));
+ }
+
+ debug(tr("Logging in as user '%1'").arg(userName));
+ QFuture<bool> authentication = QtConcurrent::run(despotify_authenticate,
+ m_session,
+ userName.toLocal8Bit(),
+ password.toLocal8Bit());
+ m_authenticationWatcher->setFuture(authentication);
+}
+
+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.songLabel->setText(QString::fromUtf8(t->title));
+}
diff --git a/qtspotifymain.h b/qtspotifymain.h
new file mode 100644
index 0000000..69f2bfb
--- /dev/null
+++ b/qtspotifymain.h
@@ -0,0 +1,106 @@
+#ifndef QTSPOTIFYMAIN_H
+#define QTSPOTIFYMAIN_H
+
+#include "ui_qtspotify.h"
+
+#include <QtGui/QMainWindow>
+
+#include <QtCore/QFuture>
+#include <QtCore/QFutureWatcher>
+
+class MyStatusBar: public QStatusBar
+{
+ Q_OBJECT
+ Q_PROPERTY(QString statusMessage READ statusMessage WRITE setStatusMessage);
+public:
+ MyStatusBar(QWidget *parent = 0) : QStatusBar(parent)
+ {
+ m_label = new QLabel();
+ addPermanentWidget(m_label);
+ }
+
+ void setStatusMessage(const QString &text)
+ {
+ m_label->setText(text);
+ }
+
+ QString statusMessage() const
+ {
+ return m_label->text();
+ }
+
+private:
+ QLabel *m_label;
+};
+
+struct despotify_session;
+struct track;
+struct playlist;
+struct search_result;
+
+class QStateMachine;
+class QState;
+
+class LogInDialog;
+class QtSpotifyMain: public QMainWindow
+{
+ Q_OBJECT
+ Q_PROPERTY(QString debuggingMessage READ debuggingMessage WRITE debug)
+public:
+ QtSpotifyMain(QWidget *parent = 0);
+ ~QtSpotifyMain();
+
+ void setNewTrack(track *t);
+
+ // Just a dummy so we can use the property mechanism to call the debug() function
+ QString debuggingMessage() const { return QString(); }
+
+ bool debugging() const { return m_debugging; }
+
+private slots:
+ void logIn();
+ void endSession();
+ void retrievePlayLists();
+ void populateSearchBox();
+ void play();
+ void pause();
+ void stop();
+ void resume();
+ void selectPlayList();
+ void decideLoginResult();
+ void debug(const QString &text);
+ void setDebugging(bool on) { m_debugging = on; }
+
+signals:
+ void loggedIn();
+ void loginFailed();
+
+private:
+ void setPlayList(track *t);
+
+ void initUi();
+ void initWatchers();
+
+ void initMachine();
+ void initLoggingInState(QState *);
+ void initLoggedInState(QState *);
+ void initPlayListHandlingState(QState *);
+ void initPlayBackHandlingState(QState *);
+ void initPlayingState(QState *);
+
+ Ui_QtSpotifyMain m_ui;
+ despotify_session *m_session;
+ search_result *m_searchResult;
+ playlist *m_storedPlaylist;
+ QStateMachine *m_machine;
+
+ bool m_debugging;
+
+ QFutureWatcher<bool> *m_authenticationWatcher;
+ QFutureWatcher<playlist *> *m_retrievingPlayListWatcher;
+
+ LogInDialog *m_logInDialog;
+
+};
+
+#endif // QTSPOTIFYMAIN_H