From 4a3bca9bf477af5483170a100637694d684a62cf Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 4 Aug 2011 19:01:41 -0400 Subject: [PATCH 1/2] Factor out dragndrop support across tomahawk into GlobalActionManager, add spotify support --- src/libtomahawk/CMakeLists.txt | 4 +- src/libtomahawk/globalactionmanager.cpp | 269 ++++++++++-------- src/libtomahawk/globalactionmanager.h | 37 ++- .../playlist/dynamic/DynamicModel.cpp | 2 +- .../playlist/dynamic/DynamicModel.h | 2 +- src/libtomahawk/playlist/playlistmodel.cpp | 88 +++--- src/libtomahawk/playlist/playlistmodel.h | 9 + src/libtomahawk/playlist/trackview.cpp | 24 +- src/libtomahawk/utils/spotifyparser.cpp | 147 ++++++++++ src/libtomahawk/utils/spotifyparser.h | 64 +++++ src/sourcetree/items/categoryitems.cpp | 99 ++----- src/sourcetree/items/categoryitems.h | 3 + src/sourcetree/items/playlistitems.cpp | 56 +--- src/sourcetree/items/playlistitems.h | 1 + src/sourcetree/sourcesmodel.cpp | 6 +- src/sourcetree/sourcetreeview.cpp | 6 +- src/tomahawkapp.cpp | 2 +- 17 files changed, 494 insertions(+), 325 deletions(-) create mode 100644 src/libtomahawk/utils/spotifyparser.cpp create mode 100644 src/libtomahawk/utils/spotifyparser.h diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index ff3a500f2..a808526bd 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -167,6 +167,7 @@ set( libSources utils/xspfloader.cpp utils/xspfgenerator.cpp utils/jspfloader.cpp + utils/spotifyparser.cpp widgets/newplaylistwidget.cpp widgets/searchwidget.cpp @@ -338,6 +339,7 @@ set( libHeaders utils/xspfloader.h utils/xspfgenerator.h utils/jspfloader.h + utils/spotifyparser.h widgets/newplaylistwidget.h widgets/searchwidget.h @@ -427,7 +429,7 @@ IF( APPLE ) widgets/maclineedit.mm utils/tomahawkutils_mac.mm ) - SET( libHeaders ${libHeaders} + SET( libHeaders ${libHeaders} infosystem/infoplugins/mac/adiumplugin.h widgets/maclineedit.h ) diff --git a/src/libtomahawk/globalactionmanager.cpp b/src/libtomahawk/globalactionmanager.cpp index b99100f52..e55f4e968 100644 --- a/src/libtomahawk/globalactionmanager.cpp +++ b/src/libtomahawk/globalactionmanager.cpp @@ -22,6 +22,7 @@ #include #include +#include "artist.h" #include "album.h" #include "sourcelist.h" #include "pipeline.h" @@ -42,9 +43,13 @@ #include #include #include "utils/jspfloader.h" +#include +#include "utils/spotifyparser.h" GlobalActionManager* GlobalActionManager::s_instance = 0; +using namespace Tomahawk; + GlobalActionManager* GlobalActionManager::instance() { @@ -54,18 +59,17 @@ GlobalActionManager::instance() return s_instance; } - GlobalActionManager::GlobalActionManager( QObject* parent ) : QObject( parent ) { - + m_mimeTypes << "application/tomahawk.query.list" << "application/tomahawk.plentry.list" << "application/tomahawk.result.list" << "text/plain"; } GlobalActionManager::~GlobalActionManager() {} QUrl -GlobalActionManager::openLinkFromQuery( const Tomahawk::query_ptr& query ) const +GlobalActionManager::openLinkFromQuery( const query_ptr& query ) const { QString title, artist, album; @@ -100,9 +104,9 @@ GlobalActionManager::openLink( const QString& title, const QString& artist, cons } QString -GlobalActionManager::copyPlaylistToClipboard( const Tomahawk::dynplaylist_ptr& playlist ) +GlobalActionManager::copyPlaylistToClipboard( const dynplaylist_ptr& playlist ) { - QUrl link( QString( "%1/%2/create/" ).arg( hostname() ).arg( playlist->mode() == Tomahawk::OnDemand ? "station" : "autoplaylist" ) ); + QUrl link( QString( "%1/%2/create/" ).arg( hostname() ).arg( playlist->mode() == OnDemand ? "station" : "autoplaylist" ) ); if( playlist->generator()->type() != "echonest" ) { tLog() << "Only echonest generators are supported"; @@ -112,8 +116,8 @@ GlobalActionManager::copyPlaylistToClipboard( const Tomahawk::dynplaylist_ptr& p link.addEncodedQueryItem( "type", "echonest" ); link.addQueryItem( "title", playlist->title() ); - QList< Tomahawk::dyncontrol_ptr > controls = playlist->generator()->controls(); - foreach( const Tomahawk::dyncontrol_ptr& c, controls ) { + QList< dyncontrol_ptr > controls = playlist->generator()->controls(); + foreach( const dyncontrol_ptr& c, controls ) { if( c->selectedType() == "Artist" ) { if( c->match().toInt() == Echonest::DynamicPlaylist::ArtistType ) link.addQueryItem( "artist_limitto", c->input() ); @@ -142,7 +146,7 @@ GlobalActionManager::copyPlaylistToClipboard( const Tomahawk::dynplaylist_ptr& p } void -GlobalActionManager::savePlaylistToFile( const Tomahawk::playlist_ptr& playlist, const QString& filename ) +GlobalActionManager::savePlaylistToFile( const playlist_ptr& playlist, const QString& filename ) { XSPFGenerator* g = new XSPFGenerator( playlist, this ); g->setProperty( "filename", filename ); @@ -169,7 +173,7 @@ GlobalActionManager::xspfCreated( const QByteArray& xspf ) void -GlobalActionManager::copyToClipboard( const Tomahawk::query_ptr& query ) const +GlobalActionManager::copyToClipboard( const query_ptr& query ) const { QClipboard* cb = QApplication::clipboard(); cb->setText( openLinkFromQuery( query ).toEncoded() ); @@ -199,7 +203,7 @@ GlobalActionManager::parseTomahawkLink( const QString& url ) return true; } else if( u.hasQueryItem( "jspf" ) ) { QUrl jspf = QUrl::fromUserInput( u.queryItemValue( "jspf" ) ); - Tomahawk::JSPFLoader* l = new Tomahawk::JSPFLoader( true, this ); + JSPFLoader* l = new JSPFLoader( true, this ); tDebug() << "Loading jspiff:" << jspf.toString(); l->load( jspf ); connect( l, SIGNAL( ok( Tomahawk::playlist_ptr ) ), ViewManager::instance(), SLOT( show( Tomahawk::playlist_ptr ) ) ); @@ -262,7 +266,7 @@ GlobalActionManager::handlePlaylistCommand( const QUrl& url ) tLog() << "New playlist command needs a title..."; return false; } - Tomahawk::playlist_ptr pl = Tomahawk::Playlist::create( SourceList::instance()->getLocal(), uuid(), url.queryItemValue( "title" ), QString(), QString(), false ); + playlist_ptr pl = Playlist::create( SourceList::instance()->getLocal(), uuid(), url.queryItemValue( "title" ), QString(), QString(), false ); ViewManager::instance()->show( pl ); } else if( parts[ 0 ] == "add" ) { if( !url.hasQueryItem( "playlistid" ) || !url.hasQueryItem( "title" ) || !url.hasQueryItem( "artist" ) ) { @@ -305,7 +309,7 @@ GlobalActionManager::handleOpenCommand(const QUrl& url) } void -GlobalActionManager::handleOpenTrack ( const Tomahawk::query_ptr& q ) +GlobalActionManager::handleOpenTrack ( const query_ptr& q ) { ViewManager::instance()->queue()->model()->append( q ); ViewManager::instance()->showQueue(); @@ -355,10 +359,10 @@ GlobalActionManager::doQueueAdd( const QStringList& parts, const QList< QPair< Q } if( !title.isEmpty() || !artist.isEmpty() || !album.isEmpty() ) { // an individual; query to add to queue - Tomahawk::query_ptr q = Tomahawk::Query::get( artist, title, album, uuid(), false ); + query_ptr q = Query::get( artist, title, album, uuid(), false ); if( !urlStr.isEmpty() ) q->setResultHint( urlStr ); - Tomahawk::Pipeline::instance()->resolve( q, true ); + Pipeline::instance()->resolve( q, true ); handleOpenTrack( q ); return true; @@ -373,10 +377,10 @@ GlobalActionManager::doQueueAdd( const QStringList& parts, const QList< QPair< Q // TODO } else { // give it a web result hint QFileInfo info( track.path() ); - Tomahawk::query_ptr q = Tomahawk::Query::get( QString(), info.baseName(), QString(), uuid(), false ); + query_ptr q = Query::get( QString(), info.baseName(), QString(), uuid(), false ); q->setResultHint( track.toString() ); - Tomahawk::Pipeline::instance()->resolve( q, true ); + Pipeline::instance()->resolve( q, true ); ViewManager::instance()->queue()->model()->append( q ); ViewManager::instance()->showQueue(); @@ -415,139 +419,139 @@ GlobalActionManager::handleAutoPlaylistCommand( const QUrl& url ) return !loadDynamicPlaylist( url, false ).isNull(); } -Tomahawk::dynplaylist_ptr +dynplaylist_ptr GlobalActionManager::loadDynamicPlaylist( const QUrl& url, bool station ) { QStringList parts = url.path().split( "/" ).mid( 1 ); // get the rest of the command if( parts.isEmpty() ) { tLog() << "No specific station command:" << url.toString(); - return Tomahawk::dynplaylist_ptr(); + return dynplaylist_ptr(); } if( parts[ 0 ] == "create" ) { if( !url.hasQueryItem( "title" ) || !url.hasQueryItem( "type" ) ) { tLog() << "Station create command needs title and type..." << url.toString(); - return Tomahawk::dynplaylist_ptr(); + return dynplaylist_ptr(); } QString title = url.queryItemValue( "title" ); QString type = url.queryItemValue( "type" ); - Tomahawk::GeneratorMode m = Tomahawk::Static; + GeneratorMode m = Static; if( station ) - m = Tomahawk::OnDemand; + m = OnDemand; - Tomahawk::dynplaylist_ptr pl = Tomahawk::DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), title, QString(), QString(), m, false, type ); + dynplaylist_ptr pl = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), title, QString(), QString(), m, false, type ); pl->setMode( m ); - QList< Tomahawk::dyncontrol_ptr > controls; + QList< dyncontrol_ptr > controls; QPair< QString, QString > param; foreach( param, url.queryItems() ) { if( param.first == "artist" ) { - Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Artist" ); + dyncontrol_ptr c = pl->generator()->createControl( "Artist" ); c->setInput( param.second ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistRadioType ) ); controls << c; } else if( param.first == "artist_limitto" ) { - Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Artist" ); + dyncontrol_ptr c = pl->generator()->createControl( "Artist" ); c->setInput( param.second ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistType ) ); controls << c; } else if( param.first == "description" ) { - Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Artist Description" ); + dyncontrol_ptr c = pl->generator()->createControl( "Artist Description" ); c->setInput( param.second ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistDescriptionType ) ); controls << c; } else if( param.first == "variety" ) { - Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Variety" ); + dyncontrol_ptr c = pl->generator()->createControl( "Variety" ); c->setInput( param.second ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Variety ) ); controls << c; } else if( param.first.startsWith( "tempo" ) ) { - Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Tempo" ); + dyncontrol_ptr c = pl->generator()->createControl( "Tempo" ); int extra = param.first.endsWith( "_max" ) ? -1 : 0; c->setInput( param.second ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinTempo + extra ) ); controls << c; } else if( param.first.startsWith( "duration" ) ) { - Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Duration" ); + dyncontrol_ptr c = pl->generator()->createControl( "Duration" ); int extra = param.first.endsWith( "_max" ) ? -1 : 0; c->setInput( param.second ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinDuration + extra ) ); controls << c; } else if( param.first.startsWith( "loudness" ) ) { - Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Loudness" ); + dyncontrol_ptr c = pl->generator()->createControl( "Loudness" ); int extra = param.first.endsWith( "_max" ) ? -1 : 0; c->setInput( param.second ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinLoudness + extra ) ); controls << c; } else if( param.first.startsWith( "danceability" ) ) { - Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Danceability" ); + dyncontrol_ptr c = pl->generator()->createControl( "Danceability" ); int extra = param.first.endsWith( "_max" ) ? 1 : 0; c->setInput( param.second ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinDanceability + extra ) ); controls << c; } else if( param.first.startsWith( "energy" ) ) { - Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Energy" ); + dyncontrol_ptr c = pl->generator()->createControl( "Energy" ); int extra = param.first.endsWith( "_max" ) ? 1 : 0; c->setInput( param.second ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinEnergy + extra ) ); controls << c; } else if( param.first.startsWith( "artist_familiarity" ) ) { - Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Artist Familiarity" ); + dyncontrol_ptr c = pl->generator()->createControl( "Artist Familiarity" ); int extra = param.first.endsWith( "_max" ) ? -1 : 0; c->setInput( param.second ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistMinFamiliarity + extra ) ); controls << c; } else if( param.first.startsWith( "artist_hotttnesss" ) ) { - Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Artist Hotttnesss" ); + dyncontrol_ptr c = pl->generator()->createControl( "Artist Hotttnesss" ); int extra = param.first.endsWith( "_max" ) ? -1 : 0; c->setInput( param.second ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistMinHotttnesss + extra ) ); controls << c; } else if( param.first.startsWith( "song_hotttnesss" ) ) { - Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Song Hotttnesss" ); + dyncontrol_ptr c = pl->generator()->createControl( "Song Hotttnesss" ); int extra = param.first.endsWith( "_max" ) ? -1 : 0; c->setInput( param.second ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::SongMinHotttnesss + extra ) ); controls << c; } else if( param.first.startsWith( "longitude" ) ) { - Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Longitude" ); + dyncontrol_ptr c = pl->generator()->createControl( "Longitude" ); int extra = param.first.endsWith( "_max" ) ? 1 : 0; c->setInput( param.second ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistMinLongitude + extra ) ); controls << c; } else if( param.first.startsWith( "latitude" ) ) { - Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Latitude" ); + dyncontrol_ptr c = pl->generator()->createControl( "Latitude" ); int extra = param.first.endsWith( "_max" ) ? 1 : 0; c->setInput( param.second ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistMinLatitude + extra ) ); controls << c; } else if( param.first == "key" ) { - Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Key" ); + dyncontrol_ptr c = pl->generator()->createControl( "Key" ); c->setInput( param.second ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Key ) ); controls << c; } else if( param.first == "mode" ) { - Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Mode" ); + dyncontrol_ptr c = pl->generator()->createControl( "Mode" ); c->setInput( param.second ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Mode ) ); controls << c; } else if( param.first == "mood" ) { - Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Mood" ); + dyncontrol_ptr c = pl->generator()->createControl( "Mood" ); c->setInput( param.second ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Mood ) ); controls << c; } else if( param.first == "style" ) { - Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Style" ); + dyncontrol_ptr c = pl->generator()->createControl( "Style" ); c->setInput( param.second ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Style ) ); controls << c; } else if( param.first == "song" ) { - Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Song" ); + dyncontrol_ptr c = pl->generator()->createControl( "Song" ); c->setInput( param.second ); c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::SongRadioType ) ); controls << c; } } - if( m == Tomahawk::OnDemand ) + if( m == OnDemand ) pl->createNewRevision( uuid(), pl->currentrevision(), type, controls ); else pl->createNewRevision( uuid(), pl->currentrevision(), type, controls, pl->entries() ); @@ -555,7 +559,7 @@ GlobalActionManager::loadDynamicPlaylist( const QUrl& url, bool station ) return pl; } - return Tomahawk::dynplaylist_ptr(); + return dynplaylist_ptr(); } @@ -587,10 +591,10 @@ GlobalActionManager::handlePlayCommand( const QUrl& url ) else if( pair.first == "url" ) urlStr = pair.second; } - Tomahawk::query_ptr q = Tomahawk::Query::get( artist, title, album ); + query_ptr q = Query::get( artist, title, album ); if( !urlStr.isEmpty() ) q->setResultHint( urlStr ); - Tomahawk::Pipeline::instance()->resolve( q, true ); + Pipeline::instance()->resolve( q, true ); m_waitingToPlay = q; connect( q.data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( waitingForResolved( bool ) ) ); @@ -622,14 +626,14 @@ bool GlobalActionManager::handleBookmarkCommand(const QUrl& url) else if( pair.first == "url" ) urlStr = pair.second; } - Tomahawk::query_ptr q = Tomahawk::Query::get( artist, title, album ); + query_ptr q = Query::get( artist, title, album ); if( !urlStr.isEmpty() ) q->setResultHint( urlStr ); - Tomahawk::Pipeline::instance()->resolve( q, true ); + Pipeline::instance()->resolve( q, true ); // now we add it to the special "bookmarks" playlist, creating it if it doesn't exist. if nothing is playing, start playing the track QSharedPointer< LocalCollection > col = SourceList::instance()->getLocal()->collection().dynamicCast< LocalCollection >(); - Tomahawk::playlist_ptr bookmarkpl = col->bookmarksPlaylist(); + playlist_ptr bookmarkpl = col->bookmarksPlaylist(); if( bookmarkpl.isNull() ) { // create it and do the deed then m_waitingToBookmark = q; col->createBookmarksPlaylist(); @@ -647,16 +651,16 @@ bool GlobalActionManager::handleBookmarkCommand(const QUrl& url) void -GlobalActionManager::bookmarkPlaylistCreated( const Tomahawk::playlist_ptr& pl ) +GlobalActionManager::bookmarkPlaylistCreated( const playlist_ptr& pl ) { Q_ASSERT( !m_waitingToBookmark.isNull() ); doBookmark( pl, m_waitingToBookmark ); } void -GlobalActionManager::doBookmark( const Tomahawk::playlist_ptr& pl, const Tomahawk::query_ptr& q ) +GlobalActionManager::doBookmark( const playlist_ptr& pl, const query_ptr& q ) { - Tomahawk::plentry_ptr e( new Tomahawk::PlaylistEntry ); + plentry_ptr e( new PlaylistEntry ); e->setGuid( uuid() ); if ( q->results().count() ) @@ -668,7 +672,7 @@ GlobalActionManager::doBookmark( const Tomahawk::playlist_ptr& pl, const Tomahaw e->setAnnotation( "" ); // FIXME e->setQuery( q ); - pl->createNewRevision( uuid(), pl->currentrevision(), QList< Tomahawk::plentry_ptr >( pl->entries() ) << e ); + pl->createNewRevision( uuid(), pl->currentrevision(), QList< plentry_ptr >( pl->entries() ) << e ); connect( pl.data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), this, SLOT( showPlaylist() ) ); m_toShow = pl; @@ -712,82 +716,111 @@ GlobalActionManager::hostname() const return QString( "http://toma.hk" ); } -/// SPOTIFY URL HANDLING +/// QMIMEDATA HANDLING -bool -GlobalActionManager::parseSpotifyLink( const QString& link ) +QStringList +GlobalActionManager::mimeTypes() const { - if( !link.contains( "track" ) ) // we only support track links atm - return false; - - // we need Spotify URIs such as spotify:track:XXXXXX, so if we by chance get a http://open.spotify.com url, convert it - QString uri = link; - if( link.contains( "open.spotify.com" ) ) - { - QString hash = link; - hash.replace( "http://open.spotify.com/track/", "" ); - uri = QString( "spotify:track:%1" ).arg( hash ); - } - - tLog() << "Parsing Spotify Track URI:" << uri; - - QUrl url = QUrl( QString( "http://ws.spotify.com/lookup/1/.json?uri=%1" ).arg( uri ) ); - tDebug() << "Looking up..." << url.toString(); - - QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( url ) ); - connect( reply, SIGNAL( finished() ), this, SLOT( spotifyTrackLookupFinished() ) ); - - return true; // all we know now + return m_mimeTypes; } +bool +GlobalActionManager::acceptsMimeData( const QMimeData* data ) +{ + if ( data->hasFormat( "application/tomahawk.query.list" ) + || data->hasFormat( "application/tomahawk.plentry.list" ) + || data->hasFormat( "application/tomahawk.result.list" ) ) + { + return true; + } + + // crude check for spotify data + if ( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "spotify" ) ) + return true; + + return false; +} + void -GlobalActionManager::spotifyTrackLookupFinished() +GlobalActionManager::tracksFromMimeData( const QMimeData* data ) { - QNetworkReply* r = qobject_cast< QNetworkReply* >( sender() ); - Q_ASSERT( r ); - - if( r->error() == QNetworkReply::NoError ) + if ( data->hasFormat( "application/tomahawk.query.list" ) ) + emit tracks( tracksFromQueryList( data ) ); + else if ( data->hasFormat( "application/tomahawk.result.list" ) ) + emit tracks( tracksFromResultList( data ) ); + else if ( data->hasFormat( "text/plain" ) ) { - QJson::Parser p; - bool ok; - QVariantMap res = p.parse( r, &ok ).toMap(); + QString plainData = QString::fromUtf8( data->data( "text/plain" ).constData() ); + tDebug() << "Got text/plain mime data:" << data->data( "text/plain" ) << "decoded to:" << plainData; + if( plainData.contains( "open.spotify.com") || + plainData.contains( "spotify:track" ) ) + { + QStringList tracks = plainData.split( "\n" ); - if( !ok ) - { - tLog() << "Failed to parse json from Spotify track lookup:" << p.errorString() << "On line" << p.errorLine(); - return; - } else if( !res.contains( "track" ) ) - { - tLog() << "No 'track' item in the spotify track lookup result... not doing anything"; - return; + tDebug() << "Got a list of spotify urls!" << tracks; + SpotifyParser* spot = new SpotifyParser( tracks, this ); + connect( spot, SIGNAL( tracks( QList ) ), this, SIGNAL( tracks( QList ) ) ); } - - // lets parse this baby - QVariantMap t = res.value( "track" ).toMap(); - - QString title, artist, album; - - title = t.value( "name", QString() ).toString(); - // TODO for now only take the first artist - if( t.contains( "artists" ) && t[ "artists" ].canConvert< QVariantList >() && t[ "artists" ].toList().size() > 0 ) - artist = t[ "artists" ].toList().first().toMap().value( "name", QString() ).toString(); - if( t.contains( "album" ) && t[ "album" ].canConvert< QVariantMap >() ) - album = t[ "album" ].toMap().value( "name", QString() ).toString(); - - if( title.isEmpty() && artist.isEmpty() ) // don't have enough... - { - tLog() << "Didn't get an artist and track name from spotify, not enough to build a query on. Aborting" << title << artist << album; - return; - } - - Tomahawk::query_ptr q = Tomahawk::Query::get( artist, title, album, uuid(), true ); - handleOpenTrack( q ); - - } else - { - tLog() << "Error in network request to Spotify for track decoding:" << r->errorString(); } } +QList< query_ptr > +GlobalActionManager::tracksFromQueryList( const QMimeData* data ) +{ + QList< query_ptr > queries; + QByteArray itemData = data->data( "application/tomahawk.query.list" ); + QDataStream stream( &itemData, QIODevice::ReadOnly ); + + while ( !stream.atEnd() ) + { + qlonglong qptr; + stream >> qptr; + + query_ptr* query = reinterpret_cast(qptr); + if ( query && !query->isNull() ) + { + tDebug() << "Dropped query item:" << query->data()->artist() << "-" << query->data()->track(); + queries << *query; + } + } + + return queries; +} + +QList< query_ptr > +GlobalActionManager::tracksFromResultList( const QMimeData* data ) +{ + QList< query_ptr > queries; + QByteArray itemData = data->data( "application/tomahawk.result.list" ); + QDataStream stream( &itemData, QIODevice::ReadOnly ); + + while ( !stream.atEnd() ) + { + qlonglong qptr; + stream >> qptr; + + result_ptr* result = reinterpret_cast(qptr); + if ( result && !result->isNull() ) + { + tDebug() << "Dropped result item:" << result->data()->artist()->name() << "-" << result->data()->track(); + query_ptr q = result->data()->toQuery(); + q->addResults( QList< result_ptr >() << *result ); + queries << q; + } + } + + return queries; +} + +/// SPOTIFY URL HANDLING + +bool +GlobalActionManager::openSpotifyLink( const QString& link ) +{ + SpotifyParser* spot = new SpotifyParser( link, this ); + connect( spot, SIGNAL( track( Tomahawk::query_ptr ) ), this, SLOT( handleOpenTrack( Tomahawk::query_ptr ) ) ); + + return true; +} diff --git a/src/libtomahawk/globalactionmanager.h b/src/libtomahawk/globalactionmanager.h index d6440555a..5f647847e 100644 --- a/src/libtomahawk/globalactionmanager.h +++ b/src/libtomahawk/globalactionmanager.h @@ -28,6 +28,9 @@ #include #include +/** + * Handles global actions such as parsing and creation of links, mime data handling, etc + */ class DLLEXPORT GlobalActionManager : public QObject { Q_OBJECT @@ -38,32 +41,47 @@ public: QUrl openLinkFromQuery( const Tomahawk::query_ptr& query ) const; QUrl openLink( const QString& title, const QString& artist, const QString& album ) const; - // spotify - bool parseSpotifyLink( const QString& link ); + /// Takes a spotify link and performs the default open action on it + bool openSpotifyLink( const QString& link ); void copyToClipboard( const Tomahawk::query_ptr& query ) const; QString copyPlaylistToClipboard( const Tomahawk::dynplaylist_ptr& playlist ); void savePlaylistToFile( const Tomahawk::playlist_ptr& playlist, const QString& filename ); + /** + * QMimeData helpers + * + * Call this to parse the tracks in a QMimeData object to query_ptrs. This will parse internal tomahawk + * data as well as all other formats supported (spotify, etc). + * + * Connect to tracks( QList< query_ptr> ); for the extracted tracks. + */ + bool acceptsMimeData( const QMimeData* data ); + void tracksFromMimeData( const QMimeData* data ); + QStringList mimeTypes() const; + public slots: bool parseTomahawkLink( const QString& link ); void waitingForResolved( bool ); Tomahawk::dynplaylist_ptr loadDynamicPlaylist( const QUrl& url, bool station ); +signals: + /// QMimeData parsing results + void tracks( const QList< Tomahawk::query_ptr >& tracks ); + private slots: void bookmarkPlaylistCreated( const Tomahawk::playlist_ptr& pl ); void showPlaylist(); void xspfCreated( const QByteArray& xspf ); - // SPOTIFY - void spotifyTrackLookupFinished(); - + void handleOpenTrack( const Tomahawk::query_ptr& qry ); private: explicit GlobalActionManager( QObject* parent = 0 ); void doBookmark( const Tomahawk::playlist_ptr& pl, const Tomahawk::query_ptr& q ); + /// handle opening of urls bool handlePlaylistCommand( const QUrl& url ); bool handleCollectionCommand(const QUrl& url ); bool handleQueueCommand(const QUrl& url ); @@ -73,16 +91,19 @@ private: bool handlePlayCommand(const QUrl& url ); bool handleBookmarkCommand(const QUrl& url ); bool handleOpenCommand(const QUrl& url ); - - void handleOpenTrack( const Tomahawk::query_ptr& qry ); - bool doQueueAdd( const QStringList& parts, const QList< QPair< QString, QString > >& queryItems ); + + /// handle parsing mime data + QList< Tomahawk::query_ptr > tracksFromQueryList( const QMimeData* d ); + QList< Tomahawk::query_ptr > tracksFromResultList( const QMimeData* d ); + QString hostname() const; Tomahawk::playlist_ptr m_toShow; Tomahawk::query_ptr m_waitingToBookmark; Tomahawk::query_ptr m_waitingToPlay; + QStringList m_mimeTypes; static GlobalActionManager* s_instance; }; diff --git a/src/libtomahawk/playlist/dynamic/DynamicModel.cpp b/src/libtomahawk/playlist/dynamic/DynamicModel.cpp index 1f5807d70..1cf9267c9 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicModel.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicModel.cpp @@ -1,6 +1,6 @@ /* === This file is part of Tomahawk Player - === * - * Copyright 2010-2011, Christian Muehlhaeuser + * 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 diff --git a/src/libtomahawk/playlist/dynamic/DynamicModel.h b/src/libtomahawk/playlist/dynamic/DynamicModel.h index 5c56d613f..243edd4f2 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicModel.h +++ b/src/libtomahawk/playlist/dynamic/DynamicModel.h @@ -1,6 +1,6 @@ /* === This file is part of Tomahawk Player - === * - * Copyright 2010-2011, Christian Muehlhaeuser + * 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 diff --git a/src/libtomahawk/playlist/playlistmodel.cpp b/src/libtomahawk/playlist/playlistmodel.cpp index 121ef9f82..82a31316c 100644 --- a/src/libtomahawk/playlist/playlistmodel.cpp +++ b/src/libtomahawk/playlist/playlistmodel.cpp @@ -27,6 +27,7 @@ #include "database/databasecommand_playbackhistory.h" #include "dynamic/GeneratorInterface.h" #include "utils/logger.h" +#include "globalactionmanager.h" using namespace Tomahawk; @@ -38,6 +39,9 @@ PlaylistModel::PlaylistModel( QObject* parent ) { qDebug() << Q_FUNC_INFO; + m_dropStorage.parent = QPersistentModelIndex(); + m_dropStorage.row = -10; + setReadOnly( false ); } @@ -168,7 +172,7 @@ PlaylistModel::clear() { if ( rowCount( QModelIndex() ) ) { - emit loadingFinished();; + emit loadingFinished(); emit beginResetModel(); delete m_rootItem; @@ -355,16 +359,32 @@ PlaylistModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int r if ( action == Qt::IgnoreAction || isReadOnly() ) return true; - if ( !data->hasFormat( "application/tomahawk.query.list" ) - && !data->hasFormat( "application/tomahawk.plentry.list" ) - && !data->hasFormat( "application/tomahawk.result.list" ) ) + if ( !GlobalActionManager::instance()->acceptsMimeData( data ) ) return false; + m_dropStorage.row = row; + m_dropStorage.parent = QPersistentModelIndex( parent ); + m_dropStorage.action = action; + connect( GlobalActionManager::instance(), SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) ); + GlobalActionManager::instance()->tracksFromMimeData( data ); + + return true; +} + +void +PlaylistModel::parsedDroppedTracks( QList< query_ptr > tracks ) +{ + + if ( m_dropStorage.row == -10 ) // nope + return; + + disconnect( GlobalActionManager::instance(), SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) ); + int beginRow; - if ( row != -1 ) - beginRow = row; - else if ( parent.isValid() ) - beginRow = parent.row(); + if ( m_dropStorage.row != -1 ) + beginRow = m_dropStorage.row; + else if ( m_dropStorage.parent.isValid() ) + beginRow = m_dropStorage.parent.row(); else beginRow = rowCount( QModelIndex() ); @@ -373,51 +393,10 @@ PlaylistModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int r if ( currentItem().isValid() ) currentuuid = itemFromIndex( currentItem() )->query()->id(); - QList queries; - if ( data->hasFormat( "application/tomahawk.result.list" ) ) + if ( tracks.count() ) { - QByteArray itemData = data->data( "application/tomahawk.result.list" ); - QDataStream stream( &itemData, QIODevice::ReadOnly ); - - while ( !stream.atEnd() ) - { - qlonglong qptr; - stream >> qptr; - - Tomahawk::result_ptr* result = reinterpret_cast(qptr); - if ( result && !result->isNull() ) - { - qDebug() << "Dropped result item:" << result->data()->artist() << "-" << result->data()->track(); - query_ptr q = result->data()->toQuery(); - q->addResults( QList< result_ptr >() << *result ); - queries << q; - } - } - } - - if ( data->hasFormat( "application/tomahawk.query.list" ) ) - { - QByteArray itemData = data->data( "application/tomahawk.query.list" ); - QDataStream stream( &itemData, QIODevice::ReadOnly ); - - while ( !stream.atEnd() ) - { - qlonglong qptr; - stream >> qptr; - - Tomahawk::query_ptr* query = reinterpret_cast(qptr); - if ( query && !query->isNull() ) - { - qDebug() << "Dropped query item:" << query->data()->artist() << "-" << query->data()->track() << action; - queries << *query; - } - } - } - - if ( queries.count() ) - { - emit beginInsertRows( QModelIndex(), beginRow, beginRow + queries.count() - 1 ); - foreach( const Tomahawk::query_ptr& query, queries ) + emit beginInsertRows( QModelIndex(), beginRow, beginRow + tracks.count() - 1 ); + foreach( const Tomahawk::query_ptr& query, tracks ) { plentry_ptr e( new PlaylistEntry() ); e->setGuid( uuid() ); @@ -441,11 +420,12 @@ PlaylistModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int r } emit endInsertRows(); - if ( action == Qt::CopyAction ) + if ( m_dropStorage.action == Qt::CopyAction ) onPlaylistChanged( true ); } - return true; + m_dropStorage.parent = QPersistentModelIndex(); + m_dropStorage.row = -10; } diff --git a/src/libtomahawk/playlist/playlistmodel.h b/src/libtomahawk/playlist/playlistmodel.h index 4ca461fd3..11fc55b2d 100644 --- a/src/libtomahawk/playlist/playlistmodel.h +++ b/src/libtomahawk/playlist/playlistmodel.h @@ -39,6 +39,12 @@ class DLLEXPORT PlaylistModel : public TrackModel { Q_OBJECT +typedef struct { + int row; + QPersistentModelIndex parent; + Qt::DropAction action; +} DropStorageData; + public: explicit PlaylistModel( QObject* parent = 0 ); ~PlaylistModel(); @@ -84,6 +90,7 @@ private slots: void onTracksAdded( const QList& tracks ); void onTracksInserted( unsigned int row, const QList& tracks ); + void parsedDroppedTracks( QList ); void trackResolved( bool ); @@ -93,6 +100,8 @@ private: Tomahawk::playlist_ptr m_playlist; bool m_waitForUpdate, m_isTemporary; QList< Tomahawk::Query* > m_waitingForResolved; + + DropStorageData m_dropStorage; }; #endif // PLAYLISTMODEL_H diff --git a/src/libtomahawk/playlist/trackview.cpp b/src/libtomahawk/playlist/trackview.cpp index bbb3bb0e9..6e379e8bc 100644 --- a/src/libtomahawk/playlist/trackview.cpp +++ b/src/libtomahawk/playlist/trackview.cpp @@ -34,6 +34,7 @@ #include "dynamic/widgets/LoadingSpinner.h" #include "utils/tomahawkutils.h" #include "utils/logger.h" +#include using namespace Tomahawk; @@ -214,9 +215,7 @@ TrackView::dragEnterEvent( QDragEnterEvent* event ) qDebug() << Q_FUNC_INFO; QTreeView::dragEnterEvent( event ); - if ( event->mimeData()->hasFormat( "application/tomahawk.query.list" ) - || event->mimeData()->hasFormat( "application/tomahawk.plentry.list" ) - || event->mimeData()->hasFormat( "application/tomahawk.result.list" ) ) + if ( GlobalActionManager::instance()->acceptsMimeData( event->mimeData() ) ) { m_dragging = true; m_dropRect = QRect(); @@ -238,9 +237,7 @@ TrackView::dragMoveEvent( QDragMoveEvent* event ) return; } - if ( event->mimeData()->hasFormat( "application/tomahawk.query.list" ) - || event->mimeData()->hasFormat( "application/tomahawk.plentry.list" ) - || event->mimeData()->hasFormat( "application/tomahawk.result.list" ) ) + if ( GlobalActionManager::instance()->acceptsMimeData( event->mimeData() ) ) { setDirtyRegion( m_dropRect ); const QPoint pos = event->pos(); @@ -274,20 +271,7 @@ TrackView::dropEvent( QDropEvent* event ) } else { - if ( event->mimeData()->hasFormat( "application/tomahawk.query.list" ) ) - { - const QPoint pos = event->pos(); - const QModelIndex index = indexAt( pos ); - - qDebug() << "Drop Event accepted at row:" << index.row(); - event->acceptProposedAction(); - - if ( !model()->isReadOnly() ) - { - model()->dropMimeData( event->mimeData(), event->proposedAction(), index.row(), 0, index.parent() ); - } - } - else if ( event->mimeData()->hasFormat( "application/tomahawk.result.list" ) ) + if ( GlobalActionManager::instance()->acceptsMimeData( event->mimeData() ) ) { const QPoint pos = event->pos(); const QModelIndex index = indexAt( pos ); diff --git a/src/libtomahawk/utils/spotifyparser.cpp b/src/libtomahawk/utils/spotifyparser.cpp new file mode 100644 index 000000000..fb2e7993c --- /dev/null +++ b/src/libtomahawk/utils/spotifyparser.cpp @@ -0,0 +1,147 @@ +/* === 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 "spotifyparser.h" + +#include "utils/logger.h" +#include "utils/tomahawkutils.h" +#include "query.h" + +#include + +#include +#include + +using namespace Tomahawk; + +SpotifyParser::SpotifyParser( const QStringList& trackUrls, QObject* parent ) + : QObject ( parent ) + , m_single( false ) +{ + foreach ( const QString& url, trackUrls ) + lookupTrack( url ); +} + +SpotifyParser::SpotifyParser( const QString& trackUrl, QObject* parent ) + : QObject ( parent ) + , m_single( true ) +{ + lookupTrack( trackUrl ); +} + +SpotifyParser::~SpotifyParser() +{ + +} + +void +SpotifyParser::lookupTrack( const QString& link ) +{ + if ( !link.contains( "track" ) ) // we only support track links atm + return; + + // we need Spotify URIs such as spotify:track:XXXXXX, so if we by chance get a http://open.spotify.com url, convert it + QString uri = link; + if ( link.contains( "open.spotify.com" ) ) + { + QString hash = link; + hash.replace( "http://open.spotify.com/track/", "" ); + uri = QString( "spotify:track:%1" ).arg( hash ); + } + + tLog() << "Parsing Spotify Track URI:" << uri; + + QUrl url = QUrl( QString( "http://ws.spotify.com/lookup/1/.json?uri=%1" ).arg( uri ) ); + tDebug() << "Looking up..." << url.toString(); + + QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( url ) ); + connect( reply, SIGNAL( finished() ), this, SLOT( spotifyTrackLookupFinished() ) ); + + m_queries.insert( reply ); + +} + +void +SpotifyParser::spotifyTrackLookupFinished() +{ + QNetworkReply* r = qobject_cast< QNetworkReply* >( sender() ); + Q_ASSERT( r ); + m_queries.remove( r ); + r->deleteLater(); + + if ( r->error() == QNetworkReply::NoError ) + { + QJson::Parser p; + bool ok; + QVariantMap res = p.parse( r, &ok ).toMap(); + + if ( !ok ) + { + tLog() << "Failed to parse json from Spotify track lookup:" << p.errorString() << "On line" << p.errorLine(); + checkFinished(); + return; + } else if ( !res.contains( "track" ) ) + { + tLog() << "No 'track' item in the spotify track lookup result... not doing anything"; + checkFinished(); + return; + } + + // lets parse this baby + QVariantMap t = res.value( "track" ).toMap(); + + QString title, artist, album; + + title = t.value( "name", QString() ).toString(); + // TODO for now only take the first artist + if ( t.contains( "artists" ) && t[ "artists" ].canConvert< QVariantList >() && t[ "artists" ].toList().size() > 0 ) + artist = t[ "artists" ].toList().first().toMap().value( "name", QString() ).toString(); + if ( t.contains( "album" ) && t[ "album" ].canConvert< QVariantMap >() ) + album = t[ "album" ].toMap().value( "name", QString() ).toString(); + + if ( title.isEmpty() && artist.isEmpty() ) // don't have enough... + { + tLog() << "Didn't get an artist and track name from spotify, not enough to build a query on. Aborting" << title << artist << album; + return; + } + + Tomahawk::query_ptr q = Tomahawk::Query::get( artist, title, album, uuid(), true ); + m_tracks << q; + + } else + { + tLog() << "Error in network request to Spotify for track decoding:" << r->errorString(); + } + + checkFinished(); +} + +void +SpotifyParser::checkFinished() +{ + if ( m_queries.isEmpty() ) // we're done + { + if ( m_single && !m_tracks.isEmpty() ) + emit track( m_tracks.first() ); + else if ( !m_single && !m_tracks.isEmpty() ) + emit tracks( m_tracks ); + + deleteLater(); + } + +} diff --git a/src/libtomahawk/utils/spotifyparser.h b/src/libtomahawk/utils/spotifyparser.h new file mode 100644 index 000000000..0a7a7a511 --- /dev/null +++ b/src/libtomahawk/utils/spotifyparser.h @@ -0,0 +1,64 @@ +/* === 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 SPOTIFY_PARSER_H +#define SPOTIFY_PARSER_H + +#include "dllmacro.h" +#include "typedefs.h" + +#include +#include +#include + +class QNetworkReply; +namespace Tomahawk +{ + +/** + * Small class to parse spotify links into query_ptrs + * + * Connect to the signals to get the results + */ +class DLLEXPORT SpotifyParser : public QObject +{ + Q_OBJECT +public: + explicit SpotifyParser( const QString& trackUrl, QObject* parent = 0 ); + explicit SpotifyParser( const QStringList& trackUrls, QObject* parent = 0 ); + virtual ~SpotifyParser(); + +signals: + void track( const Tomahawk::query_ptr& track ); + void tracks( const QList< Tomahawk::query_ptr > tracks ); + +private slots: + void spotifyTrackLookupFinished(); + +private: + void lookupTrack( const QString& track ); + void checkFinished(); + + bool m_single; + QList< query_ptr > m_tracks; + QSet< QNetworkReply* > m_queries; +}; + +} + +#endif diff --git a/src/sourcetree/items/categoryitems.cpp b/src/sourcetree/items/categoryitems.cpp index 36f57401b..336de6fe1 100644 --- a/src/sourcetree/items/categoryitems.cpp +++ b/src/sourcetree/items/categoryitems.cpp @@ -28,6 +28,7 @@ #include "widgets/playlisttypeselectordlg.h" #include #include "utils/logger.h" +#include using namespace Tomahawk; @@ -121,12 +122,7 @@ CategoryAddItem::icon() const bool CategoryAddItem::willAcceptDrag( const QMimeData* data ) const { - if ( ( m_categoryType == SourcesModel::PlaylistsCategory || m_categoryType == SourcesModel::StationsCategory ) && - ( - data->hasFormat( "application/tomahawk.query.list" ) || - data->hasFormat( "application/tomahawk.result.list" ) - ) - ) + if ( ( m_categoryType == SourcesModel::PlaylistsCategory || m_categoryType == SourcesModel::StationsCategory ) && GlobalActionManager::instance()->acceptsMimeData( data ) ) { return true; } @@ -138,78 +134,43 @@ bool CategoryAddItem::dropMimeData( const QMimeData* data, Qt::DropAction ) { // Create a new playlist seeded with these items - if ( data->hasFormat( "application/tomahawk.query.list" ) || data->hasFormat( "application/tomahawk.result.list" ) ) { + connect( GlobalActionManager::instance(), SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) ); + GlobalActionManager::instance()->tracksFromMimeData( data ); - QList< Tomahawk::query_ptr > queries; + return true; +} - if ( data->hasFormat( "application/tomahawk.query.list" ) ) { - QByteArray itemData = data->data( "application/tomahawk.query.list" ); - QDataStream stream( &itemData, QIODevice::ReadOnly ); +void +CategoryAddItem::parsedDroppedTracks( const QList< query_ptr >& tracks ) +{ + disconnect( GlobalActionManager::instance(), SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) ); + if( m_categoryType == SourcesModel::PlaylistsCategory ) { - while ( !stream.atEnd() ) - { - qlonglong qptr; - stream >> qptr; + playlist_ptr newpl = Playlist::create( SourceList::instance()->getLocal(), uuid(), "New Playlist", "", SourceList::instance()->getLocal()->friendlyName(), false, tracks ); + ViewManager::instance()->show( newpl ); - Tomahawk::query_ptr* query = reinterpret_cast(qptr); - if ( query && !query->isNull() ) - { - qDebug() << "Dropped query item:" << query->data()->artist() << "-" << query->data()->track(); - queries << *query; - } - } - } else if( data->hasFormat( "application/tomahawk.result.list" ) ) { - QByteArray itemData = data->data( "application/tomahawk.result.list" ); - QDataStream stream( &itemData, QIODevice::ReadOnly ); + // Give a shot to try to rename it. The playlist has to be created first. ugly. + QTimer::singleShot( 300, APP->mainWindow()->sourceTreeView(), SLOT( renamePlaylist() ) ); + } else if( m_categoryType == SourcesModel::StationsCategory ) { + // seed the playlist with these song filters + QString name = tracks.isEmpty() ? tr( "New Station" ) : tr( "%1 Station" ).arg( tracks.first()->track() ); + dynplaylist_ptr newpl = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), name, "", SourceList::instance()->getLocal()->friendlyName(), OnDemand, false ); + newpl->setMode( OnDemand ); - while ( !stream.atEnd() ) - { - qlonglong qptr; - stream >> qptr; - - Tomahawk::result_ptr* result = reinterpret_cast(qptr); - if ( result && !result->isNull() ) - { - qDebug() << "Dropped result item:" << result->data()->artist() << "-" << result->data()->track(); - query_ptr q = result->data()->toQuery(); - q->addResults( QList< result_ptr >() << *result ); - queries << q; - } - } + // now we want to add each query as a song filter... + QList< dyncontrol_ptr > contrls; + foreach( const Tomahawk::query_ptr& q, tracks ) { + dyncontrol_ptr c = newpl->generator()->createControl( "Song" ); + c->setInput( QString( "%1 %2" ).arg( q->track() ).arg( q->artist() ) ); + contrls << c; } - if( m_categoryType == SourcesModel::PlaylistsCategory ) { + newpl->createNewRevision( uuid(), newpl->currentrevision(), newpl->type(), contrls ); - playlist_ptr newpl = Playlist::create( SourceList::instance()->getLocal(), uuid(), "New Playlist", "", SourceList::instance()->getLocal()->friendlyName(), false, queries ); - ViewManager::instance()->show( newpl ); - - // Give a shot to try to rename it. The playlist has to be created first. ugly. - QTimer::singleShot( 300, APP->mainWindow()->sourceTreeView(), SLOT( renamePlaylist() ) ); - } else if( m_categoryType == SourcesModel::StationsCategory ) { - // seed the playlist with these song filters - QString name = queries.isEmpty() ? tr( "New Station" ) : tr( "%1 Station" ).arg( queries.first()->track() ); - dynplaylist_ptr newpl = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), name, "", SourceList::instance()->getLocal()->friendlyName(), OnDemand, false ); - newpl->setMode( OnDemand ); - - // now we want to add each query as a song filter... - QList< dyncontrol_ptr > contrls; - foreach( const Tomahawk::query_ptr& q, queries ) { - dyncontrol_ptr c = newpl->generator()->createControl( "Song" ); - c->setInput( QString( "%1 %2" ).arg( q->track() ).arg( q->artist() ) ); - contrls << c; - } - - newpl->createNewRevision( uuid(), newpl->currentrevision(), newpl->type(), contrls ); - - - ViewManager::instance()->show( newpl ); - // Give a shot to try to rename it. The playlist has to be created first. ugly. - QTimer::singleShot( 300, APP->mainWindow()->sourceTreeView(), SLOT( renamePlaylist() ) ); - } - - return true; + ViewManager::instance()->show( newpl ); + // Give a shot to try to rename it. The playlist has to be created first. ugly. + QTimer::singleShot( 300, APP->mainWindow()->sourceTreeView(), SLOT( renamePlaylist() ) ); } - return false; } diff --git a/src/sourcetree/items/categoryitems.h b/src/sourcetree/items/categoryitems.h index 2ad4b2801..52468ff06 100644 --- a/src/sourcetree/items/categoryitems.h +++ b/src/sourcetree/items/categoryitems.h @@ -35,6 +35,9 @@ public: virtual bool willAcceptDrag(const QMimeData* data) const; virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action); +private slots: + void parsedDroppedTracks( const QList< Tomahawk::query_ptr >& tracks ); + private: SourcesModel::CategoryType m_categoryType; }; diff --git a/src/sourcetree/items/playlistitems.cpp b/src/sourcetree/items/playlistitems.cpp index c94dc5c0e..2bef4cce8 100644 --- a/src/sourcetree/items/playlistitems.cpp +++ b/src/sourcetree/items/playlistitems.cpp @@ -25,6 +25,7 @@ #include "collectionitem.h" #include "utils/tomahawkutils.h" #include "utils/logger.h" +#include "globalactionmanager.h" using namespace Tomahawk; @@ -138,56 +139,23 @@ PlaylistItem::dropMimeData( const QMimeData* data, Qt::DropAction action ) data->data( "application/tomahawk.playlist.id" ) == m_playlist->guid() ) return false; // don't allow dropping on ourselves - if ( data->hasFormat( "application/tomahawk.result.list" ) ) - { - QByteArray itemData = data->data( "application/tomahawk.result.list" ); - QDataStream stream( &itemData, QIODevice::ReadOnly ); + connect( GlobalActionManager::instance(), SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) ); + GlobalActionManager::instance()->tracksFromMimeData( data ); - while ( !stream.atEnd() ) - { - qlonglong qptr; - stream >> qptr; + // TODO cant' know if it works or not yet... + return true; +} - Tomahawk::result_ptr* result = reinterpret_cast(qptr); - if ( result && !result->isNull() ) - { - qDebug() << "Dropped result item:" << result->data()->artist() << "-" << result->data()->track(); - query_ptr q = result->data()->toQuery(); - q->addResults( QList< result_ptr >() << *result ); - queries << q; - } - } - } - - if ( data->hasFormat( "application/tomahawk.query.list" ) ) - { - QByteArray itemData = data->data( "application/tomahawk.query.list" ); - QDataStream stream( &itemData, QIODevice::ReadOnly ); - - while ( !stream.atEnd() ) - { - qlonglong qptr; - stream >> qptr; - - Tomahawk::query_ptr* query = reinterpret_cast(qptr); - if ( query && !query->isNull() ) - { - qDebug() << "Dropped query item:" << query->data()->artist() << "-" << query->data()->track(); - queries << *query; - } - } - } - - if ( queries.count() && !m_playlist.isNull() && m_playlist->author()->isLocal() ) +void +PlaylistItem::parsedDroppedTracks( const QList< query_ptr >& tracks) +{ + disconnect( GlobalActionManager::instance(), SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) ); + if ( tracks.count() && !m_playlist.isNull() && m_playlist->author()->isLocal() ) { qDebug() << "on playlist:" << m_playlist->title() << m_playlist->guid() << m_playlist->currentrevision(); - m_playlist->addEntries( queries, m_playlist->currentrevision() ); - - return true; + m_playlist->addEntries( tracks, m_playlist->currentrevision() ); } - - return false; } diff --git a/src/sourcetree/items/playlistitems.h b/src/sourcetree/items/playlistitems.h index ec974b74f..42ce35e00 100644 --- a/src/sourcetree/items/playlistitems.h +++ b/src/sourcetree/items/playlistitems.h @@ -43,6 +43,7 @@ protected: private slots: void onPlaylistLoaded( Tomahawk::PlaylistRevision revision ); void onPlaylistChanged(); + void parsedDroppedTracks( const QList& tracks ); private: bool m_loaded; diff --git a/src/sourcetree/sourcesmodel.cpp b/src/sourcetree/sourcesmodel.cpp index 1eb4fb386..ff73ba253 100644 --- a/src/sourcetree/sourcesmodel.cpp +++ b/src/sourcetree/sourcesmodel.cpp @@ -32,6 +32,7 @@ #include "viewmanager.h" #include "utils/logger.h" +#include "globalactionmanager.h" using namespace Tomahawk; @@ -179,10 +180,7 @@ SourcesModel::setData( const QModelIndex& index, const QVariant& value, int role QStringList SourcesModel::mimeTypes() const { - QStringList types; - types << "application/tomahawk.query.list"; - types << "application/tomahawk.result.list"; - return types; + return GlobalActionManager::instance()->mimeTypes(); } diff --git a/src/sourcetree/sourcetreeview.cpp b/src/sourcetree/sourcetreeview.cpp index 970e4f268..3b77c6dab 100644 --- a/src/sourcetree/sourcetreeview.cpp +++ b/src/sourcetree/sourcetreeview.cpp @@ -431,8 +431,7 @@ SourceTreeView::dragEnterEvent( QDragEnterEvent* event ) qDebug() << Q_FUNC_INFO; QTreeView::dragEnterEvent( event ); - if ( event->mimeData()->hasFormat( "application/tomahawk.query.list" ) - || event->mimeData()->hasFormat( "application/tomahawk.result.list" ) ) + if ( GlobalActionManager::instance()->acceptsMimeData( event->mimeData() ) ) { m_dragging = true; m_dropRect = QRect(); @@ -460,8 +459,7 @@ SourceTreeView::dragMoveEvent( QDragMoveEvent* event ) bool accept = false; QTreeView::dragMoveEvent( event ); - if ( event->mimeData()->hasFormat( "application/tomahawk.query.list" ) - || event->mimeData()->hasFormat( "application/tomahawk.result.list" ) ) + if ( GlobalActionManager::instance()->acceptsMimeData( event->mimeData() ) ) { setDirtyRegion( m_dropRect ); const QPoint pos = event->pos(); diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp index cd2c7a502..c15aed9d0 100644 --- a/src/tomahawkapp.cpp +++ b/src/tomahawkapp.cpp @@ -516,7 +516,7 @@ TomahawkApp::loadUrl( const QString& url ) if ( url.startsWith( "tomahawk://" ) ) return GlobalActionManager::instance()->parseTomahawkLink( url ); else if ( url.contains( "open.spotify.com" ) || url.contains( "spotify:track" ) ) - return GlobalActionManager::instance()->parseSpotifyLink( url ); + return GlobalActionManager::instance()->openSpotifyLink( url ); else { QFile f( url ); From 89cec23815a0325fd0bb2521b7011d8e520f1448 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Fri, 5 Aug 2011 07:53:30 -0400 Subject: [PATCH 2/2] On OS X drops from outside the apps are Qt::MoveActions.... --- src/sourcetree/sourcesmodel.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sourcetree/sourcesmodel.cpp b/src/sourcetree/sourcesmodel.cpp index ff73ba253..94bd3e332 100644 --- a/src/sourcetree/sourcesmodel.cpp +++ b/src/sourcetree/sourcesmodel.cpp @@ -214,7 +214,11 @@ SourcesModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int ro Qt::DropActions SourcesModel::supportedDropActions() const { +#ifdef Q_OS_MAC + return Qt::CopyAction | Qt::MoveAction; +#else return Qt::CopyAction; +#endif }