diff options
-rw-r--r-- | qtspotify.pro | 11 | ||||
-rw-r--r-- | qtspotify.ui | 40 | ||||
-rw-r--r-- | qtspotifymain.cpp | 123 | ||||
-rw-r--r-- | qtspotifymain.h | 7 | ||||
-rw-r--r-- | searchdialog.cpp | 56 | ||||
-rw-r--r-- | searchdialog.h | 49 | ||||
-rw-r--r-- | searchdialog.ui | 74 | ||||
-rw-r--r-- | searchmodel.cpp | 62 | ||||
-rw-r--r-- | searchmodel.h | 51 |
9 files changed, 452 insertions, 21 deletions
diff --git a/qtspotify.pro b/qtspotify.pro index 711d738..061cbe5 100644 --- a/qtspotify.pro +++ b/qtspotify.pro @@ -3,13 +3,18 @@ HEADERS += qtspotifymain.h \ despotify_cpp.h \ qtplaylist.h \ storedplaylistmodel.h \ - coverdatabase.h + coverdatabase.h \ + searchdialog.h \ + searchmodel.h SOURCES += qtspotifymain.cpp \ main.cpp \ logindialog.cpp \ qtplaylist.cpp \ storedplaylistmodel.cpp \ - coverdatabase.cpp + coverdatabase.cpp \ + searchdialog.cpp \ + searchmodel.cpp FORMS += qtspotify.ui \ - logindialog.ui + logindialog.ui \ + searchdialog.ui LIBS += -ldespotify diff --git a/qtspotify.ui b/qtspotify.ui index 773af59..7be2d8e 100644 --- a/qtspotify.ui +++ b/qtspotify.ui @@ -18,7 +18,7 @@ <item row="0" column="0"> <widget class="QTabWidget" name="topLevelTabs"> <property name="currentIndex"> - <number>2</number> + <number>1</number> </property> <widget class="QWidget" name="tab"> <attribute name="title"> @@ -80,8 +80,35 @@ <string>Searches</string> </property> <layout class="QGridLayout" name="gridLayout_5"> - <item row="0" column="0"> - <widget class="QListView" name="listView"/> + <item row="0" column="0" colspan="2"> + <widget class="QListView" name="searchListView"/> + </item> + <item row="1" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QPushButton" name="searchMoreButton"> + <property name="text"> + <string>Search more</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="2" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout_2"/> </item> </layout> </widget> @@ -164,6 +191,13 @@ </spacer> </item> <item> + <widget class="QPushButton" name="previousButton"> + <property name="text"> + <string>Previous</string> + </property> + </widget> + </item> + <item> <widget class="QPushButton" name="playButton"> <property name="text"> <string>Play</string> diff --git a/qtspotifymain.cpp b/qtspotifymain.cpp index 0fb4750..9b9186f 100644 --- a/qtspotifymain.cpp +++ b/qtspotifymain.cpp @@ -27,6 +27,8 @@ #include "qtplaylist.h" #include "storedplaylistmodel.h" #include "coverdatabase.h" +#include "searchdialog.h" +#include "searchmodel.h" #include <QtCore/QtConcurrentRun> #include <QtCore/QFuture> @@ -99,7 +101,6 @@ QtSpotifyMain::~QtSpotifyMain() if (m_session != 0) { endSession(); } - } void QtSpotifyMain::initUi() @@ -128,9 +129,49 @@ void QtSpotifyMain::initUi() setStatusBar(new MyStatusBar(this)); + m_ui.searchListView->setModel(new SearchModel(m_session, m_ui.searchListView)); + m_coverDatabase = new CoverDatabase(m_session, this); connect(m_coverDatabase, SIGNAL(coverLoaded(QPixmap)), m_ui.coverArtLabel, SLOT(setPixmap(QPixmap))); + + connect(m_ui.actionSearch, SIGNAL(triggered()), this, SLOT(search())); + connect(m_ui.skipSongButton, SIGNAL(clicked()), this, SLOT(skip())); + connect(m_ui.previousButton, SIGNAL(clicked()), this, SLOT(previous())); + connect(m_ui.playList, SIGNAL(doubleClicked(QModelIndex)), + this, SIGNAL(playlistItemActivated())); +} + +void QtSpotifyMain::skip() +{ + QTreeWidgetItem *item = m_ui.playList->currentItem(); + if (item == 0) + return; + + int idx = m_ui.playList->indexOfTopLevelItem(item); + if (idx < 0) + return; + + if (++idx >= m_ui.playList->topLevelItemCount()) + return; + + m_ui.playList->setCurrentItem(m_ui.playList->topLevelItem(idx)); + + emit playlistItemActivated(); +} + +void QtSpotifyMain::previous() +{ + QTreeWidgetItem *item = m_ui.playList->currentItem(); + if (item == 0) + return; + + int idx = m_ui.playList->indexOfTopLevelItem(item) - 1; + if (idx < 0) + return; + + m_ui.playList->setCurrentItem(m_ui.playList->topLevelItem(idx)); + emit playlistItemActivated(); } void QtSpotifyMain::initWatchers() @@ -176,11 +217,11 @@ void QtSpotifyMain::retrievePlayLists() m_retrievingPlayListWatcher->setFuture(retrieving); } -void QtSpotifyMain::selectPlayList() +void QtSpotifyMain::selectSearchOrPlaylist(QListView *listView) { - m_ui.playList->clear(); + m_ui.playList->clear(); - QModelIndex idx = m_ui.storedPlaylists->currentIndex(); + QModelIndex idx = listView->currentIndex(); if (idx.isValid()) { QVariant data = idx.data(Qt::UserRole); QtPlaylist *pl = data.value<QtPlaylist *>(); @@ -189,7 +230,20 @@ void QtSpotifyMain::selectPlayList() setPlaylist(pl->tracks()); return; } + } else { + debug(tr("Selected playlist index is not valid")); } + +} + +void QtSpotifyMain::selectPlayList() +{ + selectSearchOrPlaylist(m_ui.storedPlaylists); +} + +void QtSpotifyMain::selectSearch() +{ + selectSearchOrPlaylist(m_ui.searchListView); } void QtSpotifyMain::populateSearchBox() @@ -202,6 +256,23 @@ void QtSpotifyMain::populateSearchBox() m_ui.storedPlaylists->setModel(new StoredPlaylistModel(m_session, rootPlaylist, m_ui.storedPlaylists)); } +void QtSpotifyMain::search() +{ + SearchDialog searchDialog; + if (searchDialog.exec() == QDialog::Accepted) { + QString searchTerm = searchDialog.searchTerm(); + if (!searchTerm.isEmpty()) { + QAbstractItemModel *model = m_ui.searchListView->model(); + SearchModel *searchModel = qobject_cast<SearchModel *>(model); + if (searchModel != 0) { + searchModel->addSearch(searchTerm); + m_ui.searchListView->setCurrentIndex(searchModel->index(searchModel->rowCount()-1)); + emit searched(); + } + } + } +} + void QtSpotifyMain::stop() { debug(tr("Stopping")); @@ -233,8 +304,8 @@ void QtSpotifyMain::play() 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)) { + debug(tr("Playing '%1'").arg(t->title)); + if (!despotify_play(m_session, t, true)) { QMessageBox::information(this, tr("Error when playing track"), tr("Despotify error: %1").arg(QString::fromUtf8(m_session->last_error))); } else { @@ -290,6 +361,7 @@ void QtSpotifyMain::initPlayBackHandlingState(QState *playBackHandlingState) stoppedState->assignProperty(m_ui.coverArtLabel, "text", QString()); stoppedState->assignProperty(m_ui.playButton, "enabled", true); stoppedState->assignProperty(m_ui.skipSongButton, "enabled", false); + stoppedState->assignProperty(m_ui.previousButton, "enabled", false); stoppedState->setObjectName("stoppedState"); connect(stoppedState, SIGNAL(entered()), this, SLOT(stop())); playBackHandlingState->setInitialState(stoppedState); @@ -302,13 +374,14 @@ void QtSpotifyMain::initPlayBackHandlingState(QState *playBackHandlingState) connect(playingState, SIGNAL(entered()), this, SLOT(play())); playingState->assignProperty(m_ui.playButton, "enabled", true); playingState->assignProperty(m_ui.skipSongButton, "enabled", true); + playingState->assignProperty(m_ui.previousButton, "enabled", false); initPlayingState(playingState); } - stoppedState->addTransition(m_ui.playList, SIGNAL(doubleClicked(QModelIndex)), - playingState); - playingState->addTransition(m_ui.playList, SIGNAL(doubleClicked(QModelIndex)), - playingState); + stoppedState->addTransition(this, SIGNAL(playlistItemActivated()), + playingState); + playingState->addTransition(this, SIGNAL(playlistItemActivated()), + playingState); } void QtSpotifyMain::initLoggedInState(QState *loggedIn) @@ -331,6 +404,9 @@ void QtSpotifyMain::initIdleState(QState *idle) QState *requestedRetrieve = new QState(idle); { requestedRetrieve->assignProperty(m_ui.storedPlaylists, "enabled", false); + requestedRetrieve->assignProperty(m_ui.searchListView, "enabled", false); + requestedRetrieve->assignProperty(m_ui.actionSearch, "enabled", false); + requestedRetrieve->assignProperty(statusBar(), "statusMessage", tr("Retrieving playlists, please wait...")); connect(requestedRetrieve, SIGNAL(entered()), this, SLOT(retrievePlayLists())); requestedRetrieve->setObjectName("requestedRetrieve"); } @@ -344,6 +420,9 @@ void QtSpotifyMain::initIdleState(QState *idle) QState *retrieved = new QState(idle); { retrieved->assignProperty(m_ui.storedPlaylists, "enabled", true); + retrieved->assignProperty(m_ui.searchListView, "enabled", true); + retrieved->assignProperty(m_ui.actionSearch, "enabled", true); + retrieved->assignProperty(statusBar(), "statusMessage", tr("Go ahead")); retrieved->setObjectName("retrieved"); } @@ -358,7 +437,6 @@ void QtSpotifyMain::initPlayListHandlingState(QState *playListHandlingState) idle->assignProperty(m_ui.actionSearch, "enabled", true); idle->assignProperty(m_ui.playList, "enabled", true); - idle->assignProperty(statusBar(), "statusMessage", tr("Go ahead...")); initIdleState(idle); playListHandlingState->setInitialState(idle); @@ -371,9 +449,19 @@ void QtSpotifyMain::initPlayListHandlingState(QState *playListHandlingState) connect(playListSelected, SIGNAL(entered()), this, SLOT(selectPlayList())); } + QState *searchSelected = new QState(playListHandlingState); + { + searchSelected->setObjectName("searchSelected"); + searchSelected->assignProperty(statusBar(), "statusMessage", tr("Searching...")); + connect(searchSelected, SIGNAL(entered()), this, SLOT(selectSearch())); + } + idle->addTransition(m_ui.storedPlaylists, SIGNAL(activated(QModelIndex)), playListSelected); - playListSelected->addTransition(idle); + idle->addTransition(m_ui.searchListView, SIGNAL(activated(QModelIndex)), searchSelected); + idle->addTransition(this, SIGNAL(searched()), searchSelected); + playListSelected->addTransition(idle); + searchSelected->addTransition(idle); } @@ -414,9 +502,7 @@ void QtSpotifyMain::initMachine() 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); + notLoggedInState->setObjectName("notLoggedInState"); } QState *loggingInState = new QState(m_machine); @@ -429,6 +515,7 @@ void QtSpotifyMain::initMachine() loggingInState->assignProperty(this, "debuggingMessage", tr("Entered 'loggingInState'")); initLoggingInState(loggingInState); + m_machine->setInitialState(loggingInState); } QState *loggedInState = new QState(QState::ParallelStates, m_machine); @@ -462,6 +549,12 @@ void QtSpotifyMain::setPlaylist(QList<track *> tracks) w->setData(2, Qt::DisplayRole, QString::fromUtf8(t->album)); w->setData(0, Qt::UserRole, QVariant::fromValue(t)); + QFont font = m_ui.playList->font(); + font.setUnderline(true); + + w->setFont(0, font); + w->setFont(2, font); + m_ui.playList->addTopLevelItem(w); } } diff --git a/qtspotifymain.h b/qtspotifymain.h index f4b11b0..4078179 100644 --- a/qtspotifymain.h +++ b/qtspotifymain.h @@ -95,13 +95,19 @@ private slots: void stop(); void resume(); void selectPlayList(); + void selectSearch(); void decideLoginResult(); void debug(const QString &text); void setDebugging(bool on) { m_debugging = on; } + void search(); + void skip(); + void previous(); signals: void loggedIn(); void loginFailed(); + void searched(); + void playlistItemActivated(); private: void setPlaylist(QList<track *> tracks); @@ -118,6 +124,7 @@ private: void initIdleState(QState *); void setNewTrack(track *t); void selectTrack(track *t); + void selectSearchOrPlaylist(QListView *listView); Ui_QtSpotifyMain m_ui; despotify_session *m_session; diff --git a/searchdialog.cpp b/searchdialog.cpp new file mode 100644 index 0000000..39b5314 --- /dev/null +++ b/searchdialog.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** 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 "searchdialog.h" +#include "ui_searchdialog.h" + +SearchDialog::SearchDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::SearchDialog) +{ + ui->setupUi(this); +} + +SearchDialog::~SearchDialog() +{ + delete ui; +} + +void SearchDialog::changeEvent(QEvent *e) +{ + QDialog::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + +QString SearchDialog::searchTerm() const +{ + return ui->searchLineEdit->text(); +} + diff --git a/searchdialog.h b/searchdialog.h new file mode 100644 index 0000000..a264163 --- /dev/null +++ b/searchdialog.h @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** 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 SEARCHDIALOG_H +#define SEARCHDIALOG_H + +#include <QDialog> + +namespace Ui { + class SearchDialog; +} + +class SearchDialog : public QDialog { + Q_OBJECT +public: + SearchDialog(QWidget *parent = 0); + ~SearchDialog(); + + QString searchTerm() const; + +protected: + void changeEvent(QEvent *e); + +private: + Ui::SearchDialog *ui; +}; + +#endif // SEARCHDIALOG_H diff --git a/searchdialog.ui b/searchdialog.ui new file mode 100644 index 0000000..d154e0d --- /dev/null +++ b/searchdialog.ui @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>SearchDialog</class> + <widget class="QDialog" name="SearchDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>83</height> + </rect> + </property> + <property name="windowTitle"> + <string>Search</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Search for:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="searchLineEdit"/> + </item> + <item row="1" 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> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>SearchDialog</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>SearchDialog</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/searchmodel.cpp b/searchmodel.cpp new file mode 100644 index 0000000..4988c56 --- /dev/null +++ b/searchmodel.cpp @@ -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$ +** +****************************************************************************/ +#include "searchmodel.h" +#include "qtplaylist.h" + +SearchModel::SearchModel(despotify_session *session, QObject *parent) + : QAbstractListModel(parent), m_session(session) +{ +} + +int SearchModel::rowCount(const QModelIndex &parent) const +{ + Q_ASSERT(!parent.isValid()); + return m_playlists.size(); +} + +QVariant SearchModel::data(const QModelIndex &index, int role) const +{ + Q_ASSERT(index.column() == 0); + if (role == Qt::DisplayRole) + return m_playlists.at(index.row())->name(); + else if (role == Qt::UserRole) + return QVariant::fromValue(m_playlists.at(index.row())); + + return QVariant(); +} + +void SearchModel::addSearch(const QString &searchTerm) +{ + beginInsertRows(QModelIndex(), m_playlists.size(), m_playlists.size()); + + QtPlaylist *playlist = new QtPlaylist(m_session, this); + playlist->setSearchTerm(searchTerm); + m_playlists.append(playlist); + + endInsertRows(); + + emit searchAdded(searchTerm); +} + diff --git a/searchmodel.h b/searchmodel.h new file mode 100644 index 0000000..856b37e --- /dev/null +++ b/searchmodel.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 SEARCHMODEL_H +#define SEARCHMODEL_H + +#include <QAbstractListModel> + +struct despotify_session; +class QtPlaylist; +class SearchModel: public QAbstractListModel +{ + Q_OBJECT +public: + SearchModel(despotify_session *session, QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + + void addSearch(const QString &searchTerm); + +signals: + void searchAdded(const QString &searchTerm); + +private: + QList<QtPlaylist *> m_playlists; + despotify_session *m_session; +}; + +#endif // SEARCHMODEL_H |