diff --git a/src/libtomahawk/LatchManager.cpp b/src/libtomahawk/LatchManager.cpp index 5e5f3649c..572577564 100644 --- a/src/libtomahawk/LatchManager.cpp +++ b/src/libtomahawk/LatchManager.cpp @@ -22,8 +22,7 @@ #include "audio/audioengine.h" #include "database/database.h" -#include -#include +#include #include "sourcelist.h" #include "database/databasecommand_socialaction.h" #include "sourceplaylistinterface.h" @@ -82,7 +81,9 @@ LatchManager::playlistChanged( Tomahawk::playlistinterface_ptr ) cmd->setTimestamp( QDateTime::currentDateTime().toTime_t() ); Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) ); - ActionCollection::instance()->getAction( "latchOn" )->setText( tr( "&Catch Up" ) ); + QAction *latchOnAction = ActionCollection::instance()->getAction( "latchOn" ); + latchOnAction->setText( tr( "&Catch Up" ) ); + latchOnAction->setIcon( QIcon() ); // If not, then keep waiting return; @@ -117,7 +118,9 @@ LatchManager::playlistChanged( Tomahawk::playlistinterface_ptr ) m_state = NotLatched; - ActionCollection::instance()->getAction( "latchOn" )->setText( tr( "&Listen Along" ) ); + QAction *latchOnAction = ActionCollection::instance()->getAction( "latchOn" ); + latchOnAction->setText( tr( "&Listen Along" ) ); + latchOnAction->setIcon( QIcon( RESPATH "images/headphones-sidebar.png" ) ); } @@ -128,6 +131,7 @@ LatchManager::catchUpRequest() AudioEngine::instance()->next(); } + void LatchManager::unlatchRequest( const source_ptr& source ) { @@ -135,5 +139,19 @@ LatchManager::unlatchRequest( const source_ptr& source ) AudioEngine::instance()->stop(); AudioEngine::instance()->setPlaylist( Tomahawk::playlistinterface_ptr() ); - ActionCollection::instance()->getAction( "latchOn" )->setText( tr( "&Listen Along" ) ); + QAction *latchOnAction = ActionCollection::instance()->getAction( "latchOn" ); + latchOnAction->setText( tr( "&Listen Along" ) ); + latchOnAction->setIcon( QIcon( RESPATH "images/headphones-sidebar.png" ) ); +} + + +void +LatchManager::latchModeChangeRequest( const Tomahawk::source_ptr& source, bool realtime ) +{ + if ( !isLatched( source ) ) + return; + + source->getPlaylistInterface()->setLatchMode( realtime ? Tomahawk::PlaylistInterface::RealTime : Tomahawk::PlaylistInterface::StayOnSong ); + if ( realtime ) + catchUpRequest(); } diff --git a/src/libtomahawk/LatchManager.h b/src/libtomahawk/LatchManager.h index 26d8b8f8f..07c27ba30 100644 --- a/src/libtomahawk/LatchManager.h +++ b/src/libtomahawk/LatchManager.h @@ -43,6 +43,7 @@ public slots: void latchRequest( const Tomahawk::source_ptr& source ); void unlatchRequest( const Tomahawk::source_ptr& source ); void catchUpRequest(); + void latchModeChangeRequest( const Tomahawk::source_ptr& source, bool realtime ); private slots: void playlistChanged( Tomahawk::playlistinterface_ptr ); diff --git a/src/libtomahawk/actioncollection.cpp b/src/libtomahawk/actioncollection.cpp index 802794df1..3088e25b8 100644 --- a/src/libtomahawk/actioncollection.cpp +++ b/src/libtomahawk/actioncollection.cpp @@ -46,6 +46,10 @@ ActionCollection::initActions() latchOff->setIcon( QIcon( RESPATH "images/headphones-off.png" ) ); m_actionCollection[ "latchOff" ] = latchOff; + QAction *realtimeFollowingAlong = new QAction( tr( "&Follow in real-time" ), this ); + realtimeFollowingAlong->setCheckable( true ); + m_actionCollection[ "realtimeFollowingAlong" ] = realtimeFollowingAlong; + bool isPublic = TomahawkSettings::instance()->privateListeningMode() == TomahawkSettings::PublicListening; QAction *privacyToggle = new QAction( ( isPublic ? tr( "&Listen Privately" ) : tr( "&Listen Publicly" ) ), this ); privacyToggle->setIcon( QIcon( RESPATH "images/private-listening.png" ) ); diff --git a/src/libtomahawk/actioncollection.h b/src/libtomahawk/actioncollection.h index 09ede7839..1183935dc 100644 --- a/src/libtomahawk/actioncollection.h +++ b/src/libtomahawk/actioncollection.h @@ -21,7 +21,7 @@ #include "dllmacro.h" -#include +#include class DLLEXPORT ActionCollection : public QObject { diff --git a/src/libtomahawk/audio/audioengine.cpp b/src/libtomahawk/audio/audioengine.cpp index 9be1cf518..2e5a7e509 100644 --- a/src/libtomahawk/audio/audioengine.cpp +++ b/src/libtomahawk/audio/audioengine.cpp @@ -228,12 +228,12 @@ AudioEngine::canGoNext() m_playlist.data()->skipRestrictions() == PlaylistInterface::NoSkipForwards ) return false; - if ( !m_currentTrack.isNull() && !m_playlist.data()->hasNextItem() && - ( m_playlist.data()->currentItem().isNull() || ( m_currentTrack->id() == m_playlist.data()->currentItem()->id() ) ) ) + if ( !m_currentTrack.isNull() && !m_playlist->hasNextItem() && + ( m_playlist->currentItem().isNull() || ( m_currentTrack->id() == m_playlist->currentItem()->id() ) ) ) { //For instance, when doing a catch-up while listening along, but the person //you're following hasn't started a new track yet...don't do anything - tDebug( LOGEXTRA ) << Q_FUNC_INFO << "catch up"; + tDebug( LOGEXTRA ) << Q_FUNC_INFO << "catch up, but same track or can't move on because don't have next track or it wasn't resolved"; return false; } @@ -580,6 +580,14 @@ AudioEngine::playItem( Tomahawk::playlistinterface_ptr playlist, const Tomahawk: void AudioEngine::playlistNextTrackReady() { + // If in real-time and you have a few seconds left, you're probably lagging -- finish it up + if ( m_playlist && m_playlist->latchMode() == PlaylistInterface::RealTime && ( m_waitingOnNewTrack || m_currentTrack.isNull() || m_currentTrack->id() == 0 || ( currentTrackTotalTime() - currentTime() > 6000 ) ) ) + { + m_waitingOnNewTrack = false; + loadNextTrack(); + return; + } + if ( !m_waitingOnNewTrack ) return; diff --git a/src/libtomahawk/playlistinterface.cpp b/src/libtomahawk/playlistinterface.cpp index 8c03d9df1..11d60ea3e 100644 --- a/src/libtomahawk/playlistinterface.cpp +++ b/src/libtomahawk/playlistinterface.cpp @@ -24,6 +24,7 @@ using namespace Tomahawk; PlaylistInterface::PlaylistInterface () : QObject() + , m_latchMode( StayOnSong ) { qRegisterMetaType( "Tomahawk::PlaylistInterface::RepeatMode" ); } diff --git a/src/libtomahawk/playlistinterface.h b/src/libtomahawk/playlistinterface.h index b125d8ae9..45350b9e1 100644 --- a/src/libtomahawk/playlistinterface.h +++ b/src/libtomahawk/playlistinterface.h @@ -39,6 +39,7 @@ public: enum SeekRestrictions { NoSeekRestrictions, NoSeek }; enum SkipRestrictions { NoSkipRestrictions, NoSkipForwards, NoSkipBackwards, NoSkip }; enum RetryMode { NoRetry, Retry }; + enum LatchMode { StayOnSong, RealTime }; explicit PlaylistInterface(); virtual ~PlaylistInterface(); @@ -55,14 +56,20 @@ public: virtual Tomahawk::result_ptr siblingItem( int itemsAway ) = 0; virtual PlaylistInterface::RepeatMode repeatMode() const = 0; + virtual bool shuffled() const = 0; + virtual PlaylistInterface::ViewMode viewMode() const { return Unknown; } + virtual PlaylistInterface::SeekRestrictions seekRestrictions() const { return NoSeekRestrictions; } virtual PlaylistInterface::SkipRestrictions skipRestrictions() const { return NoSkipRestrictions; } virtual PlaylistInterface::RetryMode retryMode() const { return NoRetry; } virtual quint32 retryInterval() const { return 30000; } + virtual PlaylistInterface::LatchMode latchMode() const { return m_latchMode; } + virtual void setLatchMode( PlaylistInterface::LatchMode latchMode ) { m_latchMode = latchMode; } + virtual QString filter() const { return m_filter; } virtual void setFilter( const QString& pattern ) { m_filter = pattern; } @@ -84,6 +91,9 @@ signals: void sourceTrackCountChanged( unsigned int tracks ); void nextTrackReady(); +protected: + LatchMode m_latchMode; + private: Q_DISABLE_COPY( PlaylistInterface ) diff --git a/src/libtomahawk/sourceplaylistinterface.cpp b/src/libtomahawk/sourceplaylistinterface.cpp index b1ee26ef6..d6108563d 100644 --- a/src/libtomahawk/sourceplaylistinterface.cpp +++ b/src/libtomahawk/sourceplaylistinterface.cpp @@ -20,20 +20,26 @@ #include "source.h" #include "pipeline.h" +#include "audio/audioengine.h" #include "utils/logger.h" using namespace Tomahawk; -SourcePlaylistInterface::SourcePlaylistInterface( Tomahawk::Source *source ) +SourcePlaylistInterface::SourcePlaylistInterface( Tomahawk::Source *source, Tomahawk::PlaylistInterface::LatchMode latchMode ) : PlaylistInterface() , m_source( source ) , m_currentItem( 0 ) , m_gotNextItem( false ) { + setLatchMode( latchMode ); + if ( !m_source.isNull() ) connect( m_source.data(), SIGNAL( playbackStarted( const Tomahawk::query_ptr& ) ), SLOT( onSourcePlaybackStarted( const Tomahawk::query_ptr& ) ) ); + + if ( AudioEngine::instance() ) + connect( AudioEngine::instance(), SIGNAL( paused() ), SLOT( audioPaused() ) ); } @@ -95,7 +101,7 @@ QList SourcePlaylistInterface::tracks() { QList tracks; - return tracks; // FIXME + return tracks; // FIXME (with what?) } diff --git a/src/libtomahawk/sourceplaylistinterface.h b/src/libtomahawk/sourceplaylistinterface.h index 82de8ad44..0c1c5dff4 100644 --- a/src/libtomahawk/sourceplaylistinterface.h +++ b/src/libtomahawk/sourceplaylistinterface.h @@ -35,7 +35,7 @@ class DLLEXPORT SourcePlaylistInterface : public Tomahawk::PlaylistInterface Q_OBJECT public: - SourcePlaylistInterface( Tomahawk::Source *source ); + SourcePlaylistInterface( Tomahawk::Source *source, Tomahawk::PlaylistInterface::LatchMode latchMode = PlaylistInterface::RealTime ); virtual ~SourcePlaylistInterface(); QList tracks(); @@ -64,6 +64,7 @@ public: public slots: virtual void setRepeatMode( PlaylistInterface::RepeatMode ) {} virtual void setShuffled( bool ) {} + virtual void audioPaused() { setLatchMode( PlaylistInterface::StayOnSong ); } private slots: void onSourcePlaybackStarted( const Tomahawk::query_ptr& query ); diff --git a/src/sourcetree/sourcetreeview.cpp b/src/sourcetree/sourcetreeview.cpp index 242315b4b..4db5c483c 100644 --- a/src/sourcetree/sourcetreeview.cpp +++ b/src/sourcetree/sourcetreeview.cpp @@ -118,6 +118,7 @@ SourceTreeView::SourceTreeView( QWidget* parent ) connect( this, SIGNAL( latchRequest( Tomahawk::source_ptr ) ), m_latchManager, SLOT( latchRequest( Tomahawk::source_ptr ) ) ); connect( this, SIGNAL( unlatchRequest( Tomahawk::source_ptr ) ), m_latchManager, SLOT( unlatchRequest( Tomahawk::source_ptr ) ) ); connect( this, SIGNAL( catchUpRequest() ), m_latchManager, SLOT( catchUpRequest() ) ); + connect( this, SIGNAL( latchModeChangeRequest( Tomahawk::source_ptr, bool ) ), m_latchManager, SLOT( latchModeChangeRequest( Tomahawk::source_ptr, bool ) ) ); connect( ActionCollection::instance(), SIGNAL( privacyModeChanged() ), SLOT( repaint() ) ); } @@ -162,10 +163,14 @@ SourceTreeView::setupMenus() { if ( m_latchManager->isLatched( source ) ) { - m_latchMenu.addSeparator(); QAction *latchOffAction = ActionCollection::instance()->getAction( "latchOff" ); m_latchMenu.addAction( latchOffAction ); - connect( latchOffAction, SIGNAL( triggered() ), SLOT( latchOff() ), Qt::QueuedConnection ); + connect( latchOffAction, SIGNAL( triggered() ), SLOT( latchOff() ) ); + m_latchMenu.addSeparator(); + QAction *latchRealtimeAction = ActionCollection::instance()->getAction( "realtimeFollowingAlong" ); + latchRealtimeAction->setChecked( source->getPlaylistInterface()->latchMode() == Tomahawk::PlaylistInterface::RealTime ); + m_latchMenu.addAction( latchRealtimeAction ); + connect( latchRealtimeAction, SIGNAL( toggled( bool ) ), SLOT( latchModeToggled( bool ) ) ); } } } @@ -416,6 +421,24 @@ SourceTreeView::latchOff( const Tomahawk::source_ptr& source ) } +void +SourceTreeView::latchModeToggled( bool checked ) +{ + + disconnect( this, SLOT( latchOff() ) ); + qDebug() << Q_FUNC_INFO; + if ( !m_contextMenuIndex.isValid() ) + return; + + SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ).toInt(); + if( type != SourcesModel::Collection ) + return; + + const SourceItem* item = itemFromIndex< SourceItem >( m_contextMenuIndex ); + const source_ptr source = item->source(); + emit latchModeChangeRequest( source, checked ); +} + void SourceTreeView::renamePlaylist() diff --git a/src/sourcetree/sourcetreeview.h b/src/sourcetree/sourcetreeview.h index 4a518cac7..0d6a9b7b1 100644 --- a/src/sourcetree/sourcetreeview.h +++ b/src/sourcetree/sourcetreeview.h @@ -58,6 +58,7 @@ signals: void latchRequest( const Tomahawk::source_ptr& source ); void unlatchRequest( const Tomahawk::source_ptr& source ); void catchUpRequest(); + void latchModeChangeRequest( const Tomahawk::source_ptr& source, bool realtime ); private slots: void onItemExpanded( const QModelIndex& idx ); @@ -75,6 +76,7 @@ private slots: void latchOff(); void latchOnOrCatchUp( const Tomahawk::source_ptr& source ); void latchOff( const Tomahawk::source_ptr& source ); + void latchModeToggled( bool checked ); void onCustomContextMenu( const QPoint& pos );