/src/sourcetree/items/collectionitem.cpp

http://github.com/tomahawk-player/tomahawk · C++ · 518 lines · 382 code · 98 blank · 38 comment · 63 complexity · 48e3d20a17454d5860d47a2df717cce1 MD5 · raw file

  1. /*
  2. * Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License along
  15. * with this program; if not, write to the Free Software Foundation, Inc.,
  16. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  17. */
  18. #include "collectionitem.h"
  19. #include "categoryitems.h"
  20. #include "playlistitems.h"
  21. #include "viewmanager.h"
  22. #include "playlist.h"
  23. #include "genericpageitems.h"
  24. #include "utils/tomahawkutils.h"
  25. #include "utils/logger.h"
  26. #include "widgets/SocialPlaylistWidget.h"
  27. #include "playlist/customplaylistview.h"
  28. #include "source.h"
  29. #include "temporarypageitem.h"
  30. #include <sourcelist.h>
  31. /// CollectionItem
  32. using namespace Tomahawk;
  33. CollectionItem::CollectionItem( SourcesModel* mdl, SourceTreeItem* parent, const Tomahawk::source_ptr& source )
  34. : SourceTreeItem( mdl, parent, SourcesModel::Collection )
  35. , m_source( source )
  36. , m_playlists( 0 )
  37. , m_stations( 0 )
  38. , m_latchedOn( false )
  39. , m_sourceInfoItem( 0 )
  40. , m_coolPlaylistsItem( 0 )
  41. , m_lovedTracksItem()
  42. , m_sourceInfoPage( 0 )
  43. , m_coolPlaylistsPage( 0 )
  44. , m_lovedTracksPage( 0 )
  45. , m_whatsHotPage( 0 )
  46. {
  47. m_lovedTracksItem = new GenericPageItem( model(), this, ( m_source.isNull() ? tr( "Top Loved Tracks" ) : tr( "Loved Tracks" ) ), QIcon( RESPATH "images/loved_playlist.png" ),
  48. boost::bind( &CollectionItem::lovedTracksClicked, this ),
  49. boost::bind( &CollectionItem::getLovedTracksPage, this ) );
  50. m_lovedTracksItem->setSortValue( -250 );
  51. if ( m_source.isNull() )
  52. {
  53. // super collection
  54. connect( ViewManager::instance(), SIGNAL( tempPageActivated( Tomahawk::ViewPage*) ), this, SLOT( tempPageActivated( Tomahawk::ViewPage* ) ) );
  55. // add misc children of root node
  56. GenericPageItem* recent = new GenericPageItem( model(), this, tr( "Dashboard" ), QIcon( RESPATH "images/dashboard.png" ),
  57. boost::bind( &ViewManager::showWelcomePage, ViewManager::instance() ),
  58. boost::bind( &ViewManager::welcomeWidget, ViewManager::instance() )
  59. );
  60. recent->setSortValue( -300 );
  61. GenericPageItem* hot = new GenericPageItem( model(), this, tr( "Charts" ), QIcon( RESPATH "images/charts.png" ),
  62. boost::bind( &ViewManager::showWhatsHotPage, ViewManager::instance() ),
  63. boost::bind( &ViewManager::whatsHotWidget, ViewManager::instance() )
  64. );
  65. hot->setSortValue( -300 );
  66. // TODO finish implementing and making pretty
  67. // m_coolPlaylistsItem = new GenericPageItem( model(), this, tr( "Cool Stuff" ), QIcon( RESPATH "images/new-additions.png" ),
  68. // boost::bind( &CollectionItem::coolPlaylistsClicked, this ),
  69. // boost::bind( &CollectionItem::getCoolPlaylistsPage, this )
  70. // );
  71. // m_coolPlaylistsItem->setSortValue( 200 );
  72. m_superCol = TomahawkUtils::createAvatarFrame( QPixmap( RESPATH "images/supercollection.png" ) );
  73. return;
  74. }
  75. m_sourceInfoItem = new GenericPageItem( model(), this, tr( "New Additions" ), QIcon( RESPATH "images/new-additions.png" ),
  76. boost::bind( &CollectionItem::sourceInfoClicked, this ),
  77. boost::bind( &CollectionItem::getSourceInfoPage, this ) );
  78. m_sourceInfoItem->setSortValue( -300 );
  79. // create category items if there are playlists to show, or stations to show
  80. QList< playlist_ptr > playlists = source->collection()->playlists();
  81. QList< dynplaylist_ptr > autoplaylists = source->collection()->autoPlaylists();
  82. QList< dynplaylist_ptr > stations = source->collection()->stations();
  83. if ( !playlists.isEmpty() || !autoplaylists.isEmpty() || source->isLocal() )
  84. {
  85. m_playlists = new CategoryItem( model(), this, SourcesModel::PlaylistsCategory, source->isLocal() );
  86. onPlaylistsAdded( playlists );
  87. onAutoPlaylistsAdded( autoplaylists );
  88. }
  89. if ( !stations.isEmpty() || source->isLocal() )
  90. {
  91. m_stations = new CategoryItem( model(), this, SourcesModel::StationsCategory, source->isLocal() );
  92. onStationsAdded( stations );
  93. }
  94. if( ViewManager::instance()->pageForCollection( source->collection() ) )
  95. model()->linkSourceItemToPage( this, ViewManager::instance()->pageForCollection( source->collection() ) );
  96. m_defaultAvatar = TomahawkUtils::createAvatarFrame( QPixmap( RESPATH "images/user-avatar.png" ) );
  97. // load auto playlists and stations!
  98. connect( source.data(), SIGNAL( stats( QVariantMap ) ), this, SIGNAL( updated() ) );
  99. connect( source.data(), SIGNAL( syncedWithDatabase() ), this, SIGNAL( updated() ) );
  100. connect( source.data(), SIGNAL( playbackStarted( Tomahawk::query_ptr ) ), this, SIGNAL( updated() ) );
  101. connect( source.data(), SIGNAL( stateChanged() ), this, SIGNAL( updated() ) );
  102. connect( source.data(), SIGNAL( offline() ), this, SIGNAL( updated() ) );
  103. connect( source.data(), SIGNAL( online() ), this, SIGNAL( updated() ) );
  104. connect( SourceList::instance(), SIGNAL( sourceLatchedOn( Tomahawk::source_ptr, Tomahawk::source_ptr ) ), this, SLOT( latchedOn( Tomahawk::source_ptr, Tomahawk::source_ptr ) ) );
  105. connect( SourceList::instance(), SIGNAL( sourceLatchedOff( Tomahawk::source_ptr, Tomahawk::source_ptr ) ), this, SLOT( latchedOff( Tomahawk::source_ptr, Tomahawk::source_ptr ) ) );
  106. connect( source->collection().data(), SIGNAL( playlistsAdded( QList<Tomahawk::playlist_ptr> ) ),
  107. SLOT( onPlaylistsAdded( QList<Tomahawk::playlist_ptr> ) ), Qt::QueuedConnection );
  108. connect( source->collection().data(), SIGNAL( autoPlaylistsAdded( QList< Tomahawk::dynplaylist_ptr > ) ),
  109. SLOT( onAutoPlaylistsAdded( QList<Tomahawk::dynplaylist_ptr> ) ), Qt::QueuedConnection );
  110. connect( source->collection().data(), SIGNAL( stationsAdded( QList<Tomahawk::dynplaylist_ptr> ) ),
  111. SLOT( onStationsAdded( QList<Tomahawk::dynplaylist_ptr> ) ), Qt::QueuedConnection );
  112. if ( m_source->isLocal() )
  113. QTimer::singleShot(0, this, SLOT(requestExpanding()));
  114. }
  115. Tomahawk::source_ptr
  116. CollectionItem::source() const
  117. {
  118. return m_source;
  119. }
  120. QString
  121. CollectionItem::text() const
  122. {
  123. return m_source.isNull() ? tr( "Super Collection" ) : m_source->friendlyName();
  124. }
  125. int
  126. CollectionItem::IDValue() const
  127. {
  128. if( m_source.isNull() )
  129. return -1;
  130. if( m_source->isLocal() )
  131. return 0;
  132. return m_source->id();
  133. }
  134. int
  135. CollectionItem::peerSortValue() const
  136. {
  137. if( m_source.isNull() )
  138. return -1;
  139. if( m_source->isLocal() )
  140. return 0;
  141. return 1;
  142. }
  143. void
  144. CollectionItem::activate()
  145. {
  146. ViewPage* p = 0;
  147. if ( source().isNull() )
  148. p = ViewManager::instance()->showSuperCollection();
  149. else
  150. p = ViewManager::instance()->show( source()->collection() );
  151. model()->linkSourceItemToPage( this, p );
  152. }
  153. QIcon
  154. CollectionItem::icon() const
  155. {
  156. if ( m_source.isNull() )
  157. return m_superCol;
  158. else
  159. {
  160. if( m_source->avatar().isNull() )
  161. return m_defaultAvatar;
  162. else
  163. return m_source->avatar( Source::FancyStyle );
  164. }
  165. }
  166. bool
  167. CollectionItem::localLatchedOn() const
  168. {
  169. // Don't show a listen icon if this is the local collection and we are latched on to someone who went offline
  170. // we are technically still latched on (if they come back online we'll be still listening along) but it's not visible
  171. // in the UI and confusing to the user why the red headphones are still there
  172. if ( !m_source.isNull() && m_source->isLocal() &&
  173. !m_latchedOnTo.isNull() && !m_latchedOnTo->isOnline() )
  174. return false;
  175. return m_latchedOn;
  176. }
  177. void
  178. CollectionItem::latchedOff( const source_ptr& from, const source_ptr& to )
  179. {
  180. if ( from->isLocal() && ( m_source == to || m_source == from ) )
  181. {
  182. m_latchedOn = false;
  183. m_latchedOnTo.clear();
  184. emit updated();
  185. }
  186. }
  187. void
  188. CollectionItem::latchedOn( const source_ptr& from, const source_ptr& to )
  189. {
  190. if ( from->isLocal() && ( m_source == to || m_source == from ) )
  191. {
  192. m_latchedOn = true;
  193. m_latchedOnTo = to;
  194. emit updated();
  195. }
  196. }
  197. void
  198. CollectionItem::playlistsAddedInternal( SourceTreeItem* parent, const QList< dynplaylist_ptr >& playlists )
  199. {
  200. QList< SourceTreeItem* > items;
  201. int addOffset = playlists.first()->author()->isLocal() ? 1 : 0;
  202. int from = parent->children().count() - addOffset;
  203. parent->beginRowsAdded( from, from + playlists.count() - 1 );
  204. foreach( const dynplaylist_ptr& p, playlists )
  205. {
  206. DynamicPlaylistItem* plItem = new DynamicPlaylistItem( model(), parent, p, parent->children().count() - addOffset );
  207. // qDebug() << "Dynamic Playlist added:" << p->title() << p->creator() << p->info();
  208. p->loadRevision();
  209. items << plItem;
  210. if( p->mode() == Static ) {
  211. if( m_source->isLocal() )
  212. connect( p.data(), SIGNAL( aboutToBeDeleted( Tomahawk::dynplaylist_ptr ) ),
  213. SLOT( onAutoPlaylistDeleted( Tomahawk::dynplaylist_ptr ) ), Qt::QueuedConnection );
  214. else
  215. connect( p.data(), SIGNAL( deleted( Tomahawk::dynplaylist_ptr ) ),
  216. SLOT( onAutoPlaylistDeleted( Tomahawk::dynplaylist_ptr ) ), Qt::QueuedConnection );
  217. } else {
  218. if( m_source->isLocal() )
  219. connect( p.data(), SIGNAL( aboutToBeDeleted( Tomahawk::dynplaylist_ptr ) ),
  220. SLOT( onStationDeleted( Tomahawk::dynplaylist_ptr ) ), Qt::QueuedConnection );
  221. else
  222. connect( p.data(), SIGNAL( deleted( Tomahawk::dynplaylist_ptr ) ),
  223. SLOT( onStationDeleted( Tomahawk::dynplaylist_ptr ) ), Qt::QueuedConnection );
  224. }
  225. }
  226. parent->endRowsAdded();
  227. }
  228. template< typename T >
  229. void
  230. CollectionItem::playlistDeletedInternal( SourceTreeItem* parent, const T& p )
  231. {
  232. Q_ASSERT( parent ); // How can we delete playlists if we have none?
  233. int curCount = parent->children().count();
  234. for( int i = 0; i < curCount; i++ )
  235. {
  236. PlaylistItem* pl = qobject_cast< PlaylistItem* >( parent->children().at( i ) );
  237. if( pl && pl->playlist() == p )
  238. {
  239. parent->beginRowsRemoved( i, i );
  240. parent->removeChild( pl );
  241. parent->endRowsRemoved();
  242. delete pl;
  243. break;
  244. }
  245. }
  246. if( ( parent == m_playlists || parent == m_stations ) &&
  247. parent->children().isEmpty() && parent->parent() ) // Don't leave an empty Playlist or Station category
  248. {
  249. int idx = parent->parent()->children().indexOf( parent );
  250. if( idx < 0 )
  251. return;
  252. parent->parent()->beginRowsRemoved( idx, idx );
  253. parent->parent()->removeChild( parent );
  254. parent->parent()->endRowsRemoved();
  255. if( parent == m_playlists )
  256. m_playlists = 0;
  257. else if( parent == m_stations )
  258. m_stations = 0;
  259. delete parent;
  260. }
  261. }
  262. void
  263. CollectionItem::onPlaylistsAdded( const QList< playlist_ptr >& playlists )
  264. {
  265. // qDebug() << Q_FUNC_INFO << m_source->friendlyName() << playlists.count();
  266. if( playlists.isEmpty() )
  267. return;
  268. if( !m_playlists )
  269. {
  270. // add the category too
  271. int cur = children().count();
  272. beginRowsAdded( cur, cur );
  273. m_playlists = new CategoryItem( model(), this, SourcesModel::PlaylistsCategory, source()->isLocal() );
  274. endRowsAdded();
  275. }
  276. QList< SourceTreeItem* > items;
  277. int addOffset = playlists.first()->author()->isLocal() ? 1 : 0;
  278. int from = m_playlists->children().count() - addOffset;
  279. m_playlists->beginRowsAdded( from, from + playlists.count() - 1 );
  280. foreach( const playlist_ptr& p, playlists )
  281. {
  282. PlaylistItem* plItem = new PlaylistItem( model(), m_playlists, p, m_playlists->children().count() - addOffset );
  283. // qDebug() << "Playlist added:" << p->title() << p->creator() << p->info();
  284. p->loadRevision();
  285. items << plItem;
  286. if( m_source->isLocal() )
  287. connect( p.data(), SIGNAL( aboutToBeDeleted( Tomahawk::playlist_ptr ) ),
  288. SLOT( onPlaylistDeleted( Tomahawk::playlist_ptr ) ), Qt::QueuedConnection );
  289. else
  290. connect( p.data(), SIGNAL( deleted( Tomahawk::playlist_ptr ) ),
  291. SLOT( onPlaylistDeleted( Tomahawk::playlist_ptr ) ), Qt::QueuedConnection );
  292. }
  293. m_playlists->endRowsAdded();
  294. }
  295. void
  296. CollectionItem::onPlaylistDeleted( const playlist_ptr& playlist )
  297. {
  298. playlistDeletedInternal( m_playlists, playlist );
  299. }
  300. void
  301. CollectionItem::onAutoPlaylistsAdded( const QList< dynplaylist_ptr >& playlists )
  302. {
  303. if( playlists.isEmpty() )
  304. return;
  305. if( !m_playlists )
  306. {
  307. // add the category too
  308. int cur = children().count();
  309. beginRowsAdded( cur, cur );
  310. m_playlists = new CategoryItem( model(), this, SourcesModel::PlaylistsCategory, source()->isLocal() );
  311. endRowsAdded();
  312. }
  313. playlistsAddedInternal( m_playlists, playlists );
  314. }
  315. void
  316. CollectionItem::onAutoPlaylistDeleted( const dynplaylist_ptr& playlist )
  317. {
  318. if( !m_playlists )
  319. qDebug() << "NO playlist category item for a deleting playlist..";
  320. playlistDeletedInternal( m_playlists, playlist );
  321. }
  322. void
  323. CollectionItem::onStationsAdded( const QList< dynplaylist_ptr >& stations )
  324. {
  325. if( stations.isEmpty() )
  326. return;
  327. if( !m_stations )
  328. {
  329. // add the category too
  330. int cur = children().count();
  331. beginRowsAdded( cur, cur );
  332. m_stations = new CategoryItem( model(), this, SourcesModel::StationsCategory, source()->isLocal() );
  333. endRowsAdded();
  334. }
  335. playlistsAddedInternal( m_stations, stations );
  336. }
  337. void
  338. CollectionItem::onStationDeleted( const dynplaylist_ptr& station )
  339. {
  340. playlistDeletedInternal( m_stations, station );
  341. }
  342. void
  343. CollectionItem::requestExpanding()
  344. {
  345. emit expandRequest(this);
  346. }
  347. void
  348. CollectionItem::tempPageActivated( Tomahawk::ViewPage* v )
  349. {
  350. const int idx = children().count();
  351. const int latest = children().last()->IDValue();
  352. foreach ( TemporaryPageItem* page, m_tempItems )
  353. {
  354. if ( page->page() == v )
  355. {
  356. emit selectRequest( page );
  357. return;
  358. }
  359. }
  360. // Only keep 5 temporary pages at once
  361. while ( m_tempItems.size() > 4 )
  362. {
  363. TemporaryPageItem* item = m_tempItems.takeFirst();
  364. QTimer::singleShot( 0, item, SLOT( removeFromList() ) );
  365. }
  366. emit beginRowsAdded( idx, idx );
  367. TemporaryPageItem* tempPage = new TemporaryPageItem( model(), this, v, latest + 1 );
  368. connect( tempPage, SIGNAL( removed() ), this, SLOT( temporaryPageDestroyed() ) );
  369. m_tempItems << tempPage;
  370. endRowsAdded();
  371. emit selectRequest( tempPage );
  372. }
  373. void
  374. CollectionItem::temporaryPageDestroyed()
  375. {
  376. TemporaryPageItem* tempPage = qobject_cast< TemporaryPageItem* >( sender() );
  377. Q_ASSERT( tempPage );
  378. m_tempItems.removeAll( tempPage );
  379. }
  380. ViewPage*
  381. CollectionItem::sourceInfoClicked()
  382. {
  383. if( m_source.isNull() )
  384. return 0;
  385. m_sourceInfoPage = ViewManager::instance()->show( m_source );
  386. return m_sourceInfoPage;
  387. }
  388. ViewPage*
  389. CollectionItem::getSourceInfoPage() const
  390. {
  391. return m_sourceInfoPage;
  392. }
  393. ViewPage*
  394. CollectionItem::coolPlaylistsClicked()
  395. {
  396. if( !m_source.isNull() )
  397. return 0;
  398. if( !m_coolPlaylistsPage )
  399. m_coolPlaylistsPage = new SocialPlaylistWidget( ViewManager::instance()->widget() );
  400. ViewManager::instance()->show( m_coolPlaylistsPage );
  401. return m_coolPlaylistsPage;
  402. }
  403. ViewPage*
  404. CollectionItem::getCoolPlaylistsPage() const
  405. {
  406. return m_coolPlaylistsPage;
  407. }
  408. ViewPage*
  409. CollectionItem::lovedTracksClicked()
  410. {
  411. if( !m_lovedTracksPage )
  412. m_lovedTracksPage = new CustomPlaylistView( m_source.isNull() ? CustomPlaylistView::AllLovedTracks : CustomPlaylistView::SourceLovedTracks, m_source, ViewManager::instance()->widget() );
  413. ViewManager::instance()->show( m_lovedTracksPage );
  414. return m_lovedTracksPage;
  415. }
  416. ViewPage*
  417. CollectionItem::getLovedTracksPage() const
  418. {
  419. return m_lovedTracksPage;
  420. }