diff --git a/data/data.qrc b/data/data.qrc
index 4354f222f..6ca9a0efe 100644
--- a/data/data.qrc
+++ b/data/data.qrc
@@ -1,5 +1,5 @@
-
+
clear.png
go-home.png
go-next.png
@@ -33,5 +33,10 @@
exit.png
copy.png
move.png
+ last.fm/as.png
+ last.fm/loved_radio.png
+ last.fm/neighbour_radio.png
+ last.fm/personal_radio.png
+ last.fm/recommended_radio.png
diff --git a/data/last.fm/as.png b/data/last.fm/as.png
new file mode 100644
index 000000000..afa8c9dfe
Binary files /dev/null and b/data/last.fm/as.png differ
diff --git a/data/last.fm/loved_radio.png b/data/last.fm/loved_radio.png
new file mode 100644
index 000000000..f231dbd32
Binary files /dev/null and b/data/last.fm/loved_radio.png differ
diff --git a/data/last.fm/neighbour_radio.png b/data/last.fm/neighbour_radio.png
new file mode 100644
index 000000000..28ab99a9a
Binary files /dev/null and b/data/last.fm/neighbour_radio.png differ
diff --git a/data/last.fm/personal_radio.png b/data/last.fm/personal_radio.png
new file mode 100644
index 000000000..0f10eeaf8
Binary files /dev/null and b/data/last.fm/personal_radio.png differ
diff --git a/data/last.fm/recommended_radio.png b/data/last.fm/recommended_radio.png
new file mode 100644
index 000000000..0b7bdfcce
Binary files /dev/null and b/data/last.fm/recommended_radio.png differ
diff --git a/src/lastfmservice.cpp b/src/lastfmservice.cpp
new file mode 100644
index 000000000..7c686eb62
--- /dev/null
+++ b/src/lastfmservice.cpp
@@ -0,0 +1,36 @@
+#include "lastfmservice.h"
+#include "radioitem.h"
+
+LastFMService::LastFMService(QObject* parent)
+ : RadioService("Last.fm", parent)
+{
+}
+
+RadioItem* LastFMService::CreateRootItem(RadioItem* parent) {
+ RadioItem* item = new RadioItem(this, RadioItem::Type_Service, "Last.fm", parent);
+ item->icon = QIcon(":last.fm/as.png");
+ return item;
+}
+
+void LastFMService::LazyPopulate(RadioItem *item) {
+ switch (item->type) {
+ case RadioItem::Type_Service:
+ CreateStationItem(Type_MyRecommendations, "My Recommendations",
+ ":last.fm/recommended_radio.png", item);
+ CreateStationItem(Type_MyRadio, "My Radio Station",
+ ":last.fm/personal_radio.png", item);
+ CreateStationItem(Type_MyLoved, "My Loved Tracks",
+ ":last.fm/loved_radio.png", item);
+ CreateStationItem(Type_MyNeighbourhood, "My Neighbourhood",
+ ":last.fm/neighbour_radio.png", item);
+ }
+}
+
+RadioItem* LastFMService::CreateStationItem(ItemType type, const QString& name,
+ const QString& icon, RadioItem* parent) {
+ RadioItem* ret = new RadioItem(this, type, name, parent);
+ ret->lazy_loaded = true;
+ ret->icon = QIcon(icon);
+
+ return ret;
+}
diff --git a/src/lastfmservice.h b/src/lastfmservice.h
new file mode 100644
index 000000000..c1e73a2b0
--- /dev/null
+++ b/src/lastfmservice.h
@@ -0,0 +1,25 @@
+#ifndef LASTFMSERVICE_H
+#define LASTFMSERVICE_H
+
+#include "radioservice.h"
+
+class LastFMService : public RadioService {
+ public:
+ LastFMService(QObject* parent = 0);
+
+ enum ItemType {
+ Type_MyRecommendations = 1000,
+ Type_MyRadio,
+ Type_MyLoved,
+ Type_MyNeighbourhood,
+ };
+
+ RadioItem* CreateRootItem(RadioItem* parent);
+ void LazyPopulate(RadioItem *item);
+
+ private:
+ RadioItem* CreateStationItem(ItemType type, const QString& name,
+ const QString& icon, RadioItem* parent);
+};
+
+#endif // LASTFMSERVICE_H
diff --git a/src/library.cpp b/src/library.cpp
index c1af818d5..0057e5004 100644
--- a/src/library.cpp
+++ b/src/library.cpp
@@ -11,11 +11,10 @@
Library::Library(EngineBase* engine, QObject* parent)
- : QAbstractItemModel(parent),
+ : SimpleTreeModel(new LibraryItem(LibraryItem::Type_Root), parent),
engine_(engine),
backend_(new BackgroundThread(this)),
watcher_(new BackgroundThread(this)),
- root_(new LibraryItem(LibraryItem::Type_Root)),
artist_icon_(":artist.png"),
album_icon_(":album.png"),
config_(new LibraryConfig)
@@ -280,22 +279,6 @@ void Library::SongsDeleted(const SongList& songs) {
}
}
-LibraryItem* Library::IndexToItem(const QModelIndex& index) const {
- if (!index.isValid())
- return root_;
- return reinterpret_cast(index.internalPointer());
-}
-
-QModelIndex Library::ItemToIndex(LibraryItem* item) const {
- if (!item || !item->parent)
- return QModelIndex();
- return createIndex(item->row, 0, item);
-}
-
-int Library::columnCount(const QModelIndex &) const {
- return 1;
-}
-
QVariant Library::data(const QModelIndex& index, int role) const {
const LibraryItem* item = IndexToItem(index);
@@ -334,33 +317,6 @@ QVariant Library::data(const LibraryItem* item, int role) const {
return QVariant();
}
-QModelIndex Library::index(int row, int, const QModelIndex& parent) const {
- LibraryItem* parent_item = IndexToItem(parent);
- if (!parent_item || parent_item->children.count() <= row)
- return QModelIndex();
-
- return ItemToIndex(parent_item->children[row]);
-}
-
-QModelIndex Library::parent(const QModelIndex& index) const {
- return ItemToIndex(IndexToItem(index)->parent);
-}
-
-int Library::rowCount(const QModelIndex & parent) const {
- LibraryItem* item = IndexToItem(parent);
- const_cast(this)->LazyPopulate(item); // Ahem
-
- return item->children.count();
-}
-
-bool Library::hasChildren(const QModelIndex &parent) const {
- LibraryItem* item = IndexToItem(parent);
- if (item->lazy_loaded)
- return !item->children.isEmpty();
- else
- return true;
-}
-
void Library::LazyPopulate(LibraryItem* item) {
if (item->lazy_loaded)
return;
diff --git a/src/library.h b/src/library.h
index 10dc6c099..82edc470b 100644
--- a/src/library.h
+++ b/src/library.h
@@ -10,11 +10,12 @@
#include "libraryquery.h"
#include "engine_fwd.h"
#include "song.h"
+#include "libraryitem.h"
+#include "simpletreemodel.h"
-class LibraryItem;
class LibraryConfig;
-class Library : public QAbstractItemModel {
+class Library : public SimpleTreeModel {
Q_OBJECT
public:
@@ -34,12 +35,7 @@ class Library : public QAbstractItemModel {
SongList GetChildSongs(const QModelIndex& index) const;
// QAbstractItemModel
- int columnCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
- QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const;
- QModelIndex parent(const QModelIndex & index) const;
- int rowCount(const QModelIndex & parent = QModelIndex()) const;
- bool hasChildren(const QModelIndex &parent) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
QStringList mimeTypes() const;
QMimeData* mimeData(const QModelIndexList& indexes) const;
@@ -54,6 +50,9 @@ class Library : public QAbstractItemModel {
void SetFilterAge(int age);
void SetFilterText(const QString& text);
+ protected:
+ void LazyPopulate(LibraryItem* item);
+
private slots:
// From LibraryBackend
void BackendInitialised();
@@ -66,7 +65,6 @@ class Library : public QAbstractItemModel {
private:
void Initialise();
- void LazyPopulate(LibraryItem* item);
LibraryItem* CreateCompilationArtistNode(bool signal);
LibraryItem* CreateArtistNode(bool signal, const QString& name);
@@ -79,8 +77,6 @@ class Library : public QAbstractItemModel {
QString SortTextForArtist(QString artist) const;
QString SortTextForAlbum(QString album) const;
- LibraryItem* IndexToItem(const QModelIndex& index) const;
- QModelIndex ItemToIndex(LibraryItem* item) const;
QVariant data(const LibraryItem* item, int role) const;
bool CompareItems(const LibraryItem* a, const LibraryItem* b) const;
@@ -94,7 +90,6 @@ class Library : public QAbstractItemModel {
QueryOptions query_options_;
- LibraryItem* root_;
QMap song_nodes_;
QMap artist_nodes_;
QMap divider_nodes_;
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 51a263ae4..9ff388e53 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -5,6 +5,7 @@
#include "libraryconfig.h"
#include "songplaylistitem.h"
#include "systemtrayicon.h"
+#include "radiomodel.h"
#include
#include
@@ -25,6 +26,7 @@ MainWindow::MainWindow(QWidget *parent)
playlist_(new Playlist(this)),
player_(new Player(playlist_, this)),
library_(new Library(player_->GetEngine(), this)),
+ radio_model_(new RadioModel(this)),
library_sort_model_(new QSortFilterProxyModel(this)),
tray_icon_(new SystemTrayIcon(this))
{
@@ -47,6 +49,8 @@ MainWindow::MainWindow(QWidget *parent)
ui_.library_view->setModel(library_sort_model_);
ui_.library_view->SetLibrary(library_);
+ ui_.radio_view->setModel(radio_model_);
+
// File view connections
connect(ui_.file_view, SIGNAL(Queue(QList)), SLOT(QueueFiles(QList)));
connect(ui_.file_view, SIGNAL(PathChanged(QString)), SLOT(FilePathChanged(QString)));
diff --git a/src/mainwindow.h b/src/mainwindow.h
index 48b6b33fc..4a4a67a63 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -10,6 +10,7 @@ class Playlist;
class Player;
class Library;
class LibraryConfig;
+class RadioModel;
class QSortFilterProxyModel;
class SystemTrayIcon;
@@ -57,6 +58,7 @@ class MainWindow : public QMainWindow {
Playlist* playlist_;
Player* player_;
Library* library_;
+ RadioModel* radio_model_;
QSortFilterProxyModel* library_sort_model_;
diff --git a/src/mainwindow.ui b/src/mainwindow.ui
index acd928d43..ba8e2b28f 100644
--- a/src/mainwindow.ui
+++ b/src/mainwindow.ui
@@ -303,7 +303,41 @@
1
-
+
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ true
+
+
+ QAbstractItemView::DragOnly
+
+
+ true
+
+
+ QAbstractItemView::ExtendedSelection
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+
+
+
diff --git a/src/radioitem.cpp b/src/radioitem.cpp
new file mode 100644
index 000000000..8b2923670
--- /dev/null
+++ b/src/radioitem.cpp
@@ -0,0 +1,8 @@
+#include "radioitem.h"
+
+RadioItem::RadioItem(RadioService* _service, int type, const QString& key,
+ RadioItem* parent)
+ : SimpleTreeItem(type, key, parent),
+ service(_service)
+{
+}
diff --git a/src/radioitem.h b/src/radioitem.h
new file mode 100644
index 000000000..c2a966dd9
--- /dev/null
+++ b/src/radioitem.h
@@ -0,0 +1,24 @@
+#ifndef RADIOITEM_H
+#define RADIOITEM_H
+
+#include
+
+#include "simpletreeitem.h"
+
+class RadioService;
+
+class RadioItem : public SimpleTreeItem {
+ public:
+ enum Type {
+ Type_Root = 1,
+ Type_Service,
+ };
+
+ RadioItem(RadioService* _service, int type, const QString& key = QString::null,
+ RadioItem* parent = NULL);
+
+ QIcon icon;
+ RadioService* service;
+};
+
+#endif // RADIOITEM_H
diff --git a/src/radiomodel.cpp b/src/radiomodel.cpp
new file mode 100644
index 000000000..1dc7ef4f2
--- /dev/null
+++ b/src/radiomodel.cpp
@@ -0,0 +1,45 @@
+#include "radiomodel.h"
+#include "radioservice.h"
+#include "lastfmservice.h"
+
+RadioModel::RadioModel(QObject* parent)
+ : SimpleTreeModel(new RadioItem(NULL, RadioItem::Type_Root), parent)
+{
+ root_->lazy_loaded = true;
+
+ LastFMService* lastfm = new LastFMService(this);
+ services_ << lastfm;
+ lastfm->CreateRootItem(root_);
+}
+
+QVariant RadioModel::data(const QModelIndex& index, int role) const {
+ const RadioItem* item = IndexToItem(index);
+
+ return data(item, role);
+}
+
+QVariant RadioModel::data(const RadioItem* item, int role) const {
+ switch (role) {
+ case Qt::DisplayRole:
+ return item->DisplayText();
+
+ case Qt::DecorationRole:
+ return item->icon;
+ break;
+
+ case Role_Type:
+ return item->type;
+
+ case Role_Key:
+ return item->key;
+
+ case Role_SortText:
+ return item->SortText();
+ }
+ return QVariant();
+}
+
+void RadioModel::LazyPopulate(RadioItem* parent) {
+ if (parent->service)
+ parent->service->LazyPopulate(parent);
+}
diff --git a/src/radiomodel.h b/src/radiomodel.h
new file mode 100644
index 000000000..10bc30feb
--- /dev/null
+++ b/src/radiomodel.h
@@ -0,0 +1,32 @@
+#ifndef RADIOMODEL_H
+#define RADIOMODEL_H
+
+#include "radioitem.h"
+#include "simpletreemodel.h"
+
+class RadioService;
+
+class RadioModel : public SimpleTreeModel {
+ public:
+ RadioModel(QObject* parent = 0);
+
+ enum {
+ Role_Type = Qt::UserRole + 1,
+ Role_SortText,
+ Role_Key,
+ };
+
+ // QAbstractItemModel
+ QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
+
+ protected:
+ void LazyPopulate(RadioItem* parent);
+
+ private:
+ QVariant data(const RadioItem* item, int role) const;
+
+ private:
+ QList services_;
+};
+
+#endif // RADIOMODEL_H
diff --git a/src/radioservice.cpp b/src/radioservice.cpp
new file mode 100644
index 000000000..b3560725f
--- /dev/null
+++ b/src/radioservice.cpp
@@ -0,0 +1,7 @@
+#include "radioservice.h"
+
+RadioService::RadioService(const QString& name, QObject *parent)
+ : QObject(parent),
+ name_(name)
+{
+}
diff --git a/src/radioservice.h b/src/radioservice.h
new file mode 100644
index 000000000..108864d8c
--- /dev/null
+++ b/src/radioservice.h
@@ -0,0 +1,23 @@
+#ifndef RADIOSERVICE_H
+#define RADIOSERVICE_H
+
+#include
+
+class RadioItem;
+
+class RadioService : public QObject {
+ Q_OBJECT
+
+ public:
+ RadioService(const QString& name, QObject* parent = 0);
+
+ QString name() const { return name_; }
+
+ virtual RadioItem* CreateRootItem(RadioItem* parent) = 0;
+ virtual void LazyPopulate(RadioItem* item) = 0;
+
+ private:
+ QString name_;
+};
+
+#endif // RADIOSERVICE_H
diff --git a/src/simpletreemodel.h b/src/simpletreemodel.h
new file mode 100644
index 000000000..22cd601e7
--- /dev/null
+++ b/src/simpletreemodel.h
@@ -0,0 +1,89 @@
+#ifndef SIMPLETREEMODEL_H
+#define SIMPLETREEMODEL_H
+
+#include
+
+class QModelIndex;
+
+template
+class SimpleTreeModel : public QAbstractItemModel {
+ public:
+ SimpleTreeModel(T* root = 0, QObject* parent = 0);
+ virtual ~SimpleTreeModel() {}
+
+ // QAbstractItemModel
+ int columnCount(const QModelIndex& parent) const;
+ QModelIndex index(int row, int, const QModelIndex& parent) const;
+ QModelIndex parent(const QModelIndex& index) const;
+ int rowCount(const QModelIndex& parent) const;
+ bool hasChildren(const QModelIndex& parent) const;
+
+ protected:
+ T* IndexToItem(const QModelIndex& index) const;
+ QModelIndex ItemToIndex(T* item) const;
+
+ virtual void LazyPopulate(T* item) = 0;
+
+ protected:
+ T* root_;
+};
+
+
+template
+SimpleTreeModel::SimpleTreeModel(T* root, QObject* parent)
+ : QAbstractItemModel(parent),
+ root_(root)
+{
+}
+
+template
+T* SimpleTreeModel::IndexToItem(const QModelIndex& index) const {
+ if (!index.isValid())
+ return root_;
+ return reinterpret_cast(index.internalPointer());
+}
+
+template
+QModelIndex SimpleTreeModel::ItemToIndex(T* item) const {
+ if (!item || !item->parent)
+ return QModelIndex();
+ return createIndex(item->row, 0, item);
+}
+
+template
+int SimpleTreeModel::columnCount(const QModelIndex &) const {
+ return 1;
+}
+
+template
+QModelIndex SimpleTreeModel::index(int row, int, const QModelIndex& parent) const {
+ T* parent_item = IndexToItem(parent);
+ if (!parent_item || parent_item->children.count() <= row)
+ return QModelIndex();
+
+ return ItemToIndex(parent_item->children[row]);
+}
+
+template
+QModelIndex SimpleTreeModel::parent(const QModelIndex& index) const {
+ return ItemToIndex(IndexToItem(index)->parent);
+}
+
+template
+int SimpleTreeModel::rowCount(const QModelIndex & parent) const {
+ T* item = IndexToItem(parent);
+ const_cast*>(this)->LazyPopulate(item); // Ahem
+
+ return item->children.count();
+}
+
+template
+bool SimpleTreeModel::hasChildren(const QModelIndex &parent) const {
+ T* item = IndexToItem(parent);
+ if (item->lazy_loaded)
+ return !item->children.isEmpty();
+ else
+ return true;
+}
+
+#endif // SIMPLETREEMODEL_H
diff --git a/src/src.pro b/src/src.pro
index 4b8bea9d6..42f00900d 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -32,7 +32,11 @@ SOURCES += main.cpp \
libraryquery.cpp \
fileview.cpp \
fileviewlist.cpp \
- playlistheader.cpp
+ playlistheader.cpp \
+ radioitem.cpp \
+ radioservice.cpp \
+ lastfmservice.cpp \
+ radiomodel.cpp
HEADERS += mainwindow.h \
player.h \
library.h \
@@ -63,7 +67,12 @@ HEADERS += mainwindow.h \
fileview.h \
fileviewlist.h \
playlistheader.h \
- simpletreeitem.h
+ simpletreeitem.h \
+ radioitem.h \
+ radioservice.h \
+ lastfmservice.h \
+ simpletreemodel.h \
+ radiomodel.h
FORMS += mainwindow.ui \
libraryconfig.ui \
fileview.ui