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