From 66581fcaa295c15ed8cd41ff0598d5a99e96e4b2 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Wed, 28 Sep 2011 18:58:45 -0400 Subject: [PATCH] Refactor latching into it's own manager for easier handling, and clean up some corner cases --- src/libtomahawk/CMakeLists.txt | 2 + src/libtomahawk/LatchManager.cpp | 144 ++++++++++++++++++++++++++ src/libtomahawk/LatchManager.h | 65 ++++++++++++ src/libtomahawk/audio/audioengine.cpp | 5 +- src/sourcetree/sourcetreeview.cpp | 141 +++++-------------------- src/sourcetree/sourcetreeview.h | 19 ++-- 6 files changed, 254 insertions(+), 122 deletions(-) create mode 100644 src/libtomahawk/LatchManager.cpp create mode 100644 src/libtomahawk/LatchManager.h diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index dd34dde69..cb0640e6a 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -33,6 +33,7 @@ set( libSources contextmenu.cpp dropjob.cpp playlistinterface.cpp + LatchManager.cpp sip/SipPlugin.cpp sip/SipHandler.cpp @@ -246,6 +247,7 @@ set( libHeaders contextmenu.h dropjob.h AtticaManager.h + LatchManager.h artist.h album.h diff --git a/src/libtomahawk/LatchManager.cpp b/src/libtomahawk/LatchManager.cpp new file mode 100644 index 000000000..9eb5a6eed --- /dev/null +++ b/src/libtomahawk/LatchManager.cpp @@ -0,0 +1,144 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "LatchManager.h" + +#include "audio/audioengine.h" +#include "database/database.h" + +#include +#include +#include "sourcelist.h" +#include "database/databasecommand_socialaction.h" +#include "sourceplaylistinterface.h" + +using namespace Tomahawk; + +LatchManager::LatchManager( QObject* parent ) + : QObject( parent ) + , m_state( NotLatched ) +{ + + connect( AudioEngine::instance(), SIGNAL( playlistChanged( Tomahawk::PlaylistInterface* ) ), this, SLOT( playlistChanged( Tomahawk::PlaylistInterface* ) ) ); + connect( AudioEngine::instance(), SIGNAL( stopped() ), this, SLOT( playlistChanged() ) ); +} + +LatchManager::~LatchManager() +{ + +} + +bool +LatchManager::isLatched( const source_ptr& src ) +{ + return m_state == Latched && m_latchedOnTo == src; +} + + +void +LatchManager::latchRequest( const source_ptr& source ) +{ + qDebug() << Q_FUNC_INFO; + if ( isLatched( source ) ) + return; + + m_state = Latching; + m_waitingForLatch = source; + AudioEngine::instance()->playItem( source->getPlaylistInterface().data(), source->getPlaylistInterface()->nextItem() ); +} + +void +LatchManager::playlistChanged( PlaylistInterface* ) +{ + // If we were latched on and changed, send the listening along stop + if ( m_latchedOnTo.isNull() ) + { + if ( m_waitingForLatch.isNull() ) + return; // Neither latched on nor waiting to be latched on, no-op + + m_latchedOnTo = m_waitingForLatch; + m_latchedInterface = m_waitingForLatch->getPlaylistInterface(); + m_waitingForLatch.clear(); + m_state = Latched; + + DatabaseCommand_SocialAction* cmd = new DatabaseCommand_SocialAction(); + cmd->setSource( SourceList::instance()->getLocal() ); + cmd->setAction( "latchOn"); + cmd->setComment( m_latchedOnTo->userName() ); + cmd->setTimestamp( QDateTime::currentDateTime().toTime_t() ); + Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) ); + + // If not, then keep waiting + return; + } + + // We're current latched, and the user changed playlist, so stop + const PlaylistInterface* pi = AudioEngine::instance()->playlist(); + bool listeningAlong = false; + source_ptr newSource; + + if ( pi && dynamic_cast< const SourcePlaylistInterface* >( pi ) ) + { + // Check if we're listening along to someone, to make sure it's not the same person + const SourcePlaylistInterface* sourcepi = dynamic_cast< const SourcePlaylistInterface* >( pi ); + if ( !AudioEngine::instance()->state() == AudioEngine::Stopped ) + { + listeningAlong = true; + newSource = sourcepi->source(); + } + } + + SourcePlaylistInterface* origsourcepi = dynamic_cast< SourcePlaylistInterface* >( m_latchedInterface.data() ); + Q_ASSERT( origsourcepi ); + const source_ptr source = origsourcepi->source(); + + // if we're currently listening along to the same source, no change + if ( listeningAlong && ( !origsourcepi->source().isNull() && origsourcepi->source()->id() == newSource->id() ) ) + return; + + DatabaseCommand_SocialAction* cmd = new DatabaseCommand_SocialAction(); + cmd->setSource( SourceList::instance()->getLocal() ); + cmd->setAction( "latchOff"); + cmd->setComment( source->userName() ); + cmd->setTimestamp( QDateTime::currentDateTime().toTime_t() ); + Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) ); + + m_latchedOnTo.clear(); + m_waitingForLatch.clear(); + m_latchedInterface.clear(); + + m_state = NotLatched; +} + + +void +LatchManager::catchUpRequest() +{ + //it's a catch-up -- logic in audioengine should take care of it + AudioEngine::instance()->next(); +} + +void +LatchManager::unlatchRequest( const source_ptr& source ) +{ + AudioEngine::instance()->playItem( source->getPlaylistInterface().data(), source->getPlaylistInterface()->nextItem() ); + + + AudioEngine::instance()->stop(); + AudioEngine::instance()->setPlaylist( 0 ); +} diff --git a/src/libtomahawk/LatchManager.h b/src/libtomahawk/LatchManager.h new file mode 100644 index 000000000..a52af5aef --- /dev/null +++ b/src/libtomahawk/LatchManager.h @@ -0,0 +1,65 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef LATCHMANAGER_H +#define LATCHMANAGER_H + +#include "dllmacro.h" +#include "source.h" + +#include + +class QState; +class QStateMachine; + +namespace Tomahawk +{ + +class DLLEXPORT LatchManager : public QObject +{ + Q_OBJECT +public: + explicit LatchManager( QObject* parent = 0 ); + virtual ~LatchManager(); + + bool isLatched( const source_ptr& src ); + +public slots: + void latchRequest( const Tomahawk::source_ptr& source ); + void unlatchRequest( const Tomahawk::source_ptr& source ); + void catchUpRequest(); + +private slots: + + void playlistChanged( Tomahawk::PlaylistInterface* ); +private: + enum State { + NotLatched = 0, + Latching, + Latched + }; + + State m_state; + source_ptr m_latchedOnTo; + source_ptr m_waitingForLatch; + playlistinterface_ptr m_latchedInterface; +}; + +} + +#endif // LATCHMANAGER_H diff --git a/src/libtomahawk/audio/audioengine.cpp b/src/libtomahawk/audio/audioengine.cpp index 4e0f9ccc3..1d4ddf66f 100644 --- a/src/libtomahawk/audio/audioengine.cpp +++ b/src/libtomahawk/audio/audioengine.cpp @@ -561,7 +561,10 @@ AudioEngine::playItem( Tomahawk::PlaylistInterface* playlist, const Tomahawk::re else if ( !m_playlist.isNull() && m_playlist.data()->retryMode() == PlaylistInterface::Retry ) { m_waitingOnNewTrack = true; - stop(); + if ( isStopped() ) + sendWaitingNotification(); + else + stop(); } } diff --git a/src/sourcetree/sourcetreeview.cpp b/src/sourcetree/sourcetreeview.cpp index f3af9deee..fb663c9e9 100644 --- a/src/sourcetree/sourcetreeview.cpp +++ b/src/sourcetree/sourcetreeview.cpp @@ -43,14 +43,16 @@ #include "utils/logger.h" #include "items/genericpageitems.h" #include "items/temporarypageitem.h" -#include -#include +#include "database/databasecommand_socialaction.h" +#include "database/database.h" +#include "LatchManager.h" using namespace Tomahawk; SourceTreeView::SourceTreeView( QWidget* parent ) : QTreeView( parent ) + , m_latchManager( new LatchManager( this ) ) , m_dragging( false ) { setFrameShape( QFrame::NoFrame ); @@ -78,8 +80,8 @@ SourceTreeView::SourceTreeView( QWidget* parent ) // setAnimated( true ); m_delegate = new SourceDelegate( this ); - connect( m_delegate, SIGNAL( latchOn( Tomahawk::source_ptr ) ), this, SLOT( doLatchOn( Tomahawk::source_ptr ) ), Qt::QueuedConnection ); - connect( m_delegate, SIGNAL( latchOff( Tomahawk::source_ptr ) ), this, SLOT( doLatchOff( Tomahawk::source_ptr ) ), Qt::QueuedConnection ); + connect( m_delegate, SIGNAL( latchOn( Tomahawk::source_ptr ) ), this, SIGNAL( latchRequest( Tomahawk::source_ptr ) ), Qt::QueuedConnection ); + connect( m_delegate, SIGNAL( latchOff( Tomahawk::source_ptr ) ), this, SIGNAL( unlatchRequest( Tomahawk::source_ptr ) ), Qt::QueuedConnection ); setItemDelegate( m_delegate ); @@ -102,15 +104,15 @@ SourceTreeView::SourceTreeView( QWidget* parent ) showOfflineSources( TomahawkSettings::instance()->showOfflineSources() ); - connect( AudioEngine::instance(), SIGNAL( playlistChanged( Tomahawk::PlaylistInterface* ) ), this, SLOT( playlistChanged( Tomahawk::PlaylistInterface* ) ) ); - connect( AudioEngine::instance(), SIGNAL( stopped() ), this, SLOT( playlistChanged() ) ); - // Light-blue sourcetree on osx #ifdef Q_WS_MAC setStyleSheet( "SourceTreeView:active { background: #DDE4EB; } " "SourceTreeView { background: #EDEDED; } " ); #endif + 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() ) ); } SourceTreeView::~SourceTreeView() @@ -144,18 +146,21 @@ SourceTreeView::setupMenus() source_ptr source = item->source(); if ( !source.isNull() ) { - PlaylistInterface* pi = AudioEngine::instance()->playlist(); - if ( pi && dynamic_cast< SourcePlaylistInterface* >( pi ) ) + if ( m_latchManager->isLatched( source ) ) { - SourcePlaylistInterface* sourcepi = dynamic_cast< SourcePlaylistInterface* >( pi ); - if ( !sourcepi->source().isNull() && sourcepi->source()->id() == source->id() && !AudioEngine::instance()->state() == AudioEngine::Stopped ) - { - m_latchOnAction->setText( tr( "&Catch Up" ) ); - m_latchMenu.addSeparator(); - m_latchOffAction = m_latchMenu.addAction( tr( "&Stop Listening Along" ) ); - connect( m_latchOffAction, SIGNAL( triggered() ), SLOT( latchOff() ) ); - } + m_latchOnAction->setText( tr( "&Catch Up" ) ); + m_latchMenu.addSeparator(); + m_latchOffAction = m_latchMenu.addAction( tr( "&Stop Listening Along" ) ); + connect( m_latchOffAction, SIGNAL( triggered() ), SLOT( latchOff() ) ); } +// PlaylistInterface* pi = AudioEngine::instance()->playlist(); +// if ( pi && dynamic_cast< SourcePlaylistInterface* >( pi ) ) +// { +// SourcePlaylistInterface* sourcepi = dynamic_cast< SourcePlaylistInterface* >( pi ); +// if ( !sourcepi->source().isNull() && sourcepi->source()->id() == source->id() && !AudioEngine::instance()->state() == AudioEngine::Stopped ) +// { +// } +// } } } @@ -189,7 +194,7 @@ SourceTreeView::setupMenus() connect( m_deletePlaylistAction, SIGNAL( triggered() ), SLOT( deletePlaylist() ) ); connect( m_copyPlaylistAction, SIGNAL( triggered() ), SLOT( copyPlaylistLink() ) ); connect( m_addToLocalAction, SIGNAL( triggered() ), SLOT( addToLocal() ) ); - connect( m_latchOnAction, SIGNAL( triggered() ), SLOT( latchOn() ) ); + connect( m_latchOnAction, SIGNAL( triggered() ), SLOT( latchOnOrCatchUp() ) ); } @@ -338,7 +343,7 @@ SourceTreeView::addToLocal() void -SourceTreeView::latchOn() +SourceTreeView::latchOnOrCatchUp() { if ( !m_contextMenuIndex.isValid() ) return; @@ -350,100 +355,10 @@ SourceTreeView::latchOn() CollectionItem* item = itemFromIndex< CollectionItem >( m_contextMenuIndex ); source_ptr source = item->source(); - doLatchOn( source ); -} + if ( m_latchManager->isLatched( source ) ) + emit catchUpRequest(); -void -SourceTreeView::doLatchOn( const source_ptr& source ) -{ - qDebug() << Q_FUNC_INFO; - - PlaylistInterface* pi = AudioEngine::instance()->playlist(); - - bool catchUp = false; - if ( pi && dynamic_cast< SourcePlaylistInterface* >( pi ) ) - { - SourcePlaylistInterface* sourcepi = dynamic_cast< SourcePlaylistInterface* >( pi ); - if ( !sourcepi->source().isNull() && sourcepi->source()->id() == source->id() ) - { - //it's a catch-up -- logic in audioengine should take care of it - AudioEngine::instance()->next(); - catchUp = true; - m_latch = sourcepi->getSharedPointer(); - } - } - - DatabaseCommand_SocialAction* cmd = new DatabaseCommand_SocialAction(); - cmd->setSource( SourceList::instance()->getLocal() ); - cmd->setAction( "latchOn"); - cmd->setComment( source->userName() ); - cmd->setTimestamp( QDateTime::currentDateTime().toTime_t() ); - Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) ); - - if ( !catchUp ) - { - m_waitingToPlayLatch = source; - AudioEngine::instance()->playItem( source->getPlaylistInterface().data(), source->getPlaylistInterface()->nextItem() ); - } -} - -void -SourceTreeView::doLatchOff( const source_ptr& source ) -{ - AudioEngine::instance()->playItem( source->getPlaylistInterface().data(), source->getPlaylistInterface()->nextItem() ); - - - AudioEngine::instance()->stop(); - AudioEngine::instance()->setPlaylist( 0 ); -} - - -void -SourceTreeView::playlistChanged( PlaylistInterface* newInterface ) -{ - Q_UNUSED( newInterface ); - // If we were latched on and changed, send the listening along stop - if ( m_latch.isNull() ) - { - if ( m_waitingToPlayLatch.isNull() ) - return; - - m_latch = m_waitingToPlayLatch->getPlaylistInterface(); - m_waitingToPlayLatch.clear(); - - return; - } - - const PlaylistInterface* pi = AudioEngine::instance()->playlist(); - bool listeningAlong = false; - source_ptr newSource; - - if ( pi && dynamic_cast< const SourcePlaylistInterface* >( pi ) ) - { - const SourcePlaylistInterface* sourcepi = dynamic_cast< const SourcePlaylistInterface* >( pi ); - if ( !AudioEngine::instance()->state() == AudioEngine::Stopped ) - { - listeningAlong = true; - newSource = sourcepi->source(); - } - } - - SourcePlaylistInterface* origsourcepi = dynamic_cast< SourcePlaylistInterface* >( m_latch.data() ); - Q_ASSERT( origsourcepi ); - const source_ptr source = origsourcepi->source(); - - // if we're currently listening along to the same source, no change - if ( listeningAlong && ( !origsourcepi->source().isNull() && origsourcepi->source()->id() == newSource->id() ) ) - return; - - DatabaseCommand_SocialAction* cmd = new DatabaseCommand_SocialAction(); - cmd->setSource( SourceList::instance()->getLocal() ); - cmd->setAction( "latchOff"); - cmd->setComment( source->userName() ); - cmd->setTimestamp( QDateTime::currentDateTime().toTime_t() ); - Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) ); - - m_latch.clear(); + emit latchRequest( source ); } void @@ -460,7 +375,7 @@ SourceTreeView::latchOff() const CollectionItem* item = itemFromIndex< CollectionItem >( m_contextMenuIndex ); const source_ptr source = item->source(); - doLatchOff( source ); + emit unlatchRequest( source ); } diff --git a/src/sourcetree/sourcetreeview.h b/src/sourcetree/sourcetreeview.h index 03a0a7929..96b26b993 100644 --- a/src/sourcetree/sourcetreeview.h +++ b/src/sourcetree/sourcetreeview.h @@ -31,6 +31,11 @@ class SourcesModel; class SourcesProxyModel; class SourceDelegate; +namespace Tomahawk +{ + class LatchManager; +} + class SourceTreeView : public QTreeView { Q_OBJECT @@ -50,6 +55,10 @@ signals: void onOnline( const QModelIndex& index ); void onOffline( const QModelIndex& index ); + void latchRequest( const Tomahawk::source_ptr& source ); + void unlatchRequest( const Tomahawk::source_ptr& source ); + void catchUpRequest(); + private slots: void onItemExpanded( const QModelIndex& idx ); void onItemActivated( const QModelIndex& index ); @@ -61,12 +70,8 @@ private slots: void copyPlaylistLink(); void addToLocal(); - void latchOn(); - void doLatchOn( const Tomahawk::source_ptr& idx ); + void latchOnOrCatchUp(); void latchOff(); - void doLatchOff( const Tomahawk::source_ptr& idx ); - - void playlistChanged( Tomahawk::PlaylistInterface* = 0 ); void onCustomContextMenu( const QPoint& pos ); @@ -91,6 +96,7 @@ private: SourcesProxyModel* m_proxyModel; QModelIndex m_contextMenuIndex; SourceDelegate* m_delegate; + Tomahawk::LatchManager* m_latchManager; QMenu m_playlistMenu; QMenu m_roPlaylistMenu; @@ -103,9 +109,6 @@ private: QAction* m_latchOnAction; QAction* m_latchOffAction; - Tomahawk::source_ptr m_waitingToPlayLatch; - Tomahawk::playlistinterface_ptr m_latch; - bool m_dragging; QRect m_dropRect; QPersistentModelIndex m_dropIndex;