/src/libtomahawk/ViewManager.cpp

http://github.com/tomahawk-player/tomahawk · C++ · 707 lines · 508 code · 162 blank · 37 comment · 77 complexity · 92e9e2435e1803eff8087c83c7f85132 MD5 · raw file

  1. /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  2. *
  3. * Copyright 2010-2015, Christian Muehlhaeuser <muesli@tomahawk-player.org>
  4. * Copyright 2010-2011, Jeff Mitchell <jeff@tomahawk-player.org>
  5. * Copyright 2010-2012, Leo Franchi <lfranchi@kde.org>
  6. * Copyright 2013, Teo Mrnjavac <teo@kde.org>
  7. *
  8. * Tomahawk is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * Tomahawk is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. #include "ViewManager.h"
  22. #include "SourceList.h"
  23. #include "TomahawkSettings.h"
  24. #include "audio/AudioEngine.h"
  25. #include "playlist/ContextView.h"
  26. #include "playlist/TreeModel.h"
  27. #include "playlist/PlaylistModel.h"
  28. #include "playlist/TrackView.h"
  29. #include "playlist/PlayableProxyModel.h"
  30. #include "playlist/PlayableModel.h"
  31. #include "playlist/ColumnView.h"
  32. #include "playlist/GridView.h"
  33. #include "playlist/AlbumModel.h"
  34. #include "playlist/InboxModel.h"
  35. #include "playlist/InboxView.h"
  36. #include "playlist/TrackItemDelegate.h"
  37. #include "playlist/RecentlyPlayedModel.h"
  38. #include "playlist/dynamic/widgets/DynamicWidget.h"
  39. #include "viewpages/PlaylistViewPage.h"
  40. #include "viewpages/SourceViewPage.h"
  41. #include "viewpages/ArtistViewPage.h"
  42. #include "viewpages/AlbumViewPage.h"
  43. #include "viewpages/TrackViewPage.h"
  44. #include "viewpages/CollectionViewPage.h"
  45. #include "utils/Logger.h"
  46. #include "utils/TomahawkUtilsGui.h"
  47. #include <QVBoxLayout>
  48. #include <QMetaMethod>
  49. using namespace Tomahawk;
  50. ViewManager* ViewManager::s_instance = 0;
  51. ViewManager*
  52. ViewManager::instance()
  53. {
  54. return s_instance;
  55. }
  56. ViewManager::ViewManager( QObject* parent )
  57. : QObject( parent )
  58. , m_widget( new QWidget() )
  59. , m_queue( 0 )
  60. , m_inboxWidget( 0 )
  61. , m_currentPage( 0 )
  62. {
  63. s_instance = this;
  64. m_widget->setLayout( new QVBoxLayout() );
  65. m_stack = new QStackedWidget();
  66. m_inboxModel = new InboxModel( this );
  67. m_inboxModel->setTitle( tr( "Inbox" ) );
  68. m_inboxModel->setDescription( tr( "Listening suggestions from your friends" ) );
  69. m_inboxModel->setIcon( TomahawkUtils::defaultPixmap( TomahawkUtils::Inbox ) );
  70. m_widget->layout()->addWidget( m_stack );
  71. m_stack->setContentsMargins( 0, 0, 0, 0 );
  72. m_widget->setContentsMargins( 0, 0, 0, 0 );
  73. m_widget->layout()->setContentsMargins( 0, 0, 0, 0 );
  74. m_widget->layout()->setMargin( 0 );
  75. m_widget->layout()->setSpacing( 0 );
  76. connect( AudioEngine::instance(), SIGNAL( playlistChanged( Tomahawk::playlistinterface_ptr ) ), this, SLOT( playlistInterfaceChanged( Tomahawk::playlistinterface_ptr ) ) );
  77. }
  78. ViewManager::~ViewManager()
  79. {
  80. delete m_inboxWidget;
  81. delete m_widget;
  82. }
  83. PlaylistViewPage*
  84. ViewManager::createPageForPlaylist( const playlist_ptr& playlist )
  85. {
  86. PlaylistViewPage* view = new PlaylistViewPage();
  87. PlaylistModel* model = new PlaylistModel();
  88. // We need to set the model on the view before loading the playlist, so spinners & co are connected
  89. view->view()->trackView()->setPlayableModel( model );
  90. model->loadPlaylist( playlist );
  91. playlist->resolve();
  92. return view;
  93. }
  94. PlaylistViewPage*
  95. ViewManager::createPageForList( const QString& title, const QList< query_ptr >& queries )
  96. {
  97. PlaylistViewPage* view = new PlaylistViewPage();
  98. PlaylistModel* model = new PlaylistModel();
  99. view->setTemporaryPage( true );
  100. // We need to set the model on the view before loading the playlist, so spinners & co are connected
  101. view->view()->trackView()->setPlayableModel( model );
  102. model->setTitle( title );
  103. model->appendQueries( queries );
  104. return view;
  105. }
  106. playlist_ptr
  107. ViewManager::playlistForPage( ViewPage* page ) const
  108. {
  109. playlist_ptr p;
  110. PlaylistViewPage* fv = dynamic_cast< PlaylistViewPage* >( page );
  111. if ( fv && fv->view()->trackView()->model() )
  112. {
  113. PlaylistModel* m = dynamic_cast< PlaylistModel* >( fv->view()->trackView()->model() );
  114. if ( m && m->playlist() )
  115. {
  116. p = m->playlist();
  117. }
  118. }
  119. else if ( dynamic_cast< DynamicWidget* >( page ) )
  120. p = dynamic_cast< DynamicWidget* >( page )->playlist();
  121. return p;
  122. }
  123. Tomahawk::ViewPage*
  124. ViewManager::show( const Tomahawk::playlist_ptr& playlist )
  125. {
  126. if ( !playlist->loaded() )
  127. playlist->loadRevision();
  128. PlaylistViewPage* view;
  129. if ( !m_playlistViews.contains( playlist ) || m_playlistViews.value( playlist ).isNull() )
  130. {
  131. view = createPageForPlaylist( playlist );
  132. m_playlistViews.insert( playlist, view );
  133. }
  134. else
  135. {
  136. view = m_playlistViews.value( playlist ).data();
  137. }
  138. setPage( view );
  139. return view;
  140. }
  141. Tomahawk::ViewPage*
  142. ViewManager::show( const Tomahawk::dynplaylist_ptr& playlist )
  143. {
  144. if ( !m_dynamicWidgets.contains( playlist ) || m_dynamicWidgets.value( playlist ).isNull() )
  145. {
  146. m_dynamicWidgets[ playlist ] = new Tomahawk::DynamicWidget( playlist, m_stack );
  147. playlist->resolve();
  148. }
  149. setPage( m_dynamicWidgets.value( playlist ).data() );
  150. return m_dynamicWidgets.value( playlist ).data();
  151. }
  152. Tomahawk::ViewPage*
  153. ViewManager::show( const Tomahawk::artist_ptr& artist )
  154. {
  155. ArtistInfoWidget* swidget;
  156. if ( !m_artistViews.contains( artist ) || m_artistViews.value( artist ).isNull() )
  157. {
  158. swidget = new ArtistInfoWidget( artist );
  159. m_artistViews.insert( artist, swidget );
  160. }
  161. else
  162. {
  163. swidget = m_artistViews.value( artist ).data();
  164. }
  165. setPage( swidget );
  166. return swidget;
  167. }
  168. Tomahawk::ViewPage*
  169. ViewManager::show( const Tomahawk::album_ptr& album )
  170. {
  171. AlbumInfoWidget* swidget;
  172. if ( !m_albumViews.contains( album ) || m_albumViews.value( album ).isNull() )
  173. {
  174. swidget = new AlbumInfoWidget( album );
  175. m_albumViews.insert( album, swidget );
  176. }
  177. else
  178. {
  179. swidget = m_albumViews.value( album ).data();
  180. }
  181. setPage( swidget );
  182. return swidget;
  183. }
  184. Tomahawk::ViewPage*
  185. ViewManager::show( const Tomahawk::query_ptr& query )
  186. {
  187. TrackInfoWidget* swidget;
  188. if ( !m_trackViews.contains( query ) || m_trackViews.value( query ).isNull() )
  189. {
  190. swidget = new TrackInfoWidget( query );
  191. m_trackViews.insert( query, swidget );
  192. }
  193. else
  194. {
  195. swidget = m_trackViews.value( query ).data();
  196. }
  197. setPage( swidget );
  198. return swidget;
  199. }
  200. Tomahawk::ViewPage*
  201. ViewManager::show( const Tomahawk::collection_ptr& collection )
  202. {
  203. m_currentCollection = collection;
  204. CollectionViewPage* view;
  205. if ( !m_collectionViews.contains( collection ) || m_collectionViews.value( collection ).isNull() )
  206. {
  207. view = new CollectionViewPage( collection );
  208. setPage( view );
  209. m_collectionViews.insert( collection, view );
  210. }
  211. else
  212. {
  213. view = m_collectionViews.value( collection ).data();
  214. }
  215. view->restoreViewMode();
  216. setPage( view );
  217. return view;
  218. }
  219. Tomahawk::ViewPage*
  220. ViewManager::show( const Tomahawk::source_ptr& source )
  221. {
  222. SourceInfoWidget* swidget;
  223. if ( !m_sourceViews.contains( source ) || m_sourceViews.value( source ).isNull() )
  224. {
  225. swidget = new SourceInfoWidget( source );
  226. m_sourceViews.insert( source, swidget );
  227. }
  228. else
  229. {
  230. swidget = m_sourceViews.value( source ).data();
  231. }
  232. setPage( swidget );
  233. return swidget;
  234. }
  235. Tomahawk::ViewPage*
  236. ViewManager::show( ViewPage* page )
  237. {
  238. setPage( page );
  239. return page;
  240. }
  241. void
  242. ViewManager::playlistInterfaceChanged( Tomahawk::playlistinterface_ptr interface )
  243. {
  244. playlist_ptr pl = playlistForInterface( interface );
  245. if ( !pl.isNull() )
  246. {
  247. TomahawkSettings::instance()->appendRecentlyPlayedPlaylist( pl->guid(), pl->author()->id() );
  248. }
  249. else
  250. {
  251. pl = dynamicPlaylistForInterface( interface );
  252. if ( !pl.isNull() )
  253. TomahawkSettings::instance()->appendRecentlyPlayedPlaylist( pl->guid(), pl->author()->id() );
  254. }
  255. }
  256. Tomahawk::ViewPage*
  257. ViewManager::showQueuePage()
  258. {
  259. if ( !m_queue )
  260. return 0;
  261. return show( m_queue );
  262. }
  263. Tomahawk::ViewPage *
  264. ViewManager::showInboxPage()
  265. {
  266. if ( !m_inboxWidget )
  267. {
  268. m_inboxWidget = new InboxPage( m_widget );
  269. }
  270. return show( m_inboxWidget );
  271. }
  272. void
  273. ViewManager::historyBack()
  274. {
  275. if ( m_pageHistoryBack.isEmpty() )
  276. return;
  277. ViewPage* page = m_pageHistoryBack.takeLast();
  278. if ( m_currentPage )
  279. {
  280. m_pageHistoryFwd << m_currentPage;
  281. tDebug() << "Moved to forward history:" << m_currentPage->widget()->metaObject()->className();
  282. }
  283. tDebug() << "Showing page after moving backwards in history:" << page->widget()->metaObject()->className();
  284. setPage( page, false );
  285. }
  286. void
  287. ViewManager::historyForward()
  288. {
  289. if ( m_pageHistoryFwd.isEmpty() )
  290. return;
  291. ViewPage* page = m_pageHistoryFwd.takeLast();
  292. if ( m_currentPage )
  293. {
  294. m_pageHistoryBack << m_currentPage;
  295. tDebug() << "Moved to backward history:" << m_currentPage->widget()->metaObject()->className();
  296. }
  297. tDebug() << "Showing page after moving forwards in history:" << page->widget()->metaObject()->className();
  298. setPage( page, false );
  299. }
  300. QList<ViewPage*>
  301. ViewManager::allPages() const
  302. {
  303. QList< ViewPage* > pages = m_pageHistoryBack + m_pageHistoryFwd;
  304. pages << m_currentPage;
  305. return pages;
  306. }
  307. QList<ViewPage*>
  308. ViewManager::historyPages() const
  309. {
  310. return m_pageHistoryBack + m_pageHistoryFwd;
  311. }
  312. void
  313. ViewManager::destroyPage( ViewPage* page )
  314. {
  315. if ( !page )
  316. return;
  317. tDebug() << Q_FUNC_INFO << "Deleting page:" << page->title();
  318. if ( historyPages().contains( page ) )
  319. {
  320. m_pageHistoryBack.removeAll( page );
  321. m_pageHistoryFwd.removeAll( page );
  322. emit historyBackAvailable( !m_pageHistoryBack.isEmpty() );
  323. emit historyForwardAvailable( !m_pageHistoryFwd.isEmpty() );
  324. }
  325. if ( m_currentPage == page )
  326. {
  327. m_currentPage = 0;
  328. historyBack();
  329. }
  330. emit viewPageAboutToBeDestroyed( page );
  331. delete page;
  332. emit viewPageDestroyed();
  333. }
  334. bool
  335. ViewManager::destroyCurrentPage()
  336. {
  337. if ( !currentPage() || !currentPage()->isTemporaryPage() )
  338. return false;
  339. destroyPage( currentPage() );
  340. return true;
  341. }
  342. void
  343. ViewManager::setPage( ViewPage* page, bool trackHistory )
  344. {
  345. if ( !page )
  346. return;
  347. if ( page == m_currentPage )
  348. return;
  349. if ( m_stack->indexOf( page->widget() ) < 0 )
  350. {
  351. m_stack->addWidget( page->widget() );
  352. }
  353. if ( m_currentPage && trackHistory )
  354. {
  355. m_pageHistoryBack << m_currentPage;
  356. m_pageHistoryFwd.clear();
  357. }
  358. m_currentPage = page;
  359. emit historyBackAvailable( m_pageHistoryBack.count() );
  360. emit historyForwardAvailable( m_pageHistoryFwd.count() );
  361. tDebug() << "View page shown:" << page->title();
  362. emit viewPageActivated( page );
  363. if ( page->isTemporaryPage() )
  364. emit tempPageActivated( page );
  365. if ( AudioEngine::instance()->state() == AudioEngine::Stopped )
  366. AudioEngine::instance()->setPlaylist( page->playlistInterface() );
  367. if ( QObject* obj = dynamic_cast< QObject* >( currentPage() ) )
  368. {
  369. if ( obj->metaObject()->indexOfSignal( "destroyed(QWidget*)" ) > -1 )
  370. connect( obj, SIGNAL( destroyed( QWidget* ) ), SLOT( onWidgetDestroyed( QWidget* ) ), Qt::UniqueConnection );
  371. }
  372. QWidget *previousPage = m_stack->currentWidget();
  373. m_stack->setCurrentWidget( page->widget() );
  374. //This should save the CPU cycles, especially with pages like the visualizer
  375. if ( previousPage && previousPage != page->widget() )
  376. previousPage->hide();
  377. }
  378. void
  379. ViewManager::onWidgetDestroyed( QWidget* widget )
  380. {
  381. tDebug() << "Destroyed child:" << widget << widget->metaObject()->className();
  382. bool resetWidget = ( m_stack->currentWidget() == widget );
  383. QList< Tomahawk::ViewPage* > p = historyPages();
  384. for ( int i = 0; i < p.count(); i++ )
  385. {
  386. ViewPage* page = p.at( i );
  387. if ( page->widget() != widget )
  388. continue;
  389. if ( !playlistForInterface( page->playlistInterface() ).isNull() )
  390. {
  391. m_playlistViews.remove( playlistForInterface( page->playlistInterface() ) );
  392. }
  393. if ( !dynamicPlaylistForInterface( page->playlistInterface() ).isNull() )
  394. {
  395. m_dynamicWidgets.remove( dynamicPlaylistForInterface( page->playlistInterface() ) );
  396. }
  397. m_pageHistoryBack.removeAll( page );
  398. m_pageHistoryFwd.removeAll( page );
  399. break;
  400. }
  401. m_stack->removeWidget( widget );
  402. if ( resetWidget )
  403. {
  404. m_currentPage = 0;
  405. historyBack();
  406. }
  407. }
  408. ViewPage*
  409. ViewManager::pageForDynPlaylist(const dynplaylist_ptr& pl) const
  410. {
  411. return m_dynamicWidgets.value( pl ).data();
  412. }
  413. ViewPage*
  414. ViewManager::pageForPlaylist(const playlist_ptr& pl) const
  415. {
  416. return m_playlistViews.value( pl ).data();
  417. }
  418. ViewPage*
  419. ViewManager::pageForInterface( Tomahawk::playlistinterface_ptr interface ) const
  420. {
  421. QList< Tomahawk::ViewPage* > pages = allPages();
  422. for ( int i = 0; i < pages.count(); i++ )
  423. {
  424. ViewPage* page = pages.at( i );
  425. if ( page->playlistInterface() == interface )
  426. return page;
  427. if ( page->playlistInterface() && page->playlistInterface()->hasChildInterface( interface ) )
  428. return page;
  429. }
  430. return 0;
  431. }
  432. Tomahawk::playlistinterface_ptr
  433. ViewManager::currentPlaylistInterface() const
  434. {
  435. if ( currentPage() )
  436. return currentPage()->playlistInterface();
  437. else
  438. return Tomahawk::playlistinterface_ptr();
  439. }
  440. Tomahawk::ViewPage*
  441. ViewManager::currentPage() const
  442. {
  443. return m_currentPage;
  444. }
  445. Tomahawk::playlist_ptr
  446. ViewManager::playlistForInterface( Tomahawk::playlistinterface_ptr interface ) const
  447. {
  448. foreach ( QPointer<PlaylistViewPage> view, m_playlistViews.values() )
  449. {
  450. if ( !view.isNull() && view.data()->playlistInterface() == interface )
  451. {
  452. return m_playlistViews.key( view );
  453. }
  454. }
  455. return Tomahawk::playlist_ptr();
  456. }
  457. Tomahawk::dynplaylist_ptr
  458. ViewManager::dynamicPlaylistForInterface( Tomahawk::playlistinterface_ptr interface ) const
  459. {
  460. foreach ( QPointer<DynamicWidget> view, m_dynamicWidgets.values() )
  461. {
  462. if ( !view.isNull() && view.data()->playlistInterface() == interface )
  463. {
  464. return m_dynamicWidgets.key( view );
  465. }
  466. }
  467. return Tomahawk::dynplaylist_ptr();
  468. }
  469. void
  470. ViewManager::showCurrentTrack()
  471. {
  472. ViewPage* page = pageForInterface( AudioEngine::instance()->currentTrackPlaylist() );
  473. if ( page )
  474. {
  475. setPage( page );
  476. page->jumpToCurrentTrack();
  477. }
  478. }
  479. Tomahawk::ViewPage*
  480. ViewManager::inboxWidget() const
  481. {
  482. return m_inboxWidget;
  483. }
  484. ViewPage*
  485. ViewManager::dynamicPageWidget( const QString& pageName ) const
  486. {
  487. if ( m_dynamicPages.contains( pageName ) )
  488. return m_dynamicPages.value( pageName );
  489. if ( m_dynamicPagePlugins.contains( pageName ) )
  490. return m_dynamicPagePlugins.value( pageName ).data();
  491. return 0;
  492. }
  493. void
  494. ViewManager::addDynamicPage( Tomahawk::ViewPagePlugin* viewPage, const QString& pageName )
  495. {
  496. const QString pageId = !pageName.isEmpty() ? pageName : viewPage->defaultName();
  497. tLog() << Q_FUNC_INFO << "Trying to add" << pageId;
  498. if ( m_dynamicPages.contains( pageId ) || m_dynamicPagePlugins.contains( pageId ) )
  499. {
  500. tDebug() << Q_FUNC_INFO << "Not adding a second page with id" << pageId << "- old colliding plugin found, please clean your install dir.";
  501. Q_ASSERT( false );
  502. }
  503. m_dynamicPagePlugins.insert( pageId, viewPage );
  504. emit viewPageAdded( pageId, viewPage, viewPage->sortValue() );
  505. }
  506. /*void
  507. ViewManager::addDynamicPage( const QString& pageName, const QString& text, const QIcon& icon, function<Tomahawk::ViewPage*()> instanceLoader, int sortValue )
  508. {
  509. tLog() << Q_FUNC_INFO << "Trying to add" << pageName;
  510. if ( m_dynamicPages.contains( pageName ) || m_dynamicPagePlugins.contains( pageName ) )
  511. {
  512. tLog() << "Not adding a second ViewPage with name" << pageName;
  513. Q_ASSERT( false );
  514. }
  515. m_dynamicPagesInstanceLoaders.insert( pageName, instanceLoader );
  516. emit viewPageAdded( pageName, text, icon, sortValue );
  517. }*/
  518. ViewPage*
  519. ViewManager::showDynamicPage( const QString& pageName )
  520. {
  521. tLog() << Q_FUNC_INFO << "pageName:" << pageName;
  522. if ( !m_dynamicPages.contains( pageName ) && !m_dynamicPagePlugins.contains( pageName ) )
  523. {
  524. if ( !m_dynamicPagesInstanceLoaders.contains( pageName ) )
  525. {
  526. Q_ASSERT_X( false, Q_FUNC_INFO, QString("Trying to show a page that does not exist and does not have a registered loader: %d" ).arg( pageName ).toStdString().c_str() );
  527. return 0;
  528. }
  529. ViewPage* viewPage = m_dynamicPagesInstanceLoaders.value( pageName )();
  530. Q_ASSERT( viewPage );
  531. if ( !viewPage ) {
  532. return NULL;
  533. }
  534. m_dynamicPages.insert( pageName, viewPage );
  535. m_dynamicPagesInstanceLoaders.remove( pageName );
  536. }
  537. return show( dynamicPageWidget( pageName ) );
  538. }
  539. InboxModel*
  540. ViewManager::inboxModel()
  541. {
  542. return m_inboxModel;
  543. }