From 9430a8fd2f02db06e39c25131f86fa5bec528b47 Mon Sep 17 00:00:00 2001 From: David Sansome Date: Sat, 26 Dec 2009 15:13:38 +0000 Subject: [PATCH] Refactor more common stuff out of the library model, and add a simple last.fm service --- data/data.qrc | 7 ++- data/last.fm/as.png | Bin 0 -> 2523 bytes data/last.fm/loved_radio.png | Bin 0 -> 936 bytes data/last.fm/neighbour_radio.png | Bin 0 -> 637 bytes data/last.fm/personal_radio.png | Bin 0 -> 580 bytes data/last.fm/recommended_radio.png | Bin 0 -> 1040 bytes src/lastfmservice.cpp | 36 ++++++++++++ src/lastfmservice.h | 25 ++++++++ src/library.cpp | 46 +-------------- src/library.h | 17 ++---- src/mainwindow.cpp | 4 ++ src/mainwindow.h | 2 + src/mainwindow.ui | 36 +++++++++++- src/radioitem.cpp | 8 +++ src/radioitem.h | 24 ++++++++ src/radiomodel.cpp | 45 +++++++++++++++ src/radiomodel.h | 32 +++++++++++ src/radioservice.cpp | 7 +++ src/radioservice.h | 23 ++++++++ src/simpletreemodel.h | 89 +++++++++++++++++++++++++++++ src/src.pro | 13 ++++- 21 files changed, 354 insertions(+), 60 deletions(-) create mode 100644 data/last.fm/as.png create mode 100644 data/last.fm/loved_radio.png create mode 100644 data/last.fm/neighbour_radio.png create mode 100644 data/last.fm/personal_radio.png create mode 100644 data/last.fm/recommended_radio.png create mode 100644 src/lastfmservice.cpp create mode 100644 src/lastfmservice.h create mode 100644 src/radioitem.cpp create mode 100644 src/radioitem.h create mode 100644 src/radiomodel.cpp create mode 100644 src/radiomodel.h create mode 100644 src/radioservice.cpp create mode 100644 src/radioservice.h create mode 100644 src/simpletreemodel.h 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 0000000000000000000000000000000000000000..afa8c9dfe42a7c4f7801336708a27683ed8010bd GIT binary patch literal 2523 zcmV<12_*K3P)?tM$%k{31;`Vs>aK>}oB2?QyS33Q+cw8a4{qoY>a(v>b) zTV-?xb=rb6Wu^t`IJ6auN*9Cz1yLvn1SBkh6bO)z5VF7It@qun=l@>_iw=U^%s+YW zzW<){{pWn=JLd|vZR1X^>YNF(ts5nRL#~kg-f>dEmn(W)K0%ZuE0s!^-O&~^++AbE z5+Bm`U4<=G?RR2V_&fmB*%MUTG#4v*gO_^7j~VM8{Qw-<10iL&A;=Ol0gw$#(_nOV zLpxuOSk;Nnc=hQ`RwVY@!qy|_zc7Gf14IW zHX?}xMuy;WLh=$YMSknK09>ZAw%|iAqD+Np1X+lwQuj%>Mk!XY^`d!BLF)>6J9J@`os%L zX#tGpHdu*d+TpEEZ+BT1-!BIPu<5UD?c1l~M^1gUu;uXO+X8sL|CfL8!=LBq z@7K5^Oe{k5%z1SE?R^LdX5WA_7(~6-`A8ptiQaw{?#uw&n!Z}ug^kLT?2Fy95rt+KY7=C+i6=X7o@IDTM;Iwf?@KMF!j{wfZ6^)Zocds zBDx$7SGMX387zeHB!qw$nH7_vhkMZa$G6B@H%HB@rI%PD8D^nx-LM(di{P6v31&P2 zE0rd%;8I}_R~VTEiYSsAcg%ig%VcD9C{m`R4;)LBs9=O9EkEtf0(nMzMkRY>$XaqKiSG8(L$f_T+&BtANX z`lF}dAU^mj%Q=_W>e#&>^-nFQN}xzk^i)dY89f%4nLMjd-lYr|Vn&0{boJ{|DvzKByA8BnJb<^AVo63gvlacIj=njH|c@dcm?eQ z!b{xAm^LxZe%dDx04b1>t|C6T2>iZEsk~rnHJ1~SdJf=FylZbej2?wfDU-A2->u`} z%zyPc1i!Zgw#P+nm3jg!*s=(Th!tAB0)ysOz*ANPnMybOubrgCHU|IVI}nHF@e$3| ziKO)GfbTBh8H*4{Bm%Ayh!11!Tl=~pA#|?{75wGVp(E!-OsZ4LyHY#`xhsAOwO=NZ zRF2W=8Y)w|pOX#mn2}^iLAa_KEw8@;fjDB&Gya8hk@M7Iq)0ou4;t%hG)P#4ep$%;=3E4)PliI1 zO?vXOk+^snojdj*w)bNaH-VQYlU)YfHFXmDFPIJI;6eP+|9<7oE9X~Gq>`UImXjlI&=N~tDlLC;VdeBG z$fEh4y@c>l%)ytPx#AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy32;bRa{vGf6951U69E94oEQKA0})9?K~y-)rBZ81lTjG{ENwwa z{pzZHrfq4{vJ_D_oKn-Qwv|*A8j)0BK_r>vjdW3#q2#h=&E_q-c&Qd8rgKXuOS?F= z72c*(m-i4Ws3e}w`O;dyJMet(;eF2YocI0S!+5Z6j62KD(gg`cdQU->!JXejqW)o! zFx-6wJBvaW>>XU^wYrXQgM_Qs1+IzUIF@A?y16IzkrDE1S8fUht8SuOodKg8f9iqA z`@l`9U#QW8KiQd^jG>yl9QSmk;0NJih;}x;eO|3lVD3_-Vn|;B+XF3z^_A!?$b`{d z@ZGSAKhd6bg5y4NTm`!G&p_`bun_HhTEQX%UobgvI}cWE7Oa}{=+$JxcCP@o@@we7 zl}GkBxV)9*>6w8$S3ophNc&3|@^Pv5_3XE((qPR$3rkKiI!+(O`}lomJs5?Sm~D6? z3&raQF(Xw9w^YIuw@%6Nk%Ux*&OH0E2%UX}@g5_co+G3;7tk?vTLJIxn5R`@l zKpngu`N12wsNEHTB8eX^iM^|+mR7JZcI(FNq^C)9+#a-_JcO3La#Ti!;r_8W^uBnG z&%;B%F*-spj7~${&$PsZTXT6l5jB`Yeg;2VF22$fDMGI@0fqy+(Qy4L=;~>BhWgxW z7;`S*sagenW(Lf!UXD<%wzjr}8aR+r77zrMO8ty)cS_Ne5|4US4w{O!Xe=zic#j32 z2Kq48-G#yK&TnO9WshoVYVPQCIyJ*RBr;Ee#p{K_@N&sIlWFf3yiq3MVQL~LZoFNJ z`n+sBzIbk;xTvstY;4S$o}PY+VHi$411DnUDhUiwR7%#qYmJeknUq_ZoPgnu53rfr zrUv@^hek(7`xFYrVIGesCk76G45ptnkL=w-0|R#44O`c2ik2ZyDuz0G=eYW^s#>Gb zXq8H3nzOU>4l1TP`wA!=W=y<1JR(aX{0-%jwI;Xa%d#-Nh)_Nw{%gRp_K}iwGA<;F z2tvrX4%r#V{+#CwSUZD%;Y9LuItv{g9sQXf7{msNVKWqD01p<$3P1&y zU}7LPNDPZQ9*_Z|AO*8JXa5IbsA3HxdBYYb9hci63}S=Cu&5IS8-PWz0uXBhu{#ul z*a}$GfeqlnqFBgO*{sM+#r&GFqR|ag9u#&sM2PwkufKR-49b$fgJ+_K!VS0FJEXsBp-1JtVv55(HU z+W#O7G~jwmOUt6V^7{9^O}+p3Y~THV$CmBCu3x|Ybla9~Kl@tx{!eI|@E>T{TcBPe zkO2~|dhS;mQtSVNFo+Ela|?10eh$*yRp0f$s<7(agb5SYmgSYd+qY}a|B|$l|5oZY zw?KN~1~g?f{s&>G0eaRNwl`CPQ~vMXwhQEypQlfsez5CVld5Jy2Vh^+xq(45)yKiV_u zKMVlH%Yir=Nu3Zl8i7~@h~=Od#FmA*3YZp<4Twfl$A%*_qPVIYia~5$jNFJVi;{u? Xfq{dg$I$A600000NkvXXu0mjfG;Su{ literal 0 HcmV?d00001 diff --git a/data/last.fm/personal_radio.png b/data/last.fm/personal_radio.png new file mode 100644 index 0000000000000000000000000000000000000000..0f10eeaf8a976a8d1b5b7d99a979e221447f279d GIT binary patch literal 580 zcmV-K0=xZ*P)Px#AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy32;bRa{vGf6951U69E94oEQKA0j)_yK~y-)rIJfX0$~uxjWVI~ zK@jO7dT6Kc9GDP9Bt#D*A_^p^&JjUGN(+jb5>W)b4?!oTAkj1f1v>L+5pQMH1- zK4h?rUZl1168BEw)@+5%@tIGKEXyW* zKHt8_8NMf~G*M5ctg2c!)5X((% z?>e1sDVxo*nM|e#4u2Sp#s!H)GKS}Ceib}V%mP7KEEdl(472L>dW$=7I2`Lzsnl3H zED*d*nSh{-Jryzvd{HhR76^6~-9YdHWx3$5AE_D9BeOhuzNh7HCbSg=|KS_|yA2h! SZ8xI;0000 literal 0 HcmV?d00001 diff --git a/data/last.fm/recommended_radio.png b/data/last.fm/recommended_radio.png new file mode 100644 index 0000000000000000000000000000000000000000..0b7bdfcce641ff3ad5481618e17a45dd059325f1 GIT binary patch literal 1040 zcmV+r1n>KaP)Px#AY({UO#lFTB>(_`g8%^e{{R4h=>PzAFaQARU;qF* zm;eA5Z<1fdMgRZ+32;bRa{vGf6951U69E94oEQKA19?eAK~y*q1;I^h6jcDg@&B9m zW_EYFUAlD3whK$4)!GUaBx*y%q9G84iW-d&14b{RMlX60ZiEWK7?t2bj3QA8pa(C~ z#7GDvL@#ZsDa44i-?R%9>hx=OXXnj4e?Ot@#InwpzL+#7Ea;&0>8+cW&n$Qo^XY6` z@K67F@-5qFd;E2)@6t!{%!-ZKhEY#zIIz0?d7>ads1UoR3Keq@QsCb&;!XcMXiS6x0;!a#tFI%TOb}|HyFrCY z*>9EeGl4 z`M8WJinDWA(n$b934siCFmq?{W;8!NK5jcn=_ISyE_kD|9C(zqL`H6OZ=!!_Y#_91 zaxIPNY@6$*at{b$*(wZ!xxahAKT;{pVN5jrP{+OxlbNL5{_qC&zR<~oO*sG?V_I!V zX?SGJ`~1Y|@n8Bb-!_r91dsp{fb;0KgKw;CTXXcq?M>|6xrr;+1{fHcM7c2*XNuVR zzy6)uJFJ$LmJK~UJr}yVy1Erm0!r&PzuTN^e(de)Wr~^E1-?G_YcVR9?sRSK$lH#s zj4|knUO(_>e@~%MxURJ>0wu9#)0;@;*bPljoN`juzEpjh>5&oY?fZXx*1dn}g`ch# zE7)ze>)>qD zIL^q}knf@4rmCq?PT`@Q9);6qN1U#S&0PsJ92Z>D1u5$4J0000< KMNUMnLSTYAq||o+ literal 0 HcmV?d00001 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