diff --git a/include/tomahawk/tomahawkapp.h b/include/tomahawk/tomahawkapp.h index 839022079..7be7cf29e 100644 --- a/include/tomahawk/tomahawkapp.h +++ b/include/tomahawk/tomahawkapp.h @@ -92,6 +92,7 @@ private: AudioEngine* m_audioEngine; SipHandler* m_sipHandler; + Servent* m_servent; XMPPBot* m_xmppBot; #ifndef NO_LIBLASTFM diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3013e524d..ed5392389 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -96,6 +96,8 @@ SET( tomahawkSourcesGui ${tomahawkSourcesGui} infowidgets/sourceinfowidget.cpp + widgets/newplaylistwidget.cpp + transferview.cpp tomahawkwindow.cpp tomahawktrayicon.cpp @@ -173,6 +175,8 @@ SET( tomahawkHeadersGui ${tomahawkHeadersGui} infowidgets/sourceinfowidget.h + widgets/newplaylistwidget.h + transferview.h tomahawkwindow.h tomahawktrayicon.h @@ -189,6 +193,7 @@ SET( tomahawkUI ${tomahawkUI} sourcetree/sourcetreeitemwidget.ui topbar/topbar.ui infowidgets/sourceinfowidget.ui + widgets/newplaylistwidget.ui ) INCLUDE_DIRECTORIES( diff --git a/src/libtomahawk/database/databasecommand_resolve.cpp b/src/libtomahawk/database/databasecommand_resolve.cpp index 0bd1f915d..9f76cc22b 100644 --- a/src/libtomahawk/database/databasecommand_resolve.cpp +++ b/src/libtomahawk/database/databasecommand_resolve.cpp @@ -151,11 +151,7 @@ DatabaseCommand_Resolve::exec( DatabaseImpl* lib ) res << Tomahawk::result_ptr( new Tomahawk::Result( m, coll ) ); } - // return results, if any found - if( res.length() > 0 ) - { - emit results( qid, res ); - } + emit results( qid, res ); } diff --git a/src/libtomahawk/database/databasecommand_resolve.h b/src/libtomahawk/database/databasecommand_resolve.h index 81ab23263..a8ef660ba 100644 --- a/src/libtomahawk/database/databasecommand_resolve.h +++ b/src/libtomahawk/database/databasecommand_resolve.h @@ -20,9 +20,7 @@ public: virtual void exec(DatabaseImpl *lib); - signals: - void results( Tomahawk::QID qid, QList results ); public slots: diff --git a/src/libtomahawk/pipeline.cpp b/src/libtomahawk/pipeline.cpp index 92951bf19..4344d4956 100644 --- a/src/libtomahawk/pipeline.cpp +++ b/src/libtomahawk/pipeline.cpp @@ -36,14 +36,10 @@ Pipeline::databaseReady() void Pipeline::indexReady() { - qDebug() << Q_FUNC_INFO << "shuting this many pending queries:" << m_queries_pending.size(); + qDebug() << Q_FUNC_INFO << "shunting this many pending queries:" << m_queries_pending.size(); m_index_ready = true; - foreach( const query_ptr& q, m_queries_pending ) - { - q->setLastPipelineWeight( 101 ); - shunt( q ); - } - m_queries_pending.clear(); + + shuntNext(); } @@ -77,7 +73,7 @@ Pipeline::addResolver( Resolver* r, bool sort ) void -Pipeline::add( const QList& qlist ) +Pipeline::add( const QList& qlist, bool prioritized ) { { QMutexLocker lock( &m_mut ); @@ -91,36 +87,28 @@ Pipeline::add( const QList& qlist ) } } - /* - Since resolvers are async, we now dispatch to the highest weighted ones - and after timeout, dispatch to next highest etc, aborting when solved - - If index not yet loaded, leave in the pending list instead. - (they are shunted when index is ready) - */ - if( m_index_ready ) + if ( prioritized ) { - foreach( const query_ptr& q, qlist ) - { - q->setLastPipelineWeight( 101 ); - shunt( q ); // bump into next stage of pipeline (highest weights are 100) - } + for( int i = qlist.count() - 1; i >= 0; i-- ) + m_queries_pending.insert( 0, qlist.at( i ) ); } else { - qDebug() << "Placing query in pending queue - index not ready yet"; m_queries_pending.append( qlist ); } + + if ( m_index_ready ) + shuntNext(); } void -Pipeline::add( const query_ptr& q ) +Pipeline::add( const query_ptr& q, bool prioritized ) { //qDebug() << Q_FUNC_INFO << (qlonglong)q.data() << q->toString(); QList< query_ptr > qlist; qlist << q; - add( qlist ); + add( qlist, prioritized ); } @@ -135,18 +123,36 @@ Pipeline::reportResults( QID qid, const QList< result_ptr >& results ) return; } - const query_ptr& q = m_qids.value( qid ); - //qDebug() << Q_FUNC_INFO << qid; - //qDebug() << "solved query:" << (qlonglong)q.data() << q->toString(); - q->addResults( results ); - - //qDebug() << "Results for " << q->toString() << ", just added" << results.length(); - foreach( const result_ptr& r, q->results() ) + if ( !results.isEmpty() ) { - m_rids.insert( r->id(), r ); - //qDebug() << "* " << (results.contains(r) ? "NEW" : "") << r->toString(); - } + //qDebug() << Q_FUNC_INFO << qid; + //qDebug() << "solved query:" << (qlonglong)q.data() << q->toString(); + const query_ptr& q = m_qids.value( qid ); + q->addResults( results ); + + foreach( const result_ptr& r, q->results() ) + { + m_rids.insert( r->id(), r ); + } + } +} + + +void +Pipeline::shuntNext() +{ + if ( m_queries_pending.isEmpty() ) + return; + + /* + Since resolvers are async, we now dispatch to the highest weighted ones + and after timeout, dispatch to next highest etc, aborting when solved + */ + + query_ptr q = m_queries_pending.takeFirst(); + q->setLastPipelineWeight( 101 ); + shunt( q ); // bump into next stage of pipeline (highest weights are 100) } @@ -156,7 +162,7 @@ Pipeline::shunt( const query_ptr& q ) if( q->solved() ) { qDebug() << "Query solved, pipeline aborted:" << q->toString() - << "numresults:" << q->results().length(); + << "numresults:" << q->results().length(); return; } unsigned int lastweight = 0; @@ -197,6 +203,8 @@ Pipeline::shunt( const query_ptr& q ) //qDebug() << "Reached end of pipeline for:" << q->toString(); // reached end of pipeline } + + shuntNext(); } diff --git a/src/libtomahawk/pipeline.h b/src/libtomahawk/pipeline.h index e62cad4f9..26429ea0e 100644 --- a/src/libtomahawk/pipeline.h +++ b/src/libtomahawk/pipeline.h @@ -49,12 +49,14 @@ public: } public slots: - void add( const query_ptr& q ); - void add( const QList& qlist ); + void add( const query_ptr& q, bool prioritized = true ); + void add( const QList& qlist, bool prioritized = true ); void databaseReady(); private slots: void shunt( const query_ptr& q ); + void shuntNext(); + void indexReady(); private: diff --git a/src/playlist/playlistmanager.cpp b/src/playlist/playlistmanager.cpp index 555bbc01d..7f771854c 100644 --- a/src/playlist/playlistmanager.cpp +++ b/src/playlist/playlistmanager.cpp @@ -66,6 +66,8 @@ PlaylistManager::PlaylistManager( QObject* parent ) m_currentInterface = m_superCollectionView->proxyModel(); connect( &m_filterTimer, SIGNAL( timeout() ), SLOT( applyFilter() ) ); + + linkPlaylist(); } @@ -248,6 +250,27 @@ PlaylistManager::show( const Tomahawk::source_ptr& source ) } +bool +PlaylistManager::show( QWidget* widget ) +{ + unlinkPlaylist(); + + connect( widget, SIGNAL( destroyed( QWidget* ) ), SLOT( onWidgetDestroyed( QWidget* ) ) ); + + m_stack->addWidget( widget ); + m_stack->setCurrentWidget( widget ); + + m_superCollectionVisible = false; + m_statsAvailable = false; + m_modesAvailable = false; + m_currentInterface = 0; + + linkPlaylist(); + + return true; +} + + bool PlaylistManager::showSuperCollection() { @@ -414,6 +437,9 @@ PlaylistManager::linkPlaylist() connect( m_currentInterface->object(), SIGNAL( shuffleModeChanged( bool ) ), this, SIGNAL( shuffleModeChanged( bool ) ) ); + + m_interfaceHistory.removeAll( m_currentInterface ); + m_interfaceHistory << m_currentInterface; } applyFilter(); @@ -433,6 +459,29 @@ PlaylistManager::linkPlaylist() } +void +PlaylistManager::onWidgetDestroyed( QWidget* widget ) +{ + qDebug() << "Destroyed child:" << widget; + + bool resetWidget = ( m_stack->currentWidget() == widget ); + m_stack->removeWidget( widget ); + + if ( resetWidget && m_interfaceHistory.count() ) + { + unlinkPlaylist(); + + m_currentInterface = m_interfaceHistory.last(); + qDebug() << "Last interface:" << m_currentInterface << m_currentInterface->widget(); + + if ( m_currentInterface->widget() ) + m_stack->setCurrentWidget( m_currentInterface->widget() ); + + linkPlaylist(); + } +} + + void PlaylistManager::setRepeatMode( PlaylistInterface::RepeatMode mode ) { diff --git a/src/playlist/playlistmanager.h b/src/playlist/playlistmanager.h index cfeaf52f4..15ae57653 100644 --- a/src/playlist/playlistmanager.h +++ b/src/playlist/playlistmanager.h @@ -39,8 +39,10 @@ public: bool show( const Tomahawk::album_ptr& album ); bool show( const Tomahawk::collection_ptr& collection ); bool show( const Tomahawk::source_ptr& source ); - bool showSuperCollection(); + bool show( QWidget* widget ); + + bool showSuperCollection(); void showCurrentTrack(); signals: @@ -71,6 +73,8 @@ public slots: private slots: void applyFilter(); + void onWidgetDestroyed( QWidget* widget ); + private: void unlinkPlaylist(); void linkPlaylist(); @@ -97,6 +101,7 @@ private: QHash< Tomahawk::source_ptr, SourceInfoWidget* > m_sourceViews; PlaylistInterface* m_currentInterface; + QList m_interfaceHistory; QWidget* m_currentInfoWidget; diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp index 760ad5f84..875ed2fcf 100644 --- a/src/tomahawkapp.cpp +++ b/src/tomahawkapp.cpp @@ -110,7 +110,7 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] ) new Pipeline( this ); new SourceList( this ); - new Servent( this ); + m_servent = new Servent( this ); #ifdef TOMAHAWK_HEADLESS m_headless = true; @@ -212,6 +212,7 @@ TomahawkApp::~TomahawkApp() qDebug() << Q_FUNC_INFO; delete m_sipHandler; + delete m_servent; #ifndef TOMAHAWK_HEADLESS delete m_mainwindow; diff --git a/src/tomahawkwindow.cpp b/src/tomahawkwindow.cpp index 30268713f..426ea6793 100644 --- a/src/tomahawkwindow.cpp +++ b/src/tomahawkwindow.cpp @@ -21,6 +21,8 @@ #include "sip/SipHandler.h" +#include "widgets/newplaylistwidget.h" + #include "audiocontrols.h" #include "network/controlconnection.h" #include "database/database.h" @@ -324,7 +326,9 @@ TomahawkWindow::createPlaylist() { qDebug() << Q_FUNC_INFO; - bool ok; + playlistManager()->show( new NewPlaylistWidget() ); + +/* bool ok; QString name = QInputDialog::getText( this, "Create New Playlist", "Name:", QLineEdit::Normal, "New Playlist", &ok ); if ( !ok || name.isEmpty() ) return; @@ -333,7 +337,7 @@ TomahawkWindow::createPlaylist() QString id = uuid(); QString info = ""; // FIXME QString creator = "someone"; // FIXME - Playlist::create( author, id, name, info, creator, false /* shared */ ); + Playlist::create( author, id, name, info, creator, false );*/ } diff --git a/src/widgets/newplaylistwidget.cpp b/src/widgets/newplaylistwidget.cpp new file mode 100644 index 000000000..c7af50674 --- /dev/null +++ b/src/widgets/newplaylistwidget.cpp @@ -0,0 +1,131 @@ +#include "newplaylistwidget.h" +#include "ui_newplaylistwidget.h" + +#include +#include + +#include "tomahawk/tomahawkapp.h" +#include "utils/tomahawkutils.h" + +#include "playlist/playlistmanager.h" +#include "playlist/playlistmodel.h" + +#include "pipeline.h" +#include "xspfloader.h" + +#include "sourcelist.h" + +#define FILTER_TIMEOUT 280 + + +NewPlaylistWidget::NewPlaylistWidget( QWidget* parent ) + : QWidget( parent ) + , ui( new Ui::NewPlaylistWidget ) +{ + ui->setupUi( this ); + + QPushButton* saveButton = new QPushButton( tr( "&Create Playlist" ) ); + saveButton->setDefault( true ); + + ui->buttonBox->addButton( saveButton, QDialogButtonBox::AcceptRole ); + + connect( ui->tagEdit, SIGNAL( textChanged( QString ) ), SLOT( tagChanged() ) ); + connect( ui->buttonBox, SIGNAL( accepted() ), SLOT( savePlaylist() ) ); + connect( ui->buttonBox, SIGNAL( rejected() ), SLOT( cancel() ) ); + + m_suggestionsModel = new PlaylistModel( ui->suggestionsView ); + ui->suggestionsView->setModel( m_suggestionsModel ); + + connect( &m_filterTimer, SIGNAL( timeout() ), SLOT( updateSuggestions() ) ); +} + + +NewPlaylistWidget::~NewPlaylistWidget() +{ + delete ui; +} + + +void +NewPlaylistWidget::changeEvent( QEvent* e ) +{ + QWidget::changeEvent( e ); + switch ( e->type() ) + { + case QEvent::LanguageChange: + ui->retranslateUi( this ); + break; + + default: + break; + } +} + + +void +NewPlaylistWidget::tagChanged() +{ + m_tag = ui->tagEdit->text(); + + m_filterTimer.stop(); + m_filterTimer.setInterval( FILTER_TIMEOUT ); + m_filterTimer.setSingleShot( true ); + m_filterTimer.start(); +} + + +void +NewPlaylistWidget::updateSuggestions() +{ + QUrl url( QString( "http://ws.audioscrobbler.com/1.0/tag/%1/toptracks.xspf" ).arg( m_tag ) ); + + XSPFLoader* loader = new XSPFLoader( false ); + connect( loader, SIGNAL( ok( Tomahawk::playlist_ptr ) ), SLOT( suggestionsFound() ) ); + + loader->load( url ); +} + + +void +NewPlaylistWidget::suggestionsFound() +{ + XSPFLoader* loader = qobject_cast( sender() ); + + m_entries = loader->entries(); + + delete m_suggestionsModel; + m_suggestionsModel = new PlaylistModel( ui->suggestionsView ); + ui->suggestionsView->setModel( m_suggestionsModel ); + + QList ql; + foreach( const Tomahawk::plentry_ptr& entry, m_entries ) + { + m_suggestionsModel->appendTrack( entry->query() ); + ql.append( entry->query() ); + } + + Tomahawk::Pipeline::instance()->add( ql ); + + loader->deleteLater(); +} + + +void +NewPlaylistWidget::savePlaylist() +{ + Tomahawk::playlist_ptr playlist; + + playlist = Tomahawk::Playlist::create( SourceList::instance()->getLocal(), uuid(), ui->titleEdit->text(), "", "", false ); + playlist->createNewRevision( uuid(), playlist->currentrevision(), m_entries ); + + APP->playlistManager()->show( playlist ); + cancel(); +} + + +void +NewPlaylistWidget::cancel() +{ + emit destroyed( this ); + deleteLater(); +} diff --git a/src/widgets/newplaylistwidget.h b/src/widgets/newplaylistwidget.h new file mode 100644 index 000000000..c20ef73f4 --- /dev/null +++ b/src/widgets/newplaylistwidget.h @@ -0,0 +1,50 @@ +#ifndef NEWPLAYLISTWIDGET_H +#define NEWPLAYLISTWIDGET_H + +#include +#include + +#include "album.h" +#include "result.h" +#include "playlistinterface.h" + +class PlaylistModel; + +namespace Ui +{ + class NewPlaylistWidget; +} + +class NewPlaylistWidget : public QWidget +{ +Q_OBJECT + +public: + NewPlaylistWidget( QWidget* parent = 0 ); + ~NewPlaylistWidget(); + +protected: + void changeEvent( QEvent* e ); + +signals: + void destroyed( QWidget* widget ); + +private slots: + void tagChanged(); + void updateSuggestions(); + void suggestionsFound(); + + void savePlaylist(); + void cancel(); + +private: + Ui::NewPlaylistWidget *ui; + + PlaylistModel* m_suggestionsModel; + QList< Tomahawk::plentry_ptr > m_entries; + + QTimer m_filterTimer; + QString m_tag; +}; + +#endif // NEWPLAYLISTWIDGET_H diff --git a/src/widgets/newplaylistwidget.ui b/src/widgets/newplaylistwidget.ui new file mode 100644 index 000000000..f6095dd78 --- /dev/null +++ b/src/widgets/newplaylistwidget.ui @@ -0,0 +1,111 @@ + + + NewPlaylistWidget + + + + 0 + 0 + 985 + 460 + + + + + + + + 20 + 75 + true + + + + Create A New Playlist + + + false + + + + + + + + 14 + + + + Enter a title for the new playlist: + + + + + + + + 0 + 26 + + + + + + + + + 14 + + + + Tomahawk offers a variety of ways to help you create playlists and find music you enjoy! + + + true + + + + + + + + 14 + + + + Just enter a genre or tag name and Tomahawk will suggest a few songs to get you started with your new playlist: + + + + + + + + 0 + 26 + + + + + + + + + + + QDialogButtonBox::Cancel + + + + + + + + PlaylistView + QTreeView +
playlistview.h
+
+
+ + +
diff --git a/src/xmppbot/xmppbot.cpp b/src/xmppbot/xmppbot.cpp index 84f3ea9ff..ca5f8a83c 100644 --- a/src/xmppbot/xmppbot.cpp +++ b/src/xmppbot/xmppbot.cpp @@ -148,10 +148,7 @@ void XMPPBot::handleMessage(const Message& msg, MessageSession* session) connect( q.data(), SIGNAL( resultsAdded( QList ) ), SLOT( onResultsAdded( QList ) ) ); - QList ql; - ql.append( q ); - - Tomahawk::Pipeline::instance()->add( ql ); + Tomahawk::Pipeline::instance()->add( q ); return; } else if ( body.startsWith( "stop" ) ) diff --git a/src/xspfloader.cpp b/src/xspfloader.cpp index e038104f0..3ae71a03e 100644 --- a/src/xspfloader.cpp +++ b/src/xspfloader.cpp @@ -77,23 +77,14 @@ XSPFLoader::gotBody() xmldoc.setContent( m_body ); QDomElement docElement( xmldoc.documentElement() ); - QString origTitle, title, info, creator; + QString origTitle; origTitle = docElement.firstChildElement( "title" ).text(); - info = docElement.firstChildElement( "creator" ).text(); - creator = docElement.firstChildElement( "info" ).text(); + m_info = docElement.firstChildElement( "creator" ).text(); + m_creator = docElement.firstChildElement( "info" ).text(); - title = origTitle; - if ( title.isEmpty() ) - title = tr( "New Playlist" ); - - m_playlist = Playlist::create( SourceList::instance()->getLocal(), - uuid(), - title, - info, - creator, - false ); - - QList< plentry_ptr > entries; + m_title = origTitle; + if ( m_title.isEmpty() ) + m_title = tr( "New Playlist" ); QDomNodeList tracklist = docElement.elementsByTagName( "track" ); for ( unsigned int i = 0; i < tracklist.length(); i++ ) @@ -113,18 +104,36 @@ XSPFLoader::gotBody() v.insert( "track", e.firstChildElement( "title" ).text() ); p->setQuery( Tomahawk::query_ptr(new Tomahawk::Query(v)) ); - entries << p; + m_entries << p; } - if ( origTitle.isEmpty() && entries.isEmpty() ) + if ( origTitle.isEmpty() && m_entries.isEmpty() ) { - QMessageBox::critical( APP->mainWindow(), tr( "XSPF Error" ), tr( "This is not a valid XSPF playlist." ) ); - deleteLater(); - return; + if ( m_autoCreate ) + { + QMessageBox::critical( APP->mainWindow(), tr( "XSPF Error" ), tr( "This is not a valid XSPF playlist." ) ); + deleteLater(); + return; + } + else + { + emit failed(); + return; + } } - m_playlist->createNewRevision( uuid(), m_playlist->currentrevision(), entries ); - emit ok( m_playlist ); + if ( m_autoCreate ) + { + m_playlist = Playlist::create( SourceList::instance()->getLocal(), + uuid(), + m_title, + m_info, + m_creator, + false ); - deleteLater(); + m_playlist->createNewRevision( uuid(), m_playlist->currentrevision(), m_entries ); + deleteLater(); + } + + emit ok( m_playlist ); } diff --git a/src/xspfloader.h b/src/xspfloader.h index 8617f8f21..0572c5094 100644 --- a/src/xspfloader.h +++ b/src/xspfloader.h @@ -20,8 +20,9 @@ class XSPFLoader : public QObject Q_OBJECT public: - explicit XSPFLoader( QObject* parent = 0 ) + explicit XSPFLoader( bool autoCreate = true, QObject* parent = 0 ) : QObject( parent ) + , m_autoCreate( autoCreate ) {} virtual ~XSPFLoader() @@ -29,6 +30,8 @@ public: qDebug() << Q_FUNC_INFO; } + QList< Tomahawk::plentry_ptr > entries() const { return m_entries; } + signals: void failed(); void ok( const Tomahawk::playlist_ptr& ); @@ -45,6 +48,10 @@ private: void reportError(); void gotBody(); + bool m_autoCreate; + QList< Tomahawk::plentry_ptr > m_entries; + QString m_title, m_info, m_creator; + QByteArray m_body; Tomahawk::playlist_ptr m_playlist; };