diff --git a/src/audiocontrols.cpp b/src/audiocontrols.cpp index a2fa864c5..0050f8ac5 100644 --- a/src/audiocontrols.cpp +++ b/src/audiocontrols.cpp @@ -538,6 +538,7 @@ AudioControls::dropEvent( QDropEvent* e ) if ( DropJob::acceptsMimeData( e->mimeData() ) ) { DropJob *dj = new DropJob(); + dj->setDropAction( DropJob::Append ); connect( dj, SIGNAL( tracks( QList ) ), this, SLOT( droppedTracks( QList ) ) ); dj->tracksFromMimeData( e->mimeData() ); @@ -551,8 +552,8 @@ AudioControls::droppedTracks( QList< query_ptr > tracks ) { if ( !tracks.isEmpty() ) { - // queue and play the first if nothign is playing - GlobalActionManager::instance()->handleOpenTrack( tracks.first() ); + // queue and play the first no matter what + GlobalActionManager::instance()->handlePlayTrack( tracks.first() ); // just queue the rest for ( int i = 1; i < tracks.size(); i++ ) diff --git a/src/libtomahawk/dropjob.cpp b/src/libtomahawk/dropjob.cpp index b9e9b79dd..bc24408e4 100644 --- a/src/libtomahawk/dropjob.cpp +++ b/src/libtomahawk/dropjob.cpp @@ -371,54 +371,50 @@ DropJob::tracksFromMixedData( const QMimeData *data ) } void -DropJob::handleXspf( const QString& fileUrl ) +DropJob::handleXspfs( const QString& fileUrls ) { - tDebug() << Q_FUNC_INFO << "Got xspf playlist!!" << fileUrl; + tDebug() << Q_FUNC_INFO << "Got xspf playlist!!" << fileUrls; + + QStringList urls = fileUrls.split( QRegExp( "\\s+" ), QString::SkipEmptyParts ); if ( dropAction() == Default ) setDropAction( Create ); - QFile xspfFile( QUrl::fromUserInput( fileUrl ).toLocalFile() ); - - if ( xspfFile.exists() ) + foreach ( const QString& url, urls ) { - XSPFLoader* l = new XSPFLoader( true, this ); - tDebug( LOGINFO ) << "Loading local xspf " << xspfFile.fileName(); - l->load( xspfFile ); + QFile xspfFile( QUrl::fromUserInput( url ).toLocalFile() ); + + if ( xspfFile.exists() ) + { + XSPFLoader* l = new XSPFLoader( true, this ); + tDebug( LOGINFO ) << "Loading local xspf " << xspfFile.fileName(); + l->load( xspfFile ); + } + else + tLog( LOGINFO ) << "Error Loading local xspf " << xspfFile.fileName(); } - else - tLog( LOGINFO ) << "Error Loading local xspf " << xspfFile.fileName(); - - } void -DropJob::handleSpotifyUrl( const QString& url ) +DropJob::handleSpotifyUrls( const QString& urlsRaw ) { - qDebug() << "Got spotify browse uri!!" << url; + QStringList urls = urlsRaw.split( QRegExp( "\\s+" ), QString::SkipEmptyParts ); + qDebug() << "Got spotify browse uris!!" << urls; /// Lets allow parsing all spotify uris here, if parse server is not available /// fallback to spotify metadata for tracks /hugo - QString browseUri = url; - if ( url.contains( "open.spotify.com/" ) ) // convert to a URI - { - browseUri.replace( "http://open.spotify.com/", "" ); - browseUri.replace( "/", ":" ); - browseUri = "spotify:" + browseUri; - } - if ( dropAction() == Default ) setDropAction( Create ); - tDebug() << "Got a spotify browse uri in dropjob!" << browseUri; - SpotifyParser* spot = new SpotifyParser( browseUri, dropAction() == Create, this ); + tDebug() << "Got a spotify browse uri in dropjob!" << urls; + SpotifyParser* spot = new SpotifyParser( urls, dropAction() == Create, this ); spot->setSingleMode( false ); /// This currently supports draging and dropping a spotify playlist and artist if ( dropAction() == Append ) { - tDebug() << Q_FUNC_INFO << "Asking for spotify browse contents from" << browseUri; + tDebug() << Q_FUNC_INFO << "Asking for spotify browse contents from" << urls; connect( spot, SIGNAL( tracks( QList ) ), this, SLOT( onTracksAdded( QList< Tomahawk::query_ptr > ) ) ); } @@ -429,11 +425,11 @@ void DropJob::handleAllUrls( const QString& urls ) { if ( urls.contains( "xspf" ) ) - handleXspf( urls ); + handleXspfs( urls ); else if ( urls.contains( "spotify" ) /// Handle all the spotify uris on internal server, if not avail. fallback to spotify && ( urls.contains( "playlist" ) || urls.contains( "artist" ) || urls.contains( "album" ) || urls.contains( "track" ) ) && s_canParseSpotifyPlaylists ) - handleSpotifyUrl( urls ); + handleSpotifyUrls( urls ); else handleTrackUrls ( urls ); } diff --git a/src/libtomahawk/dropjob.h b/src/libtomahawk/dropjob.h index 1bca326af..e79213938 100644 --- a/src/libtomahawk/dropjob.h +++ b/src/libtomahawk/dropjob.h @@ -89,8 +89,8 @@ public: void setGetWholeArtists( bool getWholeArtists ); void setGetWholeAlbums( bool getWholeAlbums ); void tracksFromMimeData( const QMimeData* data, bool allowDuplicates = false, bool onlyLocal = false, bool top10 = false ); - void handleXspf( const QString& file ); - void handleSpotifyUrl( const QString& url ); + void handleXspfs( const QString& files ); + void handleSpotifyUrls( const QString& urls ); static bool canParseSpotifyPlaylists() { return s_canParseSpotifyPlaylists; } static void setCanParseSpotifyPlaylists( bool parseable ) { s_canParseSpotifyPlaylists = parseable; } diff --git a/src/libtomahawk/globalactionmanager.cpp b/src/libtomahawk/globalactionmanager.cpp index 2baa8920f..833eae16e 100644 --- a/src/libtomahawk/globalactionmanager.cpp +++ b/src/libtomahawk/globalactionmanager.cpp @@ -363,6 +363,13 @@ GlobalActionManager::handleOpenTrack ( const query_ptr& q ) } } +void +GlobalActionManager::handlePlayTrack( const query_ptr& qry ) +{ + playNow( qry ); +} + + bool GlobalActionManager::handleQueueCommand( const QUrl& url ) @@ -697,11 +704,8 @@ GlobalActionManager::handlePlayCommand( const QUrl& url ) query_ptr q = Query::get( artist, title, album ); if( !urlStr.isEmpty() ) q->setResultHint( urlStr ); - Pipeline::instance()->resolve( q, true ); - - m_waitingToPlay = q; - connect( q.data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( waitingForResolved( bool ) ) ); + playNow( q ); return true; } @@ -717,14 +721,25 @@ GlobalActionManager::playSpotify( const QUrl& url ) QString spotifyUrl = url.hasQueryItem( "spotifyURI" ) ? url.queryItemValue( "spotifyURI" ) : url.queryItemValue( "spotifyURL" ); SpotifyParser* p = new SpotifyParser( spotifyUrl, this ); - connect( p, SIGNAL( track( Tomahawk::query_ptr ) ), this, SLOT( playNow( Tomahawk::query_ptr ) ) ); + connect( p, SIGNAL( track( Tomahawk::query_ptr ) ), this, SLOT( playOrQueueNow( Tomahawk::query_ptr ) ) ); return true; } - void GlobalActionManager::playNow( const query_ptr& q ) +{ + + Pipeline::instance()->resolve( q, true ); + + m_waitingToPlay = q; + q->setProperty( "playNow", true ); + connect( q.data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( waitingForResolved( bool ) ) ); +} + + +void +GlobalActionManager::playOrQueueNow( const query_ptr& q ) { Pipeline::instance()->resolve( q, true ); @@ -743,7 +758,7 @@ GlobalActionManager::playRdio( const QUrl& url ) QString rdioUrl = url.hasQueryItem( "rdioURI" ) ? url.queryItemValue( "spotifyURI" ) : url.queryItemValue( "rdioURL" ); RdioParser* p = new RdioParser( this ); p->parse( rdioUrl ); - connect( p, SIGNAL( track( Tomahawk::query_ptr ) ), this, SLOT( playNow( Tomahawk::query_ptr ) ) ); + connect( p, SIGNAL( track( Tomahawk::query_ptr ) ), this, SLOT( playOrQueueNow( Tomahawk::query_ptr ) ) ); return true; } @@ -921,7 +936,17 @@ GlobalActionManager::waitingForResolved( bool /* success */ ) { // play it! // AudioEngine::instance()->playItem( AudioEngine::instance()->playlist(), m_waitingToPlay->results().first() ); - AudioEngine::instance()->play(); + if ( sender() && sender()->property( "playNow" ).toBool() ) + { + if ( AudioEngine::instance()->playlist() ) + AudioEngine::instance()->playItem( AudioEngine::instance()->playlist(), m_waitingToPlay->results().first() ); + else + { + ViewManager::instance()->queue()->model()->append( m_waitingToPlay ); + AudioEngine::instance()->play(); + } + } else + AudioEngine::instance()->play(); m_waitingToPlay.clear(); } diff --git a/src/libtomahawk/globalactionmanager.h b/src/libtomahawk/globalactionmanager.h index 8b0854fe0..156fbc58d 100644 --- a/src/libtomahawk/globalactionmanager.h +++ b/src/libtomahawk/globalactionmanager.h @@ -64,6 +64,7 @@ public slots: Tomahawk::dynplaylist_ptr loadDynamicPlaylist( const QUrl& url, bool station ); void handleOpenTrack( const Tomahawk::query_ptr& qry ); + void handlePlayTrack( const Tomahawk::query_ptr& qry ); signals: void shortLinkReady( QUrl longUrl, QUrl shortUrl ) const; @@ -77,6 +78,7 @@ private slots: void xspfCreated( const QByteArray& xspf ); + void playOrQueueNow( const Tomahawk::query_ptr& ); void playNow( const Tomahawk::query_ptr& ); private: diff --git a/src/libtomahawk/jobview/JobStatusModel.cpp b/src/libtomahawk/jobview/JobStatusModel.cpp index a033842bd..3920a6a15 100644 --- a/src/libtomahawk/jobview/JobStatusModel.cpp +++ b/src/libtomahawk/jobview/JobStatusModel.cpp @@ -44,7 +44,7 @@ JobStatusModel::addJob( JobStatusItem* item ) if ( m_collapseCount.contains( item->type() ) ) { m_collapseCount[ item->type() ].append( item ); - qDebug() << "Adding item:" << item << "TO COLLAPSE ONLY"; +// qDebug() << "Adding item:" << item << "TO COLLAPSE ONLY"; return; // we're done, no new rows } else @@ -118,26 +118,26 @@ JobStatusModel::itemFinished() if ( !m_items.contains( item ) && !m_collapseCount.contains( item->type() ) ) return; - foreach( JobStatusItem* item, m_items ) - { - qDebug() << "ITEM #:" << item; - } - foreach( const QString& str, m_collapseCount.keys() ) - { - tDebug() << "\t" << str; - foreach( JobStatusItem* chain, m_collapseCount[ str ] ) - qDebug() << "\t\t" << chain; - } +// foreach( JobStatusItem* item, m_items ) +// { +// qDebug() << "ITEM #:" << item; +// } +// foreach( const QString& str, m_collapseCount.keys() ) +// { +// tDebug() << "\t" << str; +// foreach( JobStatusItem* chain, m_collapseCount[ str ] ) +// qDebug() << "\t\t" << chain; +// } if ( m_collapseCount.contains( item->type() ) ) { const int indexOf = m_items.indexOf( m_collapseCount[ item->type() ].first() ); - tDebug() << "index in main list of collapsed irst item:" << indexOf; +// tDebug() << "index in main list of collapsed irst item:" << indexOf; if ( m_collapseCount[ item->type() ].first() == item && m_items.contains( m_collapseCount[ item->type() ].first() ) && m_collapseCount[ item->type() ].size() > 1 ) { // the placeholder we use that links m_items and m_collapsecount is done, so choose another one m_items.replace( m_items.indexOf( m_collapseCount[ item->type() ].first() ), m_collapseCount[ item->type() ][ 1 ] ); - qDebug() << "Replaced" << m_collapseCount[ item->type() ].first() << "with:" << m_collapseCount[ item->type() ][ 1 ] << m_items; +// qDebug() << "Replaced" << m_collapseCount[ item->type() ].first() << "with:" << m_collapseCount[ item->type() ][ 1 ] << m_items; } m_collapseCount[ item->type() ].removeAll( item ); tDebug() << "New collapse count list:" << m_collapseCount[ item->type() ]; @@ -154,7 +154,7 @@ JobStatusModel::itemFinished() // Remove row completely const int idx = m_items.indexOf( item ); - tDebug() << "Got index of item:" << idx; +// tDebug() << "Got index of item:" << idx; Q_ASSERT( idx >= 0 ); beginRemoveRows( QModelIndex(), idx, idx ); diff --git a/src/libtomahawk/playlist.cpp b/src/libtomahawk/playlist.cpp index 6a94ed2c1..f600af761 100644 --- a/src/libtomahawk/playlist.cpp +++ b/src/libtomahawk/playlist.cpp @@ -147,6 +147,7 @@ void Playlist::init() { m_busy = false; + m_deleted = false; m_locallyChanged = false; connect( Pipeline::instance(), SIGNAL( idle() ), SLOT( onResolvingFinished() ) ); } @@ -248,6 +249,7 @@ void Playlist::reportDeleted( const Tomahawk::playlist_ptr& self ) { Q_ASSERT( self.data() == this ); + m_deleted = true; m_source->collection()->deletePlaylist( self ); emit deleted( self ); @@ -480,7 +482,7 @@ Playlist::onResultsFound( const QList& results ) void Playlist::onResolvingFinished() { - if ( m_locallyChanged ) + if ( m_locallyChanged && !m_deleted ) { m_locallyChanged = false; createNewRevision( currentrevision(), currentrevision(), m_entries ); diff --git a/src/libtomahawk/playlist.h b/src/libtomahawk/playlist.h index 23831332b..bbb1390b5 100644 --- a/src/libtomahawk/playlist.h +++ b/src/libtomahawk/playlist.h @@ -295,6 +295,7 @@ private: QQueue m_revisionQueue; bool m_locallyChanged; + bool m_deleted; bool m_busy; }; diff --git a/src/libtomahawk/playlist/playlistmodel.cpp b/src/libtomahawk/playlist/playlistmodel.cpp index 4684f02ed..df734bf24 100644 --- a/src/libtomahawk/playlist/playlistmodel.cpp +++ b/src/libtomahawk/playlist/playlistmodel.cpp @@ -368,6 +368,12 @@ PlaylistModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int r DropJob* dj = new DropJob(); + if ( !DropJob::acceptsMimeData( data, DropJob::Track | DropJob::Playlist | DropJob::Album | DropJob::Artist ) ) + return false; + + dj->setDropTypes( DropJob::Track | DropJob::Playlist | DropJob::Artist | DropJob::Album ); + dj->setDropAction( DropJob::Append ); + // On mac, drags from outside the app are still Qt::MoveActions instead of Qt::CopyAction by default // so check if the drag originated in this playlist to determine whether or not to copy #ifdef Q_WS_MAC diff --git a/src/libtomahawk/utils/spotifyparser.cpp b/src/libtomahawk/utils/spotifyparser.cpp index ba0913fee..c21fc2bde 100644 --- a/src/libtomahawk/utils/spotifyparser.cpp +++ b/src/libtomahawk/utils/spotifyparser.cpp @@ -88,10 +88,19 @@ SpotifyParser::lookupUrl( const QString& link ) void -SpotifyParser::lookupSpotifyBrowse( const QString& link ) +SpotifyParser::lookupSpotifyBrowse( const QString& linkRaw ) { - tLog() << "Parsing Spotify Browse URI:" << link; - QUrl url = QUrl( QString( SPOTIFY_PLAYLIST_API_URL "/browse/%1" ).arg( link ) ); + tLog() << "Parsing Spotify Browse URI:" << linkRaw; + QString browseUri = linkRaw; + if ( browseUri.contains( "open.spotify.com/" ) ) // convert to a URI + { + browseUri.replace( "http://open.spotify.com/", "" ); + browseUri.replace( "/", ":" ); + browseUri = "spotify:" + browseUri; + } + + + QUrl url = QUrl( QString( SPOTIFY_PLAYLIST_API_URL "/browse/%1" ).arg( browseUri ) ); tDebug() << "Looking up URL..." << url.toString(); QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( url ) ); @@ -99,13 +108,13 @@ SpotifyParser::lookupSpotifyBrowse( const QString& link ) DropJob::DropType type; - if ( link.contains( "spotify:user" ) ) + if ( browseUri.contains( "spotify:user" ) ) type = DropJob::Playlist; - if ( link.contains( "spotify:artist" ) ) + if ( browseUri.contains( "spotify:artist" ) ) type = DropJob::Artist; - if ( link.contains( "spotify:album" ) ) + if ( browseUri.contains( "spotify:album" ) ) type = DropJob::Album; - if ( link.contains( "spotify:track" ) ) + if ( browseUri.contains( "spotify:track" ) ) type = DropJob::Track; m_browseJob = new DropJobNotifier( pixmap(), "Spotify", type, reply ); diff --git a/src/resolversmodel.cpp b/src/resolversmodel.cpp index cae4b9e69..fa1c93cb0 100644 --- a/src/resolversmodel.cpp +++ b/src/resolversmodel.cpp @@ -28,7 +28,6 @@ #include "utils/logger.h" - ResolversModel::ResolversModel( QObject* parent ) : QAbstractListModel( parent ) { @@ -135,6 +134,11 @@ ResolversModel::addResolver( const QString& resolver, bool enable ) Tomahawk::ExternalResolver* res = Tomahawk::Pipeline::instance()->addScriptResolver( resolver, enable ); connect( res, SIGNAL( changed() ), this, SLOT( resolverChanged() ) ); endInsertRows(); + + if ( res->configUI() ) + emit openConfig( res->filePath() ); + else + m_waitingForLoad << resolver; } void @@ -177,6 +181,12 @@ ResolversModel::resolverChanged() qDebug() << "Got resolverChanged signal, does it have a config UI yet?" << res->configUI(); const QModelIndex idx = index( Tomahawk::Pipeline::instance()->scriptResolvers().indexOf( res ), 0, QModelIndex() ); emit dataChanged( idx, idx ); + + if ( m_waitingForLoad.contains( res->filePath() ) ) + { + m_waitingForLoad.remove( res->filePath() ); + emit openConfig( res->filePath() ); + } } } diff --git a/src/resolversmodel.h b/src/resolversmodel.h index 167320bca..9aee76f72 100644 --- a/src/resolversmodel.h +++ b/src/resolversmodel.h @@ -22,7 +22,7 @@ #include #include - +#include class ResolversModel : public QAbstractListModel { @@ -49,11 +49,16 @@ public: void removeResolver( const QString& resolver ); void saveScriptResolvers(); + +signals: + void openConfig( const QString& filePath ); + private slots: void resolverChanged(); private: void addInstalledResolvers(); + QSet m_waitingForLoad; }; #endif // RESOLVERSMODEL_H diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index 14be34bf8..24a99488f 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -200,6 +200,7 @@ SettingsDialog::SettingsDialog( QWidget *parent ) m_resolversModel = new ResolversModel( this ); ui->scriptList->setModel( m_resolversModel ); ui->scriptList->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); + connect( m_resolversModel, SIGNAL( openConfig( QString ) ), SLOT( openResolverConfig( QString ) ) ); #ifdef LIBATTICA_FOUND connect( ui->getMoreResolvers, SIGNAL( clicked() ), this, SLOT( getMoreResolvers() ) ); diff --git a/src/sourcetree/items/playlistitems.cpp b/src/sourcetree/items/playlistitems.cpp index 6d79395d5..613ab76ff 100644 --- a/src/sourcetree/items/playlistitems.cpp +++ b/src/sourcetree/items/playlistitems.cpp @@ -183,6 +183,7 @@ PlaylistItem::dropMimeData( const QMimeData* data, Qt::DropAction action ) DropJob *dj = new DropJob(); dj->setDropTypes( DropJob::Track ); + dj->setDropAction( DropJob::Append ); connect( dj, SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) ); diff --git a/src/sourcetree/sourcetreeview.cpp b/src/sourcetree/sourcetreeview.cpp index 9ed0283c0..762c2b74b 100644 --- a/src/sourcetree/sourcetreeview.cpp +++ b/src/sourcetree/sourcetreeview.cpp @@ -417,7 +417,7 @@ SourceTreeView::dragEnterEvent( QDragEnterEvent* event ) qDebug() << Q_FUNC_INFO; QTreeView::dragEnterEvent( event ); - if ( DropJob::acceptsMimeData( event->mimeData(), DropJob::Track | DropJob::Playlist, DropJob::Create ) ) + if ( DropJob::acceptsMimeData( event->mimeData(), DropJob::Track | DropJob::Artist | DropJob::Album | DropJob::Playlist, DropJob::Create ) ) { m_dragging = true; m_dropRect = QRect(); @@ -491,7 +491,7 @@ SourceTreeView::dragMoveEvent( QDragMoveEvent* event ) event->ignore(); } } - else if ( DropJob::acceptsMimeData( event->mimeData(), DropJob::Playlist, DropJob::Create ) ) + else if ( DropJob::acceptsMimeData( event->mimeData(), DropJob::Playlist | DropJob::Artist | DropJob::Album, DropJob::Create ) ) { event->setDropAction( Qt::CopyAction ); event->accept();