PageRenderTime 285ms CodeModel.GetById 71ms app.highlight 150ms RepoModel.GetById 41ms app.codeStats 0ms

/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
 22#include "ViewManager.h"
 23
 24#include "SourceList.h"
 25#include "TomahawkSettings.h"
 26
 27#include "audio/AudioEngine.h"
 28
 29#include "playlist/ContextView.h"
 30#include "playlist/TreeModel.h"
 31#include "playlist/PlaylistModel.h"
 32#include "playlist/TrackView.h"
 33#include "playlist/PlayableProxyModel.h"
 34#include "playlist/PlayableModel.h"
 35#include "playlist/ColumnView.h"
 36#include "playlist/GridView.h"
 37#include "playlist/AlbumModel.h"
 38#include "playlist/InboxModel.h"
 39#include "playlist/InboxView.h"
 40#include "playlist/TrackItemDelegate.h"
 41#include "playlist/RecentlyPlayedModel.h"
 42#include "playlist/dynamic/widgets/DynamicWidget.h"
 43
 44#include "viewpages/PlaylistViewPage.h"
 45#include "viewpages/SourceViewPage.h"
 46#include "viewpages/ArtistViewPage.h"
 47#include "viewpages/AlbumViewPage.h"
 48#include "viewpages/TrackViewPage.h"
 49#include "viewpages/CollectionViewPage.h"
 50
 51#include "utils/Logger.h"
 52#include "utils/TomahawkUtilsGui.h"
 53
 54#include <QVBoxLayout>
 55#include <QMetaMethod>
 56
 57
 58using namespace Tomahawk;
 59
 60ViewManager* ViewManager::s_instance = 0;
 61
 62
 63ViewManager*
 64ViewManager::instance()
 65{
 66    return s_instance;
 67}
 68
 69
 70ViewManager::ViewManager( QObject* parent )
 71    : QObject( parent )
 72    , m_widget( new QWidget() )
 73    , m_queue( 0 )
 74    , m_inboxWidget( 0 )
 75    , m_currentPage( 0 )
 76{
 77    s_instance = this;
 78
 79    m_widget->setLayout( new QVBoxLayout() );
 80    m_stack = new QStackedWidget();
 81
 82    m_inboxModel = new InboxModel( this );
 83    m_inboxModel->setTitle( tr( "Inbox" ) );
 84    m_inboxModel->setDescription( tr( "Listening suggestions from your friends" ) );
 85    m_inboxModel->setIcon( TomahawkUtils::defaultPixmap( TomahawkUtils::Inbox ) );
 86
 87    m_widget->layout()->addWidget( m_stack );
 88
 89    m_stack->setContentsMargins( 0, 0, 0, 0 );
 90    m_widget->setContentsMargins( 0, 0, 0, 0 );
 91    m_widget->layout()->setContentsMargins( 0, 0, 0, 0 );
 92    m_widget->layout()->setMargin( 0 );
 93    m_widget->layout()->setSpacing( 0 );
 94
 95    connect( AudioEngine::instance(), SIGNAL( playlistChanged( Tomahawk::playlistinterface_ptr ) ), this, SLOT( playlistInterfaceChanged( Tomahawk::playlistinterface_ptr ) ) );
 96}
 97
 98
 99ViewManager::~ViewManager()
100{
101    delete m_inboxWidget;
102    delete m_widget;
103}
104
105
106PlaylistViewPage*
107ViewManager::createPageForPlaylist( const playlist_ptr& playlist )
108{
109    PlaylistViewPage* view = new PlaylistViewPage();
110    PlaylistModel* model = new PlaylistModel();
111
112    // We need to set the model on the view before loading the playlist, so spinners & co are connected
113    view->view()->trackView()->setPlayableModel( model );
114
115    model->loadPlaylist( playlist );
116    playlist->resolve();
117
118    return view;
119}
120
121
122PlaylistViewPage*
123ViewManager::createPageForList( const QString& title, const QList< query_ptr >& queries )
124{
125    PlaylistViewPage* view = new PlaylistViewPage();
126    PlaylistModel* model = new PlaylistModel();
127
128    view->setTemporaryPage( true );
129
130    // We need to set the model on the view before loading the playlist, so spinners & co are connected
131    view->view()->trackView()->setPlayableModel( model );
132
133    model->setTitle( title );
134    model->appendQueries( queries );
135
136    return view;
137}
138
139
140playlist_ptr
141ViewManager::playlistForPage( ViewPage* page ) const
142{
143    playlist_ptr p;
144
145    PlaylistViewPage* fv = dynamic_cast< PlaylistViewPage* >( page );
146    if ( fv && fv->view()->trackView()->model() )
147    {
148        PlaylistModel* m = dynamic_cast< PlaylistModel* >( fv->view()->trackView()->model() );
149        if ( m && m->playlist() )
150        {
151            p = m->playlist();
152        }
153    }
154    else if ( dynamic_cast< DynamicWidget* >( page ) )
155        p = dynamic_cast< DynamicWidget* >( page )->playlist();
156
157    return p;
158}
159
160
161Tomahawk::ViewPage*
162ViewManager::show( const Tomahawk::playlist_ptr& playlist )
163{
164    if ( !playlist->loaded() )
165        playlist->loadRevision();
166
167    PlaylistViewPage* view;
168
169    if ( !m_playlistViews.contains( playlist ) || m_playlistViews.value( playlist ).isNull() )
170    {
171        view = createPageForPlaylist( playlist );
172        m_playlistViews.insert( playlist, view );
173    }
174    else
175    {
176        view = m_playlistViews.value( playlist ).data();
177    }
178
179    setPage( view );
180    return view;
181}
182
183
184Tomahawk::ViewPage*
185ViewManager::show( const Tomahawk::dynplaylist_ptr& playlist )
186{
187    if ( !m_dynamicWidgets.contains( playlist ) || m_dynamicWidgets.value( playlist ).isNull() )
188    {
189       m_dynamicWidgets[ playlist ] = new Tomahawk::DynamicWidget( playlist, m_stack );
190
191       playlist->resolve();
192    }
193
194    setPage( m_dynamicWidgets.value( playlist ).data() );
195
196    return m_dynamicWidgets.value( playlist ).data();
197}
198
199
200Tomahawk::ViewPage*
201ViewManager::show( const Tomahawk::artist_ptr& artist )
202{
203    ArtistInfoWidget* swidget;
204    if ( !m_artistViews.contains( artist ) || m_artistViews.value( artist ).isNull() )
205    {
206        swidget = new ArtistInfoWidget( artist );
207        m_artistViews.insert( artist, swidget );
208    }
209    else
210    {
211        swidget = m_artistViews.value( artist ).data();
212    }
213
214    setPage( swidget );
215    return swidget;
216}
217
218
219Tomahawk::ViewPage*
220ViewManager::show( const Tomahawk::album_ptr& album )
221{
222    AlbumInfoWidget* swidget;
223    if ( !m_albumViews.contains( album ) || m_albumViews.value( album ).isNull() )
224    {
225        swidget = new AlbumInfoWidget( album );
226        m_albumViews.insert( album, swidget );
227    }
228    else
229    {
230        swidget = m_albumViews.value( album ).data();
231    }
232
233    setPage( swidget );
234    return swidget;
235}
236
237
238Tomahawk::ViewPage*
239ViewManager::show( const Tomahawk::query_ptr& query )
240{
241    TrackInfoWidget* swidget;
242    if ( !m_trackViews.contains( query ) || m_trackViews.value( query ).isNull() )
243    {
244        swidget = new TrackInfoWidget( query );
245        m_trackViews.insert( query, swidget );
246    }
247    else
248    {
249        swidget = m_trackViews.value( query ).data();
250    }
251
252    setPage( swidget );
253    return swidget;
254}
255
256
257Tomahawk::ViewPage*
258ViewManager::show( const Tomahawk::collection_ptr& collection )
259{
260    m_currentCollection = collection;
261
262    CollectionViewPage* view;
263    if ( !m_collectionViews.contains( collection ) || m_collectionViews.value( collection ).isNull() )
264    {
265        view = new CollectionViewPage( collection );
266        setPage( view );
267
268        m_collectionViews.insert( collection, view );
269    }
270    else
271    {
272        view = m_collectionViews.value( collection ).data();
273    }
274    view->restoreViewMode();
275
276    setPage( view );
277    return view;
278}
279
280
281Tomahawk::ViewPage*
282ViewManager::show( const Tomahawk::source_ptr& source )
283{
284    SourceInfoWidget* swidget;
285    if ( !m_sourceViews.contains( source ) || m_sourceViews.value( source ).isNull() )
286    {
287        swidget = new SourceInfoWidget( source );
288        m_sourceViews.insert( source, swidget );
289    }
290    else
291    {
292        swidget = m_sourceViews.value( source ).data();
293    }
294
295    setPage( swidget );
296    return swidget;
297}
298
299
300Tomahawk::ViewPage*
301ViewManager::show( ViewPage* page )
302{
303    setPage( page );
304
305    return page;
306}
307
308
309void
310ViewManager::playlistInterfaceChanged( Tomahawk::playlistinterface_ptr interface )
311{
312    playlist_ptr pl = playlistForInterface( interface );
313    if ( !pl.isNull() )
314    {
315        TomahawkSettings::instance()->appendRecentlyPlayedPlaylist( pl->guid(), pl->author()->id() );
316    }
317    else
318    {
319        pl = dynamicPlaylistForInterface( interface );
320        if ( !pl.isNull() )
321            TomahawkSettings::instance()->appendRecentlyPlayedPlaylist( pl->guid(), pl->author()->id() );
322    }
323}
324
325
326Tomahawk::ViewPage*
327ViewManager::showQueuePage()
328{
329    if ( !m_queue )
330        return 0;
331
332    return show( m_queue );
333}
334
335
336Tomahawk::ViewPage *
337ViewManager::showInboxPage()
338{
339    if ( !m_inboxWidget )
340    {
341        m_inboxWidget = new InboxPage( m_widget );
342    }
343
344    return show( m_inboxWidget );
345}
346
347
348void
349ViewManager::historyBack()
350{
351    if ( m_pageHistoryBack.isEmpty() )
352        return;
353
354    ViewPage* page = m_pageHistoryBack.takeLast();
355
356    if ( m_currentPage )
357    {
358        m_pageHistoryFwd << m_currentPage;
359        tDebug() << "Moved to forward history:" << m_currentPage->widget()->metaObject()->className();
360    }
361
362    tDebug() << "Showing page after moving backwards in history:" << page->widget()->metaObject()->className();
363    setPage( page, false );
364}
365
366
367void
368ViewManager::historyForward()
369{
370    if ( m_pageHistoryFwd.isEmpty() )
371        return;
372
373    ViewPage* page = m_pageHistoryFwd.takeLast();
374
375    if ( m_currentPage )
376    {
377        m_pageHistoryBack << m_currentPage;
378        tDebug() << "Moved to backward history:" << m_currentPage->widget()->metaObject()->className();
379    }
380
381    tDebug() << "Showing page after moving forwards in history:" << page->widget()->metaObject()->className();
382    setPage( page, false );
383}
384
385
386QList<ViewPage*>
387ViewManager::allPages() const
388{
389    QList< ViewPage* > pages = m_pageHistoryBack + m_pageHistoryFwd;
390    pages << m_currentPage;
391
392    return pages;
393}
394
395
396QList<ViewPage*>
397ViewManager::historyPages() const
398{
399    return m_pageHistoryBack + m_pageHistoryFwd;
400}
401
402
403void
404ViewManager::destroyPage( ViewPage* page )
405{
406    if ( !page )
407        return;
408
409    tDebug() << Q_FUNC_INFO << "Deleting page:" << page->title();
410
411    if ( historyPages().contains( page ) )
412    {
413        m_pageHistoryBack.removeAll( page );
414        m_pageHistoryFwd.removeAll( page );
415
416        emit historyBackAvailable( !m_pageHistoryBack.isEmpty() );
417        emit historyForwardAvailable( !m_pageHistoryFwd.isEmpty() );
418    }
419
420    if ( m_currentPage == page )
421    {
422        m_currentPage = 0;
423
424        historyBack();
425    }
426
427    emit viewPageAboutToBeDestroyed( page );
428    delete page;
429    emit viewPageDestroyed();
430}
431
432
433bool
434ViewManager::destroyCurrentPage()
435{
436    if ( !currentPage() || !currentPage()->isTemporaryPage() )
437        return false;
438
439    destroyPage( currentPage() );
440    return true;
441}
442
443
444void
445ViewManager::setPage( ViewPage* page, bool trackHistory )
446{
447    if ( !page )
448        return;
449    if ( page == m_currentPage )
450        return;
451
452    if ( m_stack->indexOf( page->widget() ) < 0 )
453    {
454        m_stack->addWidget( page->widget() );
455    }
456
457    if ( m_currentPage && trackHistory )
458    {
459        m_pageHistoryBack << m_currentPage;
460        m_pageHistoryFwd.clear();
461    }
462    m_currentPage = page;
463
464    emit historyBackAvailable( m_pageHistoryBack.count() );
465    emit historyForwardAvailable( m_pageHistoryFwd.count() );
466
467    tDebug() << "View page shown:" << page->title();
468    emit viewPageActivated( page );
469
470    if ( page->isTemporaryPage() )
471        emit tempPageActivated( page );
472
473    if ( AudioEngine::instance()->state() == AudioEngine::Stopped )
474        AudioEngine::instance()->setPlaylist( page->playlistInterface() );
475
476    if ( QObject* obj = dynamic_cast< QObject* >( currentPage() ) )
477    {
478        if ( obj->metaObject()->indexOfSignal( "destroyed(QWidget*)" ) > -1 )
479            connect( obj, SIGNAL( destroyed( QWidget* ) ), SLOT( onWidgetDestroyed( QWidget* ) ), Qt::UniqueConnection );
480    }
481
482    QWidget *previousPage = m_stack->currentWidget();
483
484    m_stack->setCurrentWidget( page->widget() );
485
486    //This should save the CPU cycles, especially with pages like the visualizer
487    if ( previousPage && previousPage != page->widget() )
488        previousPage->hide();
489}
490
491
492void
493ViewManager::onWidgetDestroyed( QWidget* widget )
494{
495    tDebug() << "Destroyed child:" << widget << widget->metaObject()->className();
496
497    bool resetWidget = ( m_stack->currentWidget() == widget );
498
499    QList< Tomahawk::ViewPage* > p = historyPages();
500    for ( int i = 0; i < p.count(); i++ )
501    {
502        ViewPage* page = p.at( i );
503        if ( page->widget() != widget )
504            continue;
505
506        if ( !playlistForInterface( page->playlistInterface() ).isNull() )
507        {
508            m_playlistViews.remove( playlistForInterface( page->playlistInterface() ) );
509        }
510        if ( !dynamicPlaylistForInterface( page->playlistInterface() ).isNull() )
511        {
512            m_dynamicWidgets.remove( dynamicPlaylistForInterface( page->playlistInterface() ) );
513        }
514
515        m_pageHistoryBack.removeAll( page );
516        m_pageHistoryFwd.removeAll( page );
517        break;
518    }
519
520    m_stack->removeWidget( widget );
521
522    if ( resetWidget )
523    {
524        m_currentPage = 0;
525        historyBack();
526    }
527}
528
529
530ViewPage*
531ViewManager::pageForDynPlaylist(const dynplaylist_ptr& pl) const
532{
533    return m_dynamicWidgets.value( pl ).data();
534}
535
536
537ViewPage*
538ViewManager::pageForPlaylist(const playlist_ptr& pl) const
539{
540    return m_playlistViews.value( pl ).data();
541}
542
543
544ViewPage*
545ViewManager::pageForInterface( Tomahawk::playlistinterface_ptr interface ) const
546{
547    QList< Tomahawk::ViewPage* > pages = allPages();
548
549    for ( int i = 0; i < pages.count(); i++ )
550    {
551        ViewPage* page = pages.at( i );
552        if ( page->playlistInterface() == interface )
553            return page;
554        if ( page->playlistInterface() && page->playlistInterface()->hasChildInterface( interface ) )
555            return page;
556    }
557
558    return 0;
559}
560
561
562Tomahawk::playlistinterface_ptr
563ViewManager::currentPlaylistInterface() const
564{
565    if ( currentPage() )
566        return currentPage()->playlistInterface();
567    else
568        return Tomahawk::playlistinterface_ptr();
569}
570
571
572Tomahawk::ViewPage*
573ViewManager::currentPage() const
574{
575    return m_currentPage;
576}
577
578
579Tomahawk::playlist_ptr
580ViewManager::playlistForInterface( Tomahawk::playlistinterface_ptr interface ) const
581{
582    foreach ( QPointer<PlaylistViewPage> view, m_playlistViews.values() )
583    {
584        if ( !view.isNull() && view.data()->playlistInterface() == interface )
585        {
586            return m_playlistViews.key( view );
587        }
588    }
589
590    return Tomahawk::playlist_ptr();
591}
592
593
594Tomahawk::dynplaylist_ptr
595ViewManager::dynamicPlaylistForInterface( Tomahawk::playlistinterface_ptr interface ) const
596{
597    foreach ( QPointer<DynamicWidget> view, m_dynamicWidgets.values() )
598    {
599        if ( !view.isNull() && view.data()->playlistInterface() == interface )
600        {
601            return m_dynamicWidgets.key( view );
602        }
603    }
604
605    return Tomahawk::dynplaylist_ptr();
606}
607
608
609void
610ViewManager::showCurrentTrack()
611{
612    ViewPage* page = pageForInterface( AudioEngine::instance()->currentTrackPlaylist() );
613
614    if ( page )
615    {
616        setPage( page );
617        page->jumpToCurrentTrack();
618    }
619}
620
621
622Tomahawk::ViewPage*
623ViewManager::inboxWidget() const
624{
625    return m_inboxWidget;
626}
627
628
629ViewPage*
630ViewManager::dynamicPageWidget( const QString& pageName ) const
631{
632    if ( m_dynamicPages.contains( pageName ) )
633        return m_dynamicPages.value( pageName );
634
635    if ( m_dynamicPagePlugins.contains( pageName ) )
636        return m_dynamicPagePlugins.value( pageName ).data();
637
638    return 0;
639}
640
641
642void
643ViewManager::addDynamicPage( Tomahawk::ViewPagePlugin* viewPage, const QString& pageName )
644{
645    const QString pageId = !pageName.isEmpty() ? pageName : viewPage->defaultName();
646
647    tLog() << Q_FUNC_INFO << "Trying to add" << pageId;
648
649    if ( m_dynamicPages.contains( pageId ) || m_dynamicPagePlugins.contains( pageId ) )
650    {
651        tDebug() << Q_FUNC_INFO << "Not adding a second page with id" << pageId << "- old colliding plugin found, please clean your install dir.";
652        Q_ASSERT( false );
653    }
654
655    m_dynamicPagePlugins.insert( pageId, viewPage );
656    emit viewPageAdded( pageId, viewPage, viewPage->sortValue() );
657}
658
659
660/*void
661ViewManager::addDynamicPage( const QString& pageName, const QString& text, const QIcon& icon, function<Tomahawk::ViewPage*()> instanceLoader, int sortValue )
662{
663    tLog() << Q_FUNC_INFO << "Trying to add" << pageName;
664
665    if ( m_dynamicPages.contains( pageName ) || m_dynamicPagePlugins.contains( pageName ) )
666    {
667        tLog() << "Not adding a second ViewPage with name" << pageName;
668        Q_ASSERT( false );
669    }
670
671    m_dynamicPagesInstanceLoaders.insert( pageName, instanceLoader );
672    emit viewPageAdded( pageName, text, icon, sortValue );
673}*/
674
675
676ViewPage*
677ViewManager::showDynamicPage( const QString& pageName )
678{
679    tLog() << Q_FUNC_INFO << "pageName:" << pageName;
680
681    if ( !m_dynamicPages.contains( pageName ) && !m_dynamicPagePlugins.contains( pageName ) )
682    {
683        if ( !m_dynamicPagesInstanceLoaders.contains( pageName ) )
684        {
685           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() );
686           return 0;
687        }
688
689        ViewPage* viewPage = m_dynamicPagesInstanceLoaders.value( pageName )();
690        Q_ASSERT( viewPage );
691        if ( !viewPage ) {
692            return NULL;
693        }
694        m_dynamicPages.insert( pageName, viewPage );
695
696        m_dynamicPagesInstanceLoaders.remove( pageName );
697    }
698
699    return show( dynamicPageWidget( pageName ) );
700}
701
702
703InboxModel*
704ViewManager::inboxModel()
705{
706    return m_inboxModel;
707}