From 597c52f96d07431f1e3f103a92f4f8ce5044d167 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 27 Nov 2010 02:00:42 +0100 Subject: [PATCH] * Added queue feature. * Fixed a few playlist related bugs. --- src/CMakeLists.txt | 4 + src/audio/audioengine.cpp | 22 ++++- src/audio/audioengine.h | 7 +- src/playlist/collectionflatmodel.h | 2 + src/playlist/collectionview.cpp | 35 ++++++++ src/playlist/collectionview.h | 14 ++++ src/playlist/playlistitemdelegate.cpp | 7 +- src/playlist/playlistmanager.cpp | 98 +++++++++++++++++----- src/playlist/playlistmanager.h | 16 +++- src/playlist/playlistmodel.cpp | 25 +++++- src/playlist/playlistmodel.h | 4 +- src/playlist/playlistview.cpp | 13 +-- src/playlist/playlistview.h | 5 +- src/playlist/plitem.cpp | 6 +- src/playlist/plitem.h | 2 +- src/playlist/queueproxymodel.cpp | 38 +++++++++ src/playlist/queueproxymodel.h | 19 +++++ src/playlist/queueview.cpp | 115 ++++++++++++++++++++++++++ src/playlist/queueview.h | 32 +++++++ src/playlist/trackmodel.cpp | 36 ++++++++ src/playlist/trackmodel.h | 9 +- src/playlist/trackproxymodel.cpp | 2 +- src/playlist/trackview.cpp | 28 +++++++ src/playlist/trackview.h | 8 ++ 24 files changed, 497 insertions(+), 50 deletions(-) create mode 100644 src/playlist/queueproxymodel.cpp create mode 100644 src/playlist/queueproxymodel.h create mode 100644 src/playlist/queueview.cpp create mode 100644 src/playlist/queueview.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6b0924fef..ac75818a9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -116,6 +116,8 @@ SET( tomahawkSourcesGui ${tomahawkSourcesGui} playlist/playlistproxymodel.cpp playlist/playlistview.cpp playlist/playlistitemdelegate.cpp + playlist/queueproxymodel.cpp + playlist/queueview.cpp playlist/trackmodel.cpp playlist/trackproxymodel.cpp playlist/trackview.cpp @@ -242,6 +244,8 @@ SET( tomahawkHeadersGui ${tomahawkHeadersGui} playlist/playlistproxymodel.h playlist/playlistview.h playlist/playlistitemdelegate.h + playlist/queueproxymodel.h + playlist/queueview.h playlist/trackmodel.h playlist/trackproxymodel.h playlist/trackview.h diff --git a/src/audio/audioengine.cpp b/src/audio/audioengine.cpp index 235308c9a..d2e01be87 100644 --- a/src/audio/audioengine.cpp +++ b/src/audio/audioengine.cpp @@ -18,6 +18,8 @@ AudioEngine::AudioEngine() : QThread() , m_playlist( 0 ) + , m_currentTrackPlaylist( 0 ) + , m_queue( 0 ) , m_i( 0 ) { qDebug() << "Init AudioEngine"; @@ -151,6 +153,9 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result ) else { m_lastTrack = m_currentTrack; + if ( !m_lastTrack.isNull() ) + emit finished( m_lastTrack ); + m_currentTrack = result; io = TomahawkApp::instance()->getIODeviceForUrl( m_currentTrack ); @@ -256,13 +261,24 @@ AudioEngine::loadNextTrack() { qDebug() << Q_FUNC_INFO; - if ( !m_playlist ) + Tomahawk::result_ptr result; + + if ( m_queue && m_queue->trackCount() ) + { + result = m_queue->nextItem(); + } + + if ( m_playlist && result.isNull() ) + { + result = m_playlist->nextItem(); + } + + if ( result.isNull() ) { stop(); return; } - Tomahawk::result_ptr result = m_playlist->nextItem(); if ( !result.isNull() ) loadTrack( result ); else @@ -276,7 +292,7 @@ AudioEngine::playItem( PlaylistInterface* playlist, const Tomahawk::result_ptr& qDebug() << Q_FUNC_INFO; m_playlist = playlist; - m_currentPlaylist = playlist; + m_currentTrackPlaylist = playlist; loadTrack( result ); } diff --git a/src/audio/audioengine.h b/src/audio/audioengine.h index 608c98acf..7600beb4b 100644 --- a/src/audio/audioengine.h +++ b/src/audio/audioengine.h @@ -30,7 +30,7 @@ public: bool isPaused() const { return m_audio->isPaused(); } /* Returns the PlaylistInterface of the currently playing track. Note: This might be different to the current playlist! */ - PlaylistInterface* currentPlaylist() const { return m_currentPlaylist; } + PlaylistInterface* currentTrackPlaylist() const { return m_currentTrackPlaylist; } /* Returns the PlaylistInterface of the current playlist. Note: The currently playing track might still be from a different playlist! */ PlaylistInterface* playlist() const { return m_playlist; } @@ -50,12 +50,14 @@ public slots: void playItem( PlaylistInterface* playlist, const Tomahawk::result_ptr& result ); void setPlaylist( PlaylistInterface* playlist ) { m_playlist = playlist; } + void setQueue( PlaylistInterface* queue ) { m_queue = queue; } void onTrackAboutToClose(); signals: void loading( const Tomahawk::result_ptr& track ); void started( const Tomahawk::result_ptr& track ); + void finished( const Tomahawk::result_ptr& track ); void stopped(); void paused(); void resumed(); @@ -93,7 +95,8 @@ private: Tomahawk::result_ptr m_currentTrack; Tomahawk::result_ptr m_lastTrack; PlaylistInterface* m_playlist; - PlaylistInterface* m_currentPlaylist; + PlaylistInterface* m_currentTrackPlaylist; + PlaylistInterface* m_queue; QMutex m_mutex; int m_i; diff --git a/src/playlist/collectionflatmodel.h b/src/playlist/collectionflatmodel.h index 4e886ed23..56cc6c1a1 100644 --- a/src/playlist/collectionflatmodel.h +++ b/src/playlist/collectionflatmodel.h @@ -36,6 +36,8 @@ public: void addFilteredCollection( const Tomahawk::collection_ptr& collection, unsigned int amount, DatabaseCommand_AllTracks::SortOrder order ); + virtual void appendTrack( const Tomahawk::query_ptr& query ) {} + signals: void repeatModeChanged( PlaylistInterface::RepeatMode mode ); void shuffleModeChanged( bool enabled ); diff --git a/src/playlist/collectionview.cpp b/src/playlist/collectionview.cpp index e364485ff..d2bf2974b 100644 --- a/src/playlist/collectionview.cpp +++ b/src/playlist/collectionview.cpp @@ -18,6 +18,9 @@ CollectionView::CollectionView( QWidget* parent ) setDragDropMode( QAbstractItemView::DragOnly ); setAcceptDrops( false ); + + setContextMenuPolicy( Qt::CustomContextMenu ); + connect( this, SIGNAL( customContextMenuRequested( const QPoint& ) ), SLOT( onCustomContextMenu( const QPoint& ) ) ); } @@ -34,3 +37,35 @@ CollectionView::dragEnterEvent( QDragEnterEvent* event ) event->ignore(); } + +void +CollectionView::setupMenus() +{ + m_itemMenu.clear(); + + m_playItemAction = m_itemMenu.addAction( tr( "&Play" ) ); + m_addItemsToQueueAction = m_itemMenu.addAction( tr( "Add to &Queue" ) ); + m_itemMenu.addSeparator(); + m_addItemsToPlaylistAction = m_itemMenu.addAction( tr( "&Add to Playlist" ) ); + + connect( m_playItemAction, SIGNAL( triggered() ), SLOT( playItem() ) ); + connect( m_addItemsToQueueAction, SIGNAL( triggered() ), SLOT( addItemsToQueue() ) ); + connect( m_addItemsToPlaylistAction, SIGNAL( triggered() ), SLOT( addItemsToPlaylist() ) ); +} + + +void +CollectionView::onCustomContextMenu( const QPoint& pos ) +{ + qDebug() << Q_FUNC_INFO; + setupMenus(); + + QModelIndex idx = indexAt( pos ); + idx = idx.sibling( idx.row(), 0 ); + setContextMenuIndex( idx ); + + if ( !idx.isValid() ) + return; + + m_itemMenu.exec( mapToGlobal( pos ) ); +} diff --git a/src/playlist/collectionview.h b/src/playlist/collectionview.h index 880970627..9559fc2ed 100644 --- a/src/playlist/collectionview.h +++ b/src/playlist/collectionview.h @@ -1,6 +1,8 @@ #ifndef COLLECTIONVIEW_H #define COLLECTIONVIEW_H +#include + #include "tomahawk/tomahawkapp.h" #include "trackview.h" @@ -12,8 +14,20 @@ public: explicit CollectionView( QWidget* parent = 0 ); ~CollectionView(); +private slots: + void onCustomContextMenu( const QPoint& pos ); + protected: virtual void dragEnterEvent( QDragEnterEvent* event ); + +private: + void setupMenus(); + + QMenu m_itemMenu; + + QAction* m_playItemAction; + QAction* m_addItemsToQueueAction; + QAction* m_addItemsToPlaylistAction; }; #endif // COLLECTIONVIEW_H diff --git a/src/playlist/playlistitemdelegate.cpp b/src/playlist/playlistitemdelegate.cpp index f5a713418..7c7e475bc 100644 --- a/src/playlist/playlistitemdelegate.cpp +++ b/src/playlist/playlistitemdelegate.cpp @@ -6,10 +6,13 @@ #include "tomahawk/query.h" #include "tomahawk/result.h" +#include "tomahawk/tomahawkapp.h" #include "playlist/plitem.h" #include "playlist/trackproxymodel.h" +#include "audio/audioengine.h" + PlaylistItemDelegate::PlaylistItemDelegate( QAbstractItemView* parent, TrackProxyModel* proxy ) : QStyledItemDelegate( (QObject*)parent ) @@ -45,8 +48,10 @@ void PlaylistItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const { PlItem* item = m_model->itemFromIndex( m_model->mapToSource( index ) ); - if ( !item ) + if ( !item || item->query().isNull() ) + { return; + } if ( item->query()->results().count() ) painter->setOpacity( item->query()->results().at( 0 )->score() ); diff --git a/src/playlist/playlistmanager.cpp b/src/playlist/playlistmanager.cpp index 6b8b39389..eb579f35f 100644 --- a/src/playlist/playlistmanager.cpp +++ b/src/playlist/playlistmanager.cpp @@ -1,11 +1,14 @@ #include "playlistmanager.h" +#include + #include "audioengine.h" #include "collectionmodel.h" #include "collectionflatmodel.h" #include "collectionview.h" #include "playlistmodel.h" #include "playlistview.h" +#include "queueview.h" #include "trackproxymodel.h" #include "trackmodel.h" @@ -13,22 +16,42 @@ #define FILTER_TIMEOUT 280 + PlaylistManager::PlaylistManager( QObject* parent ) : QObject( parent ) - , m_widget( new QStackedWidget() ) + , m_widget( new QWidget() ) , m_currentProxyModel( 0 ) , m_currentModel( 0 ) , m_currentView( 0 ) , m_currentMode( 0 ) , m_superCollectionVisible( true ) { - m_widget->setMinimumWidth( 690 ); + m_stack = new QStackedWidget(); + + m_queueView = new QueueView(); + m_queueModel = new PlaylistModel( m_queueView ); + m_queueView->queue()->setModel( m_queueModel ); + APP->audioEngine()->setQueue( m_queueView->queue()->proxyModel() ); + + m_widget->setLayout( new QVBoxLayout() ); + + m_splitter = new QSplitter(); + m_splitter->setOrientation( Qt::Vertical ); + m_splitter->setChildrenCollapsible( false ); + + m_splitter->addWidget( m_stack ); + m_splitter->addWidget( m_queueView ); + m_splitter->setStretchFactor( 0, 3 ); + m_splitter->setStretchFactor( 1, 1 ); + + m_widget->layout()->setMargin( 0 ); + m_widget->layout()->addWidget( m_splitter ); m_superCollectionView = new CollectionView(); m_superCollectionFlatModel = new CollectionFlatModel( m_superCollectionView ); m_superCollectionView->setModel( m_superCollectionFlatModel ); - m_widget->addWidget( m_superCollectionView ); + m_stack->addWidget( m_superCollectionView ); m_currentView = m_superCollectionView; connect( &m_filterTimer, SIGNAL( timeout() ), SLOT( applyFilter() ) ); @@ -41,6 +64,13 @@ PlaylistManager::~PlaylistManager() } +PlaylistView* +PlaylistManager::queue() const +{ + return m_queueView->queue(); +} + + bool PlaylistManager::show( const Tomahawk::playlist_ptr& playlist ) { @@ -61,14 +91,14 @@ PlaylistManager::show( const Tomahawk::playlist_ptr& playlist ) m_playlistViews.insert( playlist, view ); m_views.insert( (PlaylistInterface*)m_currentProxyModel, view ); - m_widget->addWidget( view ); - m_widget->setCurrentWidget( view ); + m_stack->addWidget( view ); + m_stack->setCurrentWidget( view ); m_currentView = view; } else { PlaylistView* view = m_playlistViews.value( playlist ); - m_widget->setCurrentWidget( view ); + m_stack->setCurrentWidget( view ); m_currentProxyModel = view->proxyModel(); m_currentModel = view->model(); m_currentView = view; @@ -102,14 +132,14 @@ PlaylistManager::show( const Tomahawk::album_ptr& album ) m_albumViews.insert( album, view ); m_views.insert( (PlaylistInterface*)m_currentProxyModel, view ); - m_widget->addWidget( view ); - m_widget->setCurrentWidget( view ); + m_stack->addWidget( view ); + m_stack->setCurrentWidget( view ); m_currentView = view; } else { PlaylistView* view = m_albumViews.value( album ); - m_widget->setCurrentWidget( view ); + m_stack->setCurrentWidget( view ); m_currentProxyModel = view->proxyModel(); m_currentModel = view->model(); m_currentView = view; @@ -142,14 +172,14 @@ PlaylistManager::show( const Tomahawk::collection_ptr& collection ) m_collectionViews.insert( collection, view ); m_views.insert( (PlaylistInterface*)m_currentProxyModel, view ); - m_widget->addWidget( view ); - m_widget->setCurrentWidget( view ); + m_stack->addWidget( view ); + m_stack->setCurrentWidget( view ); m_currentView = view; } else { CollectionView* view = m_collectionViews.value( collection ); - m_widget->setCurrentWidget( view ); + m_stack->setCurrentWidget( view ); m_currentProxyModel = view->proxyModel(); m_currentModel = view->model(); m_currentView = view; @@ -176,7 +206,7 @@ PlaylistManager::show( const Tomahawk::source_ptr& source ) { SourceInfoWidget* swidget = new SourceInfoWidget( source ); m_currentInfoWidget = swidget; - m_widget->addWidget( m_currentInfoWidget ); + m_stack->addWidget( m_currentInfoWidget ); m_sourceViews.insert( source, swidget ); } else @@ -184,7 +214,7 @@ PlaylistManager::show( const Tomahawk::source_ptr& source ) m_currentInfoWidget = m_sourceViews.value( source ); } - m_widget->setCurrentWidget( m_currentInfoWidget ); + m_stack->setCurrentWidget( m_currentInfoWidget ); m_superCollectionVisible = false; emit numSourcesChanged( 1 ); @@ -204,7 +234,7 @@ PlaylistManager::showSuperCollection() } } - m_widget->setCurrentWidget( m_superCollectionView ); + m_stack->setCurrentWidget( m_superCollectionView ); m_currentProxyModel = m_superCollectionView->proxyModel(); m_currentModel = m_superCollectionView->model(); m_currentView = m_superCollectionView; @@ -226,7 +256,7 @@ PlaylistManager::setTreeMode() qDebug() << Q_FUNC_INFO; m_currentMode = 1; - m_widget->setCurrentWidget( m_superCollectionView ); + m_stack->setCurrentWidget( m_superCollectionView ); } @@ -236,7 +266,37 @@ PlaylistManager::setTableMode() qDebug() << Q_FUNC_INFO; m_currentMode = 0; - m_widget->setCurrentWidget( m_superCollectionView ); + m_stack->setCurrentWidget( m_superCollectionView ); +} + + +void +PlaylistManager::showQueue() +{ + if ( QThread::currentThread() != thread() ) + { + qDebug() << "Reinvoking in correct thread:" << Q_FUNC_INFO; + QMetaObject::invokeMethod( this, "showQueue", + Qt::QueuedConnection ); + return; + } + + m_queueView->showQueue(); +} + + +void +PlaylistManager::hideQueue() +{ + if ( QThread::currentThread() != thread() ) + { + qDebug() << "Reinvoking in correct thread:" << Q_FUNC_INFO; + QMetaObject::invokeMethod( this, "hideQueue", + Qt::QueuedConnection ); + return; + } + + m_queueView->hideQueue(); } @@ -336,7 +396,7 @@ PlaylistManager::setShuffled( bool enabled ) void PlaylistManager::showCurrentTrack() { - PlaylistInterface* playlist = APP->audioEngine()->currentPlaylist(); + PlaylistInterface* playlist = APP->audioEngine()->currentTrackPlaylist(); if ( m_views.contains( playlist ) ) { @@ -346,7 +406,7 @@ PlaylistManager::showCurrentTrack() m_currentProxyModel = m_currentView->proxyModel(); m_currentModel = m_currentView->model(); - m_widget->setCurrentWidget( m_currentView ); + m_stack->setCurrentWidget( m_currentView ); linkPlaylist(); } diff --git a/src/playlist/playlistmanager.h b/src/playlist/playlistmanager.h index ac70eee39..93a5e736e 100644 --- a/src/playlist/playlistmanager.h +++ b/src/playlist/playlistmanager.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "tomahawk/collection.h" #include "tomahawk/playlistinterface.h" @@ -11,7 +12,9 @@ class CollectionModel; class CollectionFlatModel; class CollectionView; +class PlaylistModel; class PlaylistView; +class QueueView; class TrackProxyModel; class TrackModel; class TrackView; @@ -26,11 +29,10 @@ public: ~PlaylistManager(); QWidget* widget() const { return m_widget; } + PlaylistView* queue() const; bool isSuperCollectionVisible() const { return true; } -// QList views( const Tomahawk::playlist_ptr& playlist ) { return m_views.value( playlist ); } - bool show( const Tomahawk::playlist_ptr& playlist ); bool show( const Tomahawk::album_ptr& album ); bool show( const Tomahawk::collection_ptr& collection ); @@ -52,6 +54,9 @@ public slots: void setTreeMode(); void setTableMode(); + void showQueue(); + void hideQueue(); + void setFilter( const QString& filter ); void setRepeatMode( PlaylistInterface::RepeatMode mode ); @@ -65,7 +70,12 @@ private: void unlinkPlaylist(); void linkPlaylist(); - QStackedWidget* m_widget; + QWidget* m_widget; + QStackedWidget* m_stack; + QSplitter* m_splitter; + + PlaylistModel* m_queueModel; + QueueView* m_queueView; CollectionFlatModel* m_superCollectionFlatModel; CollectionView* m_superCollectionView; diff --git a/src/playlist/playlistmodel.cpp b/src/playlist/playlistmodel.cpp index cbeafe6b1..a60c4db85 100644 --- a/src/playlist/playlistmodel.cpp +++ b/src/playlist/playlistmodel.cpp @@ -14,6 +14,8 @@ PlaylistModel::PlaylistModel( QObject* parent ) , m_waitForUpdate( false ) { qDebug() << Q_FUNC_INFO; + + setReadOnly( false ); } @@ -110,6 +112,19 @@ PlaylistModel::loadAlbum( const Tomahawk::album_ptr& album ) } +void +PlaylistModel::appendTrack( const Tomahawk::query_ptr& query ) +{ + if ( query.isNull() ) + return; + + QList< Tomahawk::query_ptr > ql; + ql << query; + + onTracksAdded( ql ); +} + + void PlaylistModel::onTracksAdded( const QList& tracks, const Tomahawk::collection_ptr& collection ) { @@ -145,7 +160,8 @@ void PlaylistModel::onDataChanged() { PlItem* p = (PlItem*)sender(); - emit dataChanged( p->index, p->index.sibling( p->index.row(), columnCount() - 1 ) ); + if ( p && p->index.isValid() ) + emit dataChanged( p->index, p->index.sibling( p->index.row(), columnCount() - 1 ) ); } @@ -278,6 +294,9 @@ PlaylistModel::removeIndex( const QModelIndex& index ) TrackModel::removeIndex( index ); - m_waitForUpdate = true; - onPlaylistChanged(); + if ( !m_playlist.isNull() ) + { + m_waitForUpdate = true; + onPlaylistChanged(); + } } diff --git a/src/playlist/playlistmodel.h b/src/playlist/playlistmodel.h index adc908649..0159aebbc 100644 --- a/src/playlist/playlistmodel.h +++ b/src/playlist/playlistmodel.h @@ -33,6 +33,8 @@ public: void loadPlaylist( const Tomahawk::playlist_ptr& playlist ); void loadAlbum( const Tomahawk::album_ptr& album ); + void appendTrack( const Tomahawk::query_ptr& query ); + virtual void removeIndex( const QModelIndex& index ); signals: @@ -50,7 +52,7 @@ private slots: void onRevisionLoaded( Tomahawk::PlaylistRevision revision ); void onPlaylistChanged(); - void onTracksAdded( const QList& tracks, const Tomahawk::collection_ptr& collection ); + void onTracksAdded( const QList& tracks, const Tomahawk::collection_ptr& collection = Tomahawk::collection_ptr() ); private: QList playlistEntries() const; diff --git a/src/playlist/playlistview.cpp b/src/playlist/playlistview.cpp index 5027e8bcc..1502adef3 100644 --- a/src/playlist/playlistview.cpp +++ b/src/playlist/playlistview.cpp @@ -39,6 +39,7 @@ PlaylistView::setupMenus() m_itemMenu.clear(); m_playItemAction = m_itemMenu.addAction( tr( "&Play" ) ); + m_addItemsToQueueAction = m_itemMenu.addAction( tr( "Add to &Queue" ) ); m_itemMenu.addSeparator(); m_addItemsToPlaylistAction = m_itemMenu.addAction( tr( "&Add to Playlist" ) ); m_itemMenu.addSeparator(); @@ -48,6 +49,7 @@ PlaylistView::setupMenus() m_deleteItemAction->setEnabled( !model()->isReadOnly() ); connect( m_playItemAction, SIGNAL( triggered() ), SLOT( playItem() ) ); + connect( m_addItemsToQueueAction, SIGNAL( triggered() ), SLOT( addItemsToQueue() ) ); connect( m_addItemsToPlaylistAction, SIGNAL( triggered() ), SLOT( addItemsToPlaylist() ) ); connect( m_deleteItemAction, SIGNAL( triggered() ), SLOT( deleteItem() ) ); } @@ -61,7 +63,7 @@ PlaylistView::onCustomContextMenu( const QPoint& pos ) QModelIndex idx = indexAt( pos ); idx = idx.sibling( idx.row(), 0 ); - m_contextMenuIndex = idx; + setContextMenuIndex( idx ); if ( !idx.isValid() ) return; @@ -87,13 +89,6 @@ PlaylistView::keyPressEvent( QKeyEvent* event ) } -void -PlaylistView::playItem() -{ - onItemActivated( m_contextMenuIndex ); -} - - void PlaylistView::addItemsToPlaylist() { @@ -103,5 +98,5 @@ PlaylistView::addItemsToPlaylist() void PlaylistView::deleteItem() { - proxyModel()->removeIndex( m_contextMenuIndex ); + proxyModel()->removeIndex( contextMenuIndex() ); } diff --git a/src/playlist/playlistview.h b/src/playlist/playlistview.h index 0addecccf..a28cc4a69 100644 --- a/src/playlist/playlistview.h +++ b/src/playlist/playlistview.h @@ -22,17 +22,16 @@ protected: private slots: void onCustomContextMenu( const QPoint& pos ); - void playItem(); void addItemsToPlaylist(); void deleteItem(); private: void setupMenus(); - QModelIndex m_contextMenuIndex; - QMenu m_itemMenu; + QAction* m_playItemAction; + QAction* m_addItemsToQueueAction; QAction* m_addItemsToPlaylistAction; QAction* m_deleteItemAction; }; diff --git a/src/playlist/plitem.cpp b/src/playlist/plitem.cpp index efaa0eb70..8a680fcfd 100644 --- a/src/playlist/plitem.cpp +++ b/src/playlist/plitem.cpp @@ -12,13 +12,13 @@ PlItem::~PlItem() // Don't use qDeleteAll here! The children will remove themselves // from the list when they get deleted and the qDeleteAll iterator // will fail badly! - for ( int i = children.count() - 1; i >= 0; i-- ) - delete children.at( i ); - if ( parent && index.isValid() ) { parent->children.removeAt( index.row() ); } + + for ( int i = children.count() - 1; i >= 0; i-- ) + delete children.at( i ); } diff --git a/src/playlist/plitem.h b/src/playlist/plitem.h index 5cd41e471..7be700336 100644 --- a/src/playlist/plitem.h +++ b/src/playlist/plitem.h @@ -22,7 +22,7 @@ public: explicit PlItem( const Tomahawk::plentry_ptr& entry, PlItem* parent = 0, int row = -1 ); const Tomahawk::plentry_ptr& entry() const { return m_entry; }; - const Tomahawk::query_ptr& query() const { return m_query; }; + const Tomahawk::query_ptr& query() const { if ( !m_entry.isNull() ) return m_entry->query(); else return m_query; }; bool isPlaying() { return m_isPlaying; } void setIsPlaying( bool b ) { m_isPlaying = b; emit dataChanged(); } diff --git a/src/playlist/queueproxymodel.cpp b/src/playlist/queueproxymodel.cpp new file mode 100644 index 000000000..e55da1bbb --- /dev/null +++ b/src/playlist/queueproxymodel.cpp @@ -0,0 +1,38 @@ +#include "queueproxymodel.h" + +#include + +#include "tomahawk/tomahawkapp.h" +#include "playlist/playlistmanager.h" + +using namespace Tomahawk; + + +QueueProxyModel::QueueProxyModel( QObject* parent ) + : PlaylistProxyModel( parent ) +{ + qDebug() << Q_FUNC_INFO; +} + + +QueueProxyModel::~QueueProxyModel() +{ +} + + +Tomahawk::result_ptr +QueueProxyModel::siblingItem( int itemsAway ) +{ + qDebug() << Q_FUNC_INFO << rowCount( QModelIndex() ); + + setCurrentItem( QModelIndex() ); + Tomahawk::result_ptr res = PlaylistProxyModel::siblingItem( itemsAway ); + + qDebug() << "new rowcount:" << rowCount( QModelIndex() ); + if ( rowCount( QModelIndex() ) == 1 ) + APP->playlistManager()->hideQueue(); + + removeIndex( currentItem() ); + + return res; +} diff --git a/src/playlist/queueproxymodel.h b/src/playlist/queueproxymodel.h new file mode 100644 index 000000000..6cf280694 --- /dev/null +++ b/src/playlist/queueproxymodel.h @@ -0,0 +1,19 @@ +#ifndef QUEUEPROXYMODEL_H +#define QUEUEPROXYMODEL_H + +#include "playlistproxymodel.h" + +class QMetaData; + +class QueueProxyModel : public PlaylistProxyModel +{ +Q_OBJECT + +public: + explicit QueueProxyModel( QObject* parent = 0 ); + ~QueueProxyModel(); + + virtual Tomahawk::result_ptr siblingItem( int itemsAway ); +}; + +#endif // QUEUEPROXYMODEL_H diff --git a/src/playlist/queueview.cpp b/src/playlist/queueview.cpp new file mode 100644 index 000000000..812ba3fa8 --- /dev/null +++ b/src/playlist/queueview.cpp @@ -0,0 +1,115 @@ +#include "queueview.h" + +#include +#include +#include + +#include "playlist/queueproxymodel.h" + +using namespace Tomahawk; + + +QueueView::QueueView( QWidget* parent ) + : QWidget( parent ) + , m_prevHeight( 175 ) +{ + setMinimumHeight( 25 ); + setMaximumHeight( 25 ); + setLayout( new QVBoxLayout() ); + + m_queue = new PlaylistView( this ); + m_queue->setProxyModel( new QueueProxyModel( this ) ); + + m_button = new QPushButton(); + m_button->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ); + m_button->setText( tr( "Click to show queue" ) ); + connect( m_button, SIGNAL( clicked() ), SLOT( showQueue() ) ); + + layout()->setMargin( 0 ); + layout()->addWidget( m_button ); + layout()->addWidget( m_queue ); + + m_queue->hide(); +} + + +QueueView::~QueueView() +{ + qDebug() << Q_FUNC_INFO; +} + + +void +QueueView::showQueue() +{ + if ( !m_queue->isHidden() ) + return; + + m_button->setText( tr( "Click to hide queue" ) ); + disconnect( m_button, SIGNAL( clicked() ), this, SLOT( showQueue() ) ); + connect( m_button, SIGNAL( clicked() ), SLOT( hideQueue() ) ); + + m_queue->setMaximumHeight( 0 ); + m_queue->show(); + + QTimeLine *timeLine = new QTimeLine( 300, this ); + timeLine->setFrameRange( 0, m_prevHeight ); + timeLine->setUpdateInterval( 20 ); + connect( timeLine, SIGNAL( frameChanged( int ) ), SLOT( onAnimationStep( int ) ) ); + connect( timeLine, SIGNAL( finished() ), SLOT( onAnimationFinished() ) ); + timeLine->start(); +} + + +void +QueueView::hideQueue() +{ + if ( m_queue->isHidden() ) + return; + + m_button->setText( tr( "Click to show queue" ) ); + disconnect( m_button, SIGNAL( clicked() ), this, SLOT( hideQueue() ) ); + connect( m_button, SIGNAL( clicked() ), SLOT( showQueue() ) ); + + m_prevHeight = height() - 25; + QTimeLine *timeLine = new QTimeLine( 300, this ); + timeLine->setFrameRange( 0, m_prevHeight ); + timeLine->setUpdateInterval( 20 ); + timeLine->setDirection( QTimeLine::Backward ); + connect( timeLine, SIGNAL( frameChanged( int ) ), SLOT( onAnimationStep( int ) ) ); + connect( timeLine, SIGNAL( finished() ), SLOT( onAnimationFinished() ) ); + timeLine->start(); +} + + +void +QueueView::onAnimationStep( int frame ) +{ + setMinimumHeight( frame + 25 ); + setMaximumHeight( frame + 25 ); + m_queue->setMaximumHeight( frame ); + + m_queue->resize( m_queue->width(), frame ); +} + + +void +QueueView::onAnimationFinished() +{ + qDebug() << Q_FUNC_INFO << maximumHeight(); + + if ( maximumHeight() < 32 ) + { + setMinimumHeight( 25 ); + setMaximumHeight( 25 ); + m_queue->hide(); + } + else + { + setMinimumHeight( 200 ); + m_queue->setMaximumHeight( QWIDGETSIZE_MAX ); + setMaximumHeight( QWIDGETSIZE_MAX ); + } + + sender()->deleteLater(); +} diff --git a/src/playlist/queueview.h b/src/playlist/queueview.h new file mode 100644 index 000000000..a7bd00284 --- /dev/null +++ b/src/playlist/queueview.h @@ -0,0 +1,32 @@ +#ifndef QUEUEVIEW_H +#define QUEUEVIEW_H + +#include + +#include "playlistview.h" + +class QueueView : public QWidget +{ +Q_OBJECT + +public: + explicit QueueView( QWidget* parent = 0 ); + ~QueueView(); + + PlaylistView* queue() const { return m_queue; } + +public slots: + void showQueue(); + void hideQueue(); + +private slots: + void onAnimationStep( int frame ); + void onAnimationFinished(); + +private: + PlaylistView* m_queue; + QPushButton* m_button; + unsigned int m_prevHeight; +}; + +#endif // QUEUEVIEW_H diff --git a/src/playlist/trackmodel.cpp b/src/playlist/trackmodel.cpp index 61ae496a7..847eae12b 100644 --- a/src/playlist/trackmodel.cpp +++ b/src/playlist/trackmodel.cpp @@ -16,6 +16,9 @@ TrackModel::TrackModel( QObject* parent ) , m_readOnly( true ) { qDebug() << Q_FUNC_INFO; + + connect( (QObject*)APP->audioEngine(), SIGNAL( finished( Tomahawk::result_ptr ) ), SLOT( onPlaybackFinished( Tomahawk::result_ptr ) ), Qt::DirectConnection ); + connect( (QObject*)APP->audioEngine(), SIGNAL( stopped() ), SLOT( onPlaybackStopped() ), Qt::DirectConnection ); } @@ -272,6 +275,16 @@ TrackModel::mimeData( const QModelIndexList &indexes ) const void TrackModel::removeIndex( const QModelIndex& index ) { + if ( QThread::currentThread() != thread() ) + { + qDebug() << "Reinvoking in correct thread:" << Q_FUNC_INFO; + QMetaObject::invokeMethod( this, "removeIndex", + Qt::QueuedConnection, + Q_ARG(const QModelIndex, index) + ); + return; + } + qDebug() << Q_FUNC_INFO; if ( index.column() > 0 ) @@ -309,3 +322,26 @@ TrackModel::itemFromIndex( const QModelIndex& index ) const return m_rootItem; } } + + +void +TrackModel::onPlaybackFinished( const Tomahawk::result_ptr& result ) +{ + PlItem* oldEntry = itemFromIndex( m_currentIndex ); + qDebug() << oldEntry->query(); + if ( oldEntry && !oldEntry->query().isNull() && oldEntry->query()->results().contains( result ) ) + { + oldEntry->setIsPlaying( false ); + } +} + + +void +TrackModel::onPlaybackStopped() +{ + PlItem* oldEntry = itemFromIndex( m_currentIndex ); + if ( oldEntry ) + { + oldEntry->setIsPlaying( false ); + } +} diff --git a/src/playlist/trackmodel.h b/src/playlist/trackmodel.h index 2ecd7088d..3644002c4 100644 --- a/src/playlist/trackmodel.h +++ b/src/playlist/trackmodel.h @@ -39,7 +39,6 @@ public: virtual QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const; virtual QVariant headerData( int section, Qt::Orientation orientation, int role ) const; - virtual void removeIndex( const QModelIndex& index ); virtual void removeIndexes( const QList& indexes ); virtual Tomahawk::result_ptr siblingItem( int direction ) { return Tomahawk::result_ptr(); } @@ -54,6 +53,8 @@ public: virtual PlaylistInterface::RepeatMode repeatMode() const { return PlaylistInterface::NoRepeat; } virtual bool shuffled() const { return false; } + virtual void appendTrack( const Tomahawk::query_ptr& query ) = 0; + PlItem* itemFromIndex( const QModelIndex& index ) const; PlItem* m_rootItem; @@ -67,12 +68,18 @@ signals: public slots: virtual void setCurrentItem( const QModelIndex& index ); + virtual void removeIndex( const QModelIndex& index ); + virtual void setRepeatMode( PlaylistInterface::RepeatMode mode ) {} virtual void setShuffled( bool shuffled ) {} protected: virtual void setReadOnly( bool b ) { m_readOnly = b; } +private slots: + void onPlaybackFinished( const Tomahawk::result_ptr& result ); + void onPlaybackStopped(); + private: QPersistentModelIndex m_currentIndex; bool m_readOnly; diff --git a/src/playlist/trackproxymodel.cpp b/src/playlist/trackproxymodel.cpp index 34379f843..8bfe1a42c 100644 --- a/src/playlist/trackproxymodel.cpp +++ b/src/playlist/trackproxymodel.cpp @@ -163,7 +163,7 @@ TrackProxyModel::removeIndex( const QModelIndex& index ) if ( !sourceModel() ) return; - if ( index.column() > 0 ) + if ( !index.isValid() ) return; sourceModel()->removeIndex( mapToSource( index ) ); diff --git a/src/playlist/trackview.cpp b/src/playlist/trackview.cpp index 4d83b4616..c0c2ecef1 100644 --- a/src/playlist/trackview.cpp +++ b/src/playlist/trackview.cpp @@ -7,6 +7,8 @@ #include #include "tomahawk/tomahawkapp.h" +#include "playlist/playlistmanager.h" +#include "playlist/queueview.h" #include "audioengine.h" #include "tomahawksettings.h" #include "trackmodel.h" @@ -31,6 +33,7 @@ TrackView::TrackView( QWidget* parent ) setDragDropMode( QAbstractItemView::InternalMove ); setDragDropOverwriteMode( false ); setAllColumnsShowFocus( true ); + setMinimumWidth( 690 ); #ifndef Q_WS_WIN QFont f = font(); @@ -149,6 +152,31 @@ TrackView::onItemResized( const QModelIndex& index ) } +void +TrackView::playItem() +{ + onItemActivated( m_contextMenuIndex ); +} + + +void +TrackView::addItemsToQueue() +{ + foreach( const QModelIndex& idx, selectedIndexes() ) + { + if ( idx.column() ) + continue; + + PlItem* item = model()->itemFromIndex( proxyModel()->mapToSource( idx ) ); + if ( item && item->query()->numResults() ) + { + APP->playlistManager()->queue()->model()->appendTrack( item->query() ); + APP->playlistManager()->showQueue(); + } + } +} + + void TrackView::resizeEvent( QResizeEvent* event ) { diff --git a/src/playlist/trackview.h b/src/playlist/trackview.h index e376ff6f6..9b594671f 100644 --- a/src/playlist/trackview.h +++ b/src/playlist/trackview.h @@ -26,9 +26,15 @@ public: void setModel( TrackModel* model ); + QModelIndex contextMenuIndex() const { return m_contextMenuIndex; } + void setContextMenuIndex( const QModelIndex& idx ) { m_contextMenuIndex = idx; } + public slots: void onItemActivated( const QModelIndex& index ); + void playItem(); + void addItemsToQueue(); + protected: virtual void resizeEvent( QResizeEvent* event ); @@ -65,6 +71,8 @@ private: bool m_resizing; bool m_dragging; QRect m_dropRect; + + QModelIndex m_contextMenuIndex; }; #endif // TRACKVIEW_H