diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index a2bc8d9c9..074f5d3e4 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -120,6 +120,7 @@ set( libGuiSources widgets/playlisttypeselectordlg.cpp widgets/welcomewidget.cpp widgets/whatshotwidget.cpp + widgets/ChartDataLoader.cpp widgets/RecentlyPlayedPlaylistsModel.cpp widgets/RecentPlaylistsModel.cpp widgets/OverlayButton.cpp @@ -136,6 +137,10 @@ set( libGuiSources widgets/BreadcrumbButton.cpp ) +IF(QCA2_FOUND) + set( libGuiSources ${libGuiSources} utils/groovesharkparser.cpp ) +ENDIF(QCA2_FOUND) + set( libGuiHeaders actioncollection.h @@ -237,6 +242,7 @@ set( libGuiHeaders widgets/welcomewidget.h widgets/whatshotwidget.h widgets/whatshotwidget_p.h + widgets/ChartDataLoader.h widgets/RecentlyPlayedPlaylistsModel.h widgets/RecentPlaylistsModel.h widgets/OverlayButton.h @@ -262,9 +268,12 @@ set( libGuiHeaders jobview/LatchedStatusItem.h thirdparty/Qocoa/qsearchfield.h - ) +IF(QCA2_FOUND) + set( libGuiHeaders ${libGuiHeaders} utils/groovesharkparser.h ) +ENDIF(QCA2_FOUND) + set( libSources tomahawksettings.cpp sourcelist.cpp diff --git a/src/libtomahawk/dropjob.cpp b/src/libtomahawk/dropjob.cpp index 88e2b52fb..5e67b724e 100644 --- a/src/libtomahawk/dropjob.cpp +++ b/src/libtomahawk/dropjob.cpp @@ -35,6 +35,10 @@ #include "utils/xspfloader.h" #include "jobview/JobStatusView.h" #include "jobview/JobStatusModel.h" +#ifdef QCA2_FOUND +#include "utils/groovesharkparser.h" +#endif //QCA2_FOUND + using namespace Tomahawk; @@ -120,6 +124,9 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType // Not the most elegant if ( url.contains( "spotify" ) && url.contains( "playlist" ) && s_canParseSpotifyPlaylists ) return true; + + if ( url.contains( "grooveshark.com" ) && url.contains( "playlist" ) ) + return true; } if ( acceptedType.testFlag( Track ) ) @@ -188,7 +195,10 @@ DropJob::isDropType( DropJob::DropType desired, const QMimeData* data ) if ( url.contains( "rdio.com" ) && url.contains( "people" ) && url.contains( "playlist" ) ) return true; - +#ifdef QCA2_FOUND + if ( url.contains( "grooveshark.com" ) && url.contains( "playlist" ) ) + return true; +#endif //QCA2_FOUND if ( ShortenedLinkParser::handlesUrl( url ) ) return true; } @@ -555,6 +565,31 @@ DropJob::handleRdioUrls( const QString& urlsRaw ) rdio->parse( urls ); } +void +DropJob::handleGroovesharkUrls ( const QString& urlsRaw ) +{ +#ifdef QCA2_FOUND + QStringList urls = urlsRaw.split( QRegExp( "\\s+" ), QString::SkipEmptyParts ); + tDebug() << "Got Grooveshark urls!" << urls; + + if ( dropAction() == Default ) + setDropAction( Create ); + + GroovesharkParser* groove = new GroovesharkParser( urls, dropAction() == Create, this ); + connect( groove, SIGNAL( tracks( QList ) ), this, SLOT( onTracksAdded( QList< Tomahawk::query_ptr > ) ) ); + + if ( dropAction() == Append ) + { + tDebug() << Q_FUNC_INFO << "Asking for grooveshark contents from" << urls; + connect( groove, SIGNAL( tracks( QList ) ), this, SLOT( onTracksAdded( QList< Tomahawk::query_ptr > ) ) ); + m_queryCount++; + } +#else + tLog() << "Tomahawk compiled without QCA support, cannot use groovesharkparser"; +#endif +} + + void DropJob::handleAllUrls( const QString& urls ) @@ -569,6 +604,10 @@ DropJob::handleAllUrls( const QString& urls ) handleSpotifyUrls( urls ); else if ( urls.contains( "rdio.com" ) ) handleRdioUrls( urls ); +#ifdef QCA2_FOUND + else if ( urls.contains( "grooveshark.com" ) ) + handleGroovesharkUrls( urls ); +#endif else handleTrackUrls ( urls ); } diff --git a/src/libtomahawk/dropjob.h b/src/libtomahawk/dropjob.h index eda72504e..71ca32787 100644 --- a/src/libtomahawk/dropjob.h +++ b/src/libtomahawk/dropjob.h @@ -106,6 +106,7 @@ public: void handleM3u( const QString& urls ); void handleSpotifyUrls( const QString& urls ); void handleRdioUrls( const QString& urls ); + void handleGroovesharkUrls( const QString& urls ); static bool canParseSpotifyPlaylists() { return s_canParseSpotifyPlaylists; } static void setCanParseSpotifyPlaylists( bool parseable ) { s_canParseSpotifyPlaylists = parseable; } diff --git a/src/libtomahawk/pipeline.cpp b/src/libtomahawk/pipeline.cpp index af0b271e8..73d14fce4 100644 --- a/src/libtomahawk/pipeline.cpp +++ b/src/libtomahawk/pipeline.cpp @@ -169,7 +169,7 @@ Pipeline::removeScriptResolver( const QString& scriptPath ) if ( r ) { r->stop(); - connect( r, SIGNAL( stopped() ), r, SLOT( deleteLater() ) ); + r->deleteLater(); } } diff --git a/src/libtomahawk/resolvers/scriptresolver.cpp b/src/libtomahawk/resolvers/scriptresolver.cpp index 29a259906..56d027f65 100644 --- a/src/libtomahawk/resolvers/scriptresolver.cpp +++ b/src/libtomahawk/resolvers/scriptresolver.cpp @@ -300,7 +300,7 @@ ScriptResolver::cmdExited( int code, QProcess::ExitStatus status ) if ( m_stopped ) { tLog() << "*** Script resolver stopped "; - emit stopped(); + emit terminated(); return; } diff --git a/src/libtomahawk/resolvers/scriptresolver.h b/src/libtomahawk/resolvers/scriptresolver.h index 9191396be..c7cd9a9e4 100644 --- a/src/libtomahawk/resolvers/scriptresolver.h +++ b/src/libtomahawk/resolvers/scriptresolver.h @@ -55,7 +55,7 @@ public: virtual bool running() const; signals: - void stopped(); + void terminated(); public slots: virtual void stop(); diff --git a/src/libtomahawk/thirdparty/Qocoa/qocoa_mac.h b/src/libtomahawk/thirdparty/Qocoa/qocoa_mac.h index d86f9ad27..1b6963d11 100644 --- a/src/libtomahawk/thirdparty/Qocoa/qocoa_mac.h +++ b/src/libtomahawk/thirdparty/Qocoa/qocoa_mac.h @@ -45,6 +45,7 @@ static inline void zeroLayout(void *cocoaView, QWidget *parent) { QVBoxLayout *layout = new QVBoxLayout(parent); layout->setMargin(0); + parent->setAttribute(Qt::WA_NativeWindow); layout->addWidget(new QMacCocoaViewContainer(cocoaView, parent)); } diff --git a/src/libtomahawk/thirdparty/Qocoa/qsearchfield_mac.mm b/src/libtomahawk/thirdparty/Qocoa/qsearchfield_mac.mm index 5f60cf1e8..120a8c291 100644 --- a/src/libtomahawk/thirdparty/Qocoa/qsearchfield_mac.mm +++ b/src/libtomahawk/thirdparty/Qocoa/qsearchfield_mac.mm @@ -25,8 +25,6 @@ THE SOFTWARE. #include "qocoa_mac.h" -#include "qsearchfield.h" - #import "Foundation/NSAutoreleasePool.h" #import "Foundation/NSNotification.h" #import "AppKit/NSSearchField.h" diff --git a/src/libtomahawk/utils/groovesharkparser.cpp b/src/libtomahawk/utils/groovesharkparser.cpp new file mode 100644 index 000000000..59c898822 --- /dev/null +++ b/src/libtomahawk/utils/groovesharkparser.cpp @@ -0,0 +1,258 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Leo Franchi + * Copyright 2010-2011, Hugo Lindström + * Copyright 2010-2011, Stefan Derkits + * + * 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 "groovesharkparser.h" + +#include "utils/logger.h" +#include "utils/tomahawkutils.h" +#include "query.h" +#include "sourcelist.h" +#include "dropjob.h" +#include "jobview/JobStatusView.h" +#include "jobview/JobStatusModel.h" +#include "dropjobnotifier.h" +#include "viewmanager.h" + +#include + +#include + +#include +#include +#include + +using namespace Tomahawk; + +QPixmap* GroovesharkParser::s_pixmap = 0; + +const char* enApiSecret = "erCj5s0Vebyqtc9Aduyotc1CLListJ9HfO2os5hBeew="; + +GroovesharkParser::GroovesharkParser( const QStringList& trackUrls, bool createNewPlaylist, QObject* parent ) + : QObject ( parent ) + , m_limit ( 40 ) + , m_trackMode( true ) + , m_createNewPlaylist( createNewPlaylist ) + , m_browseJob( 0 ) +{ + QByteArray magic = QByteArray::fromBase64( enApiSecret ); + + QByteArray wand = QByteArray::fromBase64( QCoreApplication::applicationName().toLatin1() ); + int length = magic.length(), n2 = wand.length(); + for ( int i=0; ipost( QNetworkRequest( url ), data ); + connect( reply, SIGNAL( finished() ), this, SLOT( groovesharkLookupFinished() ) ); + + m_browseJob = new DropJobNotifier( pixmap(), "Grooveshark", type, reply ); + JobStatusView::instance()->model()->addJob( m_browseJob ); + + m_queries.insert( reply ); +} + +void +GroovesharkParser::groovesharkLookupFinished() +{ + 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 Grooveshark browse item :" << p.errorString() << "On line" << p.errorLine(); + checkTrackFinished(); + return; + } + + QVariantList list = res.value( "result" ).toMap().value( "songs" ).toList(); + foreach (const QVariant& var, list) + { + QVariantMap trackResult = var.toMap(); + + QString title, artist, album; + + title = trackResult.value( "SongName", QString() ).toString(); + artist = trackResult.value( "ArtistName", QString() ).toString(); + album = trackResult.value( "AlbumName", QString() ).toString(); + + if ( title.isEmpty() && artist.isEmpty() ) // don't have enough... + { + tLog() << "Didn't get an artist and track name from grooveshark, not enough to build a query on. Aborting" << title << artist << album; + return; + } + + Tomahawk::query_ptr q = Tomahawk::Query::get( artist, title, album, uuid(), m_trackMode ); + m_tracks << q; + } + + + } else + { + tLog() << "Error in network request to grooveshark for track decoding:" << r->errorString(); + } + + if ( m_trackMode ) + checkTrackFinished(); + else + checkPlaylistFinished(); +} + +void +GroovesharkParser::checkPlaylistFinished() +{ + tDebug() << "Checking for grooveshark batch playlist job finished" << m_queries.isEmpty() << m_createNewPlaylist; + if ( m_queries.isEmpty() ) // we're done + { + if ( m_browseJob ) + m_browseJob->setFinished(); + + if( m_createNewPlaylist && !m_tracks.isEmpty() ) + { + m_playlist = Playlist::create( SourceList::instance()->getLocal(), + uuid(), + m_title, + m_info, + m_creator, + false, + m_tracks ); + connect( m_playlist.data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), this, SLOT( playlistCreated() ) ); + return; + } + + + emit tracks( m_tracks ); + + deleteLater(); + } +} + +void +GroovesharkParser::checkTrackFinished() +{ + tDebug() << "Checking for grooveshark batch track job finished" << m_queries.isEmpty(); + if ( m_queries.isEmpty() ) // we're done + { + if ( m_browseJob ) + m_browseJob->setFinished(); + + emit tracks( m_tracks ); + + deleteLater(); + } + +} + +void +GroovesharkParser::playlistCreated() +{ + + ViewManager::instance()->show( m_playlist ); + + deleteLater(); +} + + +QPixmap +GroovesharkParser::pixmap() const +{ + if ( !s_pixmap ) + s_pixmap = new QPixmap( RESPATH "images/grooveshark.png" ); + + return *s_pixmap; +} diff --git a/src/libtomahawk/utils/groovesharkparser.h b/src/libtomahawk/utils/groovesharkparser.h new file mode 100644 index 000000000..a387ed928 --- /dev/null +++ b/src/libtomahawk/utils/groovesharkparser.h @@ -0,0 +1,87 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Leo Franchi + * Copyright 2010-2011, Hugo Lindström + * Copyright 2011, Stefan Derkits + * + * 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 GROOVESHARKPARSER_H +#define GROOVESHARKPARSER_H + +#include "dllmacro.h" +#include "typedefs.h" +#include "query.h" +#include "jobview/JobStatusItem.h" + +#include + +#include +#include +#include + +/** + * Small class to parse grooveshark links into query_ptrs + * + * Connect to the signals to get the results + */ + +class QNetworkReply; + +namespace Tomahawk +{ + +class DropJobNotifier; + +class DLLEXPORT GroovesharkParser : public QObject +{ + Q_OBJECT +public: + explicit GroovesharkParser( const QStringList& trackUrls, bool createNewPlaylist = false, QObject* parent = 0 ); + virtual ~GroovesharkParser(); +signals: + void track( const Tomahawk::query_ptr& track ); + void tracks( const QList< Tomahawk::query_ptr > tracks ); + void playlist( const Tomahawk::query_ptr& playlist ); + +private slots: + void groovesharkLookupFinished(); + + void playlistCreated(); +private: + QPixmap pixmap() const; + + void lookupUrl( const QString& url ); + void lookupGroovesharkPlaylist( const QString& playlist ); + void checkTrackFinished(); + void checkPlaylistFinished(); + int m_limit; + bool m_trackMode; + bool m_createNewPlaylist; + QList< query_ptr > m_tracks; + QSet< QNetworkReply* > m_queries; + QString m_title, m_info, m_creator; + Tomahawk::playlist_ptr m_playlist; + DropJobNotifier* m_browseJob; + + QCA::SymmetricKey m_apiKey; + + static QPixmap* s_pixmap; + +}; + +} + +#endif // GROOVESHARKPARSER_H diff --git a/src/libtomahawk/utils/shortenedlinkparser.cpp b/src/libtomahawk/utils/shortenedlinkparser.cpp index 0e196449d..6c37f951e 100644 --- a/src/libtomahawk/utils/shortenedlinkparser.cpp +++ b/src/libtomahawk/utils/shortenedlinkparser.cpp @@ -56,6 +56,7 @@ ShortenedLinkParser::handlesUrl( const QString& url ) url.contains( "fb.me" ) || url.contains( "itun.es" ) || url.contains( "tinyurl.com" ) || + url.contains( "tinysong.com" ) || url.contains( "rd.io" ) ); } diff --git a/src/libtomahawk/widgets/ChartDataLoader.cpp b/src/libtomahawk/widgets/ChartDataLoader.cpp new file mode 100644 index 000000000..a786aa199 --- /dev/null +++ b/src/libtomahawk/widgets/ChartDataLoader.cpp @@ -0,0 +1,72 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 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 "ChartDataLoader.h" + +using namespace Tomahawk; + + +ChartDataLoader::ChartDataLoader() + : QObject( 0 ) +{ +} + +void +ChartDataLoader::go() +{ + switch ( m_type ) + { + case Track: + { + QList< query_ptr > track_ptrs; + foreach ( const Tomahawk::InfoSystem::InfoStringHash& track, m_data ) + { + track_ptrs << Query::get( track[ "artist" ], track[ "track" ], QString(), uuid(), false ); + } + + emit tracks( this, track_ptrs ); + break; + } + case Artist: + { + QList< artist_ptr > artist_ptrs; + + foreach ( const QString& artistname, m_artists ) + { + artist_ptrs << Artist::get( artistname, false ); + } + + emit artists( this, artist_ptrs ); + break; + } + case Album: + { + QList< album_ptr > album_ptrs; + + foreach ( const Tomahawk::InfoSystem::InfoStringHash& album, m_data ) + { + tDebug( LOGVERBOSE) << Q_FUNC_INFO << "Getting album" << album[ "album" ] << "By" << album[ "artist" ]; + artist_ptr artistPtr = Artist::get( album[ "artist" ], false ); + album_ptr albumPtr = Album::get( artistPtr, album[ "album" ], false ); + album_ptrs << albumPtr; + } + + emit albums( this, album_ptrs ); + break; + } + } +} diff --git a/src/libtomahawk/widgets/ChartDataLoader.h b/src/libtomahawk/widgets/ChartDataLoader.h new file mode 100644 index 000000000..3fbb4cae7 --- /dev/null +++ b/src/libtomahawk/widgets/ChartDataLoader.h @@ -0,0 +1,69 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 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 CHARTDATALOADER_H +#define CHARTDATALOADER_H + +#include "infosystem/infosystem.h" +#include "query.h" +#include "artist.h" +#include "album.h" + +#include + +namespace Tomahawk +{ + +/** + Synchronous loading of track, artist, album objects from the db + into their respective tomahawk types. Move this object to a thread + and listen to the result signals. +*/ +class ChartDataLoader : public QObject +{ + Q_OBJECT +public: + enum DataType { + Track, + Artist, + Album + }; + + ChartDataLoader(); + + void setType( DataType type ) { m_type = type; } + void setData( const QList< InfoSystem::InfoStringHash >& data ) { m_data = data; } + void setData( const QStringList& artists ) { m_artists = artists; } + +public slots: + void go(); + +signals: + void tracks( Tomahawk::ChartDataLoader*, const QList< Tomahawk::query_ptr >& tracks ); + void artists( Tomahawk::ChartDataLoader*, const QList< Tomahawk::artist_ptr >& artists ); + void albums( Tomahawk::ChartDataLoader*, const QList< Tomahawk::album_ptr >& albums ); + + +private: + DataType m_type; + QList m_data; + QStringList m_artists; +}; + +} + +#endif // CHARTDATALOADER_H diff --git a/src/libtomahawk/widgets/whatshotwidget.cpp b/src/libtomahawk/widgets/whatshotwidget.cpp index 3a9f8183c..6eccfc402 100644 --- a/src/libtomahawk/widgets/whatshotwidget.cpp +++ b/src/libtomahawk/widgets/whatshotwidget.cpp @@ -29,6 +29,7 @@ #include "sourcelist.h" #include "tomahawksettings.h" #include "RecentPlaylistsModel.h" +#include "ChartDataLoader.h" #include "audio/audioengine.h" #include "dynamic/GeneratorInterface.h" @@ -52,6 +53,7 @@ WhatsHotWidget::WhatsHotWidget( QWidget* parent ) : QWidget( parent ) , ui( new Ui::WhatsHotWidget ) , m_sortedProxy( 0 ) + , m_workerThread( 0 ) { ui->setupUi( this ); @@ -93,6 +95,9 @@ WhatsHotWidget::WhatsHotWidget( QWidget* parent ) m_playlistInterface = ( new ChartsPlaylistInterface( this ) )->getSharedPointer(); + m_workerThread = new QThread( this ); + m_workerThread->start(); + connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) ); @@ -105,6 +110,8 @@ WhatsHotWidget::WhatsHotWidget( QWidget* parent ) WhatsHotWidget::~WhatsHotWidget() { + m_workerThread->exit(0); + delete m_playlistInterface; delete ui; } @@ -237,68 +244,60 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat const QString chartId = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >().value( "chart_id" ); m_queuedFetches.remove( chartId ); - if( type == "artists" ) + + ChartDataLoader* loader = new ChartDataLoader(); + loader->setProperty( "chartid", chartId ); + loader->moveToThread( m_workerThread ); + + if ( type == "artists" ) { - const QStringList artists = returnedData["artists"].toStringList(); + loader->setType( ChartDataLoader::Artist ); + loader->setData( returnedData[ "artists" ].value< QStringList >() ); + + connect( loader, SIGNAL( artists( Tomahawk::ChartDataLoader*, QList< Tomahawk::artist_ptr > ) ), this, SLOT( chartArtistsLoaded( Tomahawk::ChartDataLoader*, QList< Tomahawk::artist_ptr > ) ) ); TreeModel* artistsModel = new TreeModel( ui->artistsViewLeft ); artistsModel->setColumnStyle( TreeModel::TrackOnly ); - foreach ( const QString& artist, artists ) - { - artist_ptr artistPtr = Artist::get( artist, false ); - artistsModel->addArtists( artistPtr ); - } m_artistModels[ chartId ] = artistsModel; if ( m_queueItemToShow == chartId ) setLeftViewArtists( artistsModel ); } - else if( type == "albums" ) + else if ( type == "albums" ) { - QList al; - const QList< Tomahawk::InfoSystem::InfoStringHash > albums = returnedData[ "albums" ].value< QList< Tomahawk::InfoSystem::InfoStringHash > >(); + + loader->setType( ChartDataLoader::Album ); + loader->setData( returnedData[ "albums" ].value< QList< Tomahawk::InfoSystem::InfoStringHash > >() ); + + connect( loader, SIGNAL( albums( Tomahawk::ChartDataLoader*, QList< Tomahawk::album_ptr > ) ), this, SLOT( chartAlbumsLoaded( Tomahawk::ChartDataLoader*, QList< Tomahawk::album_ptr > ) ) ); AlbumModel* albumModel = new AlbumModel( ui->additionsView ); - foreach ( const Tomahawk::InfoSystem::InfoStringHash& album, albums ) - { - tDebug( LOGVERBOSE) << Q_FUNC_INFO << "Getting album" << album[ "album" ] << "By" << album[ "artist" ]; - artist_ptr artistPtr = Artist::get( album[ "artist" ], false ); - album_ptr albumPtr = Album::get( artistPtr, album[ "album" ], false ); - al << albumPtr; - - } - albumModel->addAlbums( al ); m_albumModels[ chartId ] = albumModel; if ( m_queueItemToShow == chartId ) setLeftViewAlbums( albumModel ); } - else if( type == "tracks" ) + else if ( type == "tracks" ) { - const QList< Tomahawk::InfoSystem::InfoStringHash > tracks = returnedData[ "tracks" ].value< QList< Tomahawk::InfoSystem::InfoStringHash > >(); + + loader->setType( ChartDataLoader::Track ); + loader->setData( returnedData[ "tracks" ].value< QList< Tomahawk::InfoSystem::InfoStringHash > >() ); + + connect( loader, SIGNAL( tracks( Tomahawk::ChartDataLoader*, QList< Tomahawk::query_ptr > ) ), this, SLOT( chartTracksLoaded( Tomahawk::ChartDataLoader*, QList< Tomahawk::query_ptr > ) ) ); PlaylistModel* trackModel = new PlaylistModel( ui->tracksViewLeft ); trackModel->setStyle( TrackModel::Short ); - QList tracklist; - foreach ( const Tomahawk::InfoSystem::InfoStringHash& track, tracks ) - { - query_ptr query = Query::get( track[ "artist" ], track[ "track" ], QString(), uuid(), false ); - tracklist << query; - } - Pipeline::instance()->resolve( tracklist ); - trackModel->append( tracklist ); m_trackModels[ chartId ] = trackModel; if ( m_queueItemToShow == chartId ) setLeftViewTracks( trackModel ); } - else - { - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "WhatsHot: got unknown chart type" << type; - } + + QMetaObject::invokeMethod( loader, "go", Qt::QueuedConnection ); + break; } @@ -471,3 +470,51 @@ WhatsHotWidget::setLeftViewTracks( PlaylistModel* model ) ui->tracksViewLeft->proxyModel()->sort( -1 ); ui->stackLeft->setCurrentIndex( 0 ); } + + +void +WhatsHotWidget::chartArtistsLoaded( ChartDataLoader* loader, const QList< artist_ptr >& artists ) +{ + QString chartId = loader->property( "chartid" ).toString(); + Q_ASSERT( m_artistModels.contains( chartId ) ); + + if ( m_artistModels.contains( chartId ) ) + { + foreach( const artist_ptr& artist, artists ) + { + m_artistModels[ chartId ]->addArtists( artist ); + } + } + + loader->deleteLater(); +} + + +void +WhatsHotWidget::chartTracksLoaded( ChartDataLoader* loader, const QList< query_ptr >& tracks ) +{ + + QString chartId = loader->property( "chartid" ).toString(); + Q_ASSERT( m_trackModels.contains( chartId ) ); + + if ( m_trackModels.contains( chartId ) ) + { + Pipeline::instance()->resolve( tracks ); + m_trackModels[ chartId ]->append( tracks ); + } + + loader->deleteLater(); +} + + +void +WhatsHotWidget::chartAlbumsLoaded( ChartDataLoader* loader, const QList< album_ptr >& albums ) +{ + QString chartId = loader->property( "chartid" ).toString(); + Q_ASSERT( m_albumModels.contains( chartId ) ); + + if ( m_albumModels.contains( chartId ) ) + m_albumModels[ chartId ]->addAlbums( albums ); + + loader->deleteLater(); +} diff --git a/src/libtomahawk/widgets/whatshotwidget.h b/src/libtomahawk/widgets/whatshotwidget.h index 06129b323..7ac5320e2 100644 --- a/src/libtomahawk/widgets/whatshotwidget.h +++ b/src/libtomahawk/widgets/whatshotwidget.h @@ -46,6 +46,11 @@ namespace Ui class WhatsHotWidget; } +namespace Tomahawk +{ + class ChartDataLoader; +} + /** * \class * \brief The tomahawk page that shows music charts. @@ -83,6 +88,11 @@ private slots: void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); void infoSystemFinished( QString target ); void leftCrumbIndexChanged( QModelIndex ); + + void chartArtistsLoaded( Tomahawk::ChartDataLoader*, const QList< Tomahawk::artist_ptr >& ); + void chartAlbumsLoaded( Tomahawk::ChartDataLoader*, const QList< Tomahawk::album_ptr >& ); + void chartTracksLoaded( Tomahawk::ChartDataLoader*, const QList< Tomahawk::query_ptr >& ); + private: void setLeftViewArtists( TreeModel* artistModel ); void setLeftViewAlbums( AlbumModel* albumModel ); @@ -96,6 +106,11 @@ private: QStandardItemModel* m_crumbModelLeft; QSortFilterProxyModel* m_sortedProxy; + // Load artist, album, and track objects in a thread + // {Artist,Album,Track}::get() calls are all synchronous db calls + // and we don't want to lock up out UI in case the db is busy (e.g. on startup) + QThread* m_workerThread; + // Cache our model data QHash< QString, AlbumModel* > m_albumModels; QHash< QString, TreeModel* > m_artistModels; diff --git a/src/mac/tomahawkapp_mac.mm b/src/mac/tomahawkapp_mac.mm index fae3e1d51..d5a0ca295 100644 --- a/src/mac/tomahawkapp_mac.mm +++ b/src/mac/tomahawkapp_mac.mm @@ -175,6 +175,7 @@ NSString *bundleID = [[NSBundle mainBundle] bundleIdentifier]; OSStatus httpResult = LSSetDefaultHandlerForURLScheme((CFStringRef)@"tomahawk", (CFStringRef)bundleID); + Q_UNUSED(httpResult); //TODO: Check httpResult and httpsResult for errors } return self; diff --git a/src/sourcetree/items/groupitem.cpp b/src/sourcetree/items/groupitem.cpp index 57e8b56cd..01804c0ba 100644 --- a/src/sourcetree/items/groupitem.cpp +++ b/src/sourcetree/items/groupitem.cpp @@ -32,7 +32,8 @@ GroupItem::GroupItem( SourcesModel* model, SourceTreeItem* parent, const QString , m_text( text ) , m_peerSortValue( peerSortValue ) { - connect( this, SIGNAL( toggleExpandRequest( SourceTreeItem* ) ), model, SLOT( itemToggleExpandRequest( SourceTreeItem* ) ) ); + // expand by default + QTimer::singleShot( 0, this, SLOT( requestExpanding() ) ); } @@ -48,6 +49,13 @@ GroupItem::activate() } +void +GroupItem::requestExpanding() +{ + emit expandRequest( this ); +} + + QString GroupItem::text() const { diff --git a/src/sourcetree/items/groupitem.h b/src/sourcetree/items/groupitem.h index ae129b773..5f173ac0b 100644 --- a/src/sourcetree/items/groupitem.h +++ b/src/sourcetree/items/groupitem.h @@ -25,7 +25,6 @@ #include "boost/function.hpp" #include "boost/bind.hpp" -// generic item that has some name, some text, and calls a certain slot when activated. badabing! class GroupItem : public SourceTreeItem { Q_OBJECT @@ -35,15 +34,19 @@ public: virtual ~GroupItem(); virtual QString text() const; - virtual void activate(); virtual bool willAcceptDrag( const QMimeData* data ) const { Q_UNUSED( data ); return false; } virtual QIcon icon() const { return QIcon(); } virtual int peerSortValue() const { return m_peerSortValue; } virtual bool isBeingPlayed() const { return false; } +public slots: + virtual void activate(); + signals: void activated(); - void toggleExpandRequest( SourceTreeItem* ); + +private slots: + void requestExpanding(); private: QString m_text; diff --git a/src/sourcetree/items/historyitem.h b/src/sourcetree/items/historyitem.h index d3dd23449..7406d4e84 100644 --- a/src/sourcetree/items/historyitem.h +++ b/src/sourcetree/items/historyitem.h @@ -38,11 +38,11 @@ public: HistoryItem( SourcesModel* model, SourceTreeItem* parent, const QString& text, int peerSortValue = 0 ); virtual ~HistoryItem(); +public slots: virtual void activate(); signals: void activated(); - void toggleExpandRequest( SourceTreeItem* ); private slots: void tempPageActivated( Tomahawk::ViewPage* ); diff --git a/src/sourcetree/items/sourceitem.cpp b/src/sourcetree/items/sourceitem.cpp index 5a78379ff..9eafeab36 100644 --- a/src/sourcetree/items/sourceitem.cpp +++ b/src/sourcetree/items/sourceitem.cpp @@ -158,7 +158,6 @@ SourceItem::activate() p = ViewManager::instance()->showSuperCollection(); else emit toggleExpandRequest( this ); -// p = ViewManager::instance()->show( source()->collection() ); model()->linkSourceItemToPage( this, p ); } diff --git a/src/sourcetree/items/sourceitem.h b/src/sourcetree/items/sourceitem.h index 7827721e3..0b9230a8e 100644 --- a/src/sourcetree/items/sourceitem.h +++ b/src/sourcetree/items/sourceitem.h @@ -37,7 +37,6 @@ public: SourceItem( SourcesModel* model, SourceTreeItem* parent, const Tomahawk::source_ptr& source ); virtual QString text() const; - virtual void activate(); virtual QIcon icon() const; virtual int peerSortValue() const; virtual int IDValue() const; @@ -51,6 +50,9 @@ public: void setStationsCategory( CategoryItem* item ) { m_stations = item; } void setPlaylistsCategory( CategoryItem* item ) { m_playlists = item; } +public slots: + virtual void activate(); + private slots: void onPlaylistsAdded( const QList& playlists ); void onPlaylistDeleted( const Tomahawk::playlist_ptr& playlists ); diff --git a/src/sourcetree/items/sourcetreeitem.h b/src/sourcetree/items/sourcetreeitem.h index 4e17f7e35..7e174b789 100644 --- a/src/sourcetree/items/sourcetreeitem.h +++ b/src/sourcetree/items/sourcetreeitem.h @@ -59,7 +59,6 @@ public: // varies depending on the type of the item virtual QString text() const { return QString(); } virtual Qt::ItemFlags flags() const { return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } - virtual void activate() {} virtual QIcon icon() const { return QIcon(); } virtual bool willAcceptDrag( const QMimeData* ) const { return false; } virtual bool dropMimeData( const QMimeData*, Qt::DropAction ) { return false; } @@ -77,6 +76,9 @@ public: void beginRowsRemoved( int from, int to ) { emit beginChildRowsRemoved( from, to ); } void endRowsRemoved() { emit childRowsRemoved(); } +public slots: + virtual void activate() {} + signals: void updated(); void selectRequest( SourceTreeItem* ); diff --git a/src/sourcetree/sourcesmodel.cpp b/src/sourcetree/sourcesmodel.cpp index 4ddf6fa36..6e81995fc 100644 --- a/src/sourcetree/sourcesmodel.cpp +++ b/src/sourcetree/sourcesmodel.cpp @@ -256,8 +256,8 @@ SourcesModel::appendGroups() { beginInsertRows( QModelIndex(), rowCount(), rowCount() + 2 ); - SourceTreeItem* divider = new SourceTreeItem( this, m_rootItem, SourcesModel::Divider, 0 ); - HistoryItem* history = new HistoryItem( this, m_rootItem, tr( "History" ), 5 ); + new SourceTreeItem( this, m_rootItem, SourcesModel::Divider, 0 ); + new HistoryItem( this, m_rootItem, tr( "History" ), 5 ); GroupItem* browse = new GroupItem( this, m_rootItem, tr( "Browse" ), 10 ); // super collection @@ -611,7 +611,7 @@ SourcesModel::indexFromItem( SourceTreeItem* item ) const int SourcesModel::rowForItem( SourceTreeItem* item ) const { - if( !item || !item->parent() || !item->parent()->children().contains( item ) ) + if ( !item || !item->parent() || !item->parent()->children().contains( item ) ) return -1; return item->parent()->children().indexOf( item ); @@ -628,7 +628,6 @@ SourcesModel::itemSelectRequest( SourceTreeItem* item ) void SourcesModel::itemExpandRequest( SourceTreeItem *item ) { - qDebug() << "expanding source" << indexFromItem( item ) << item; emit expandRequest( QPersistentModelIndex( indexFromItem( item ) ) ); } diff --git a/src/sourcetree/sourcetreeview.cpp b/src/sourcetree/sourcetreeview.cpp index 01ecd8995..abacd799b 100644 --- a/src/sourcetree/sourcetreeview.cpp +++ b/src/sourcetree/sourcetreeview.cpp @@ -84,8 +84,8 @@ SourceTreeView::SourceTreeView( QWidget* parent ) // setAnimated( true ); m_delegate = new SourceDelegate( this ); - connect( m_delegate, SIGNAL( latchOn( Tomahawk::source_ptr ) ), this, SLOT( latchOnOrCatchUp( Tomahawk::source_ptr ) ), Qt::QueuedConnection ); - connect( m_delegate, SIGNAL( latchOff( Tomahawk::source_ptr ) ), this, SLOT( latchOff( Tomahawk::source_ptr ) ), Qt::QueuedConnection ); + connect( m_delegate, SIGNAL( latchOn( Tomahawk::source_ptr ) ), SLOT( latchOnOrCatchUp( Tomahawk::source_ptr ) ), Qt::QueuedConnection ); + connect( m_delegate, SIGNAL( latchOff( Tomahawk::source_ptr ) ), SLOT( latchOff( Tomahawk::source_ptr ) ), Qt::QueuedConnection ); setItemDelegate( m_delegate ); @@ -94,9 +94,9 @@ SourceTreeView::SourceTreeView( QWidget* parent ) m_model = new SourcesModel( this ); m_proxyModel = new SourcesProxyModel( m_model, this ); - connect( m_proxyModel, SIGNAL( selectRequest( QPersistentModelIndex ) ), this, SLOT( selectRequest( QPersistentModelIndex ) ) ); - connect( m_proxyModel, SIGNAL( expandRequest( QPersistentModelIndex ) ), this, SLOT( expandRequest( QPersistentModelIndex ) ) ); - connect( m_proxyModel, SIGNAL( toggleExpandRequest( QPersistentModelIndex ) ), this, SLOT( toggleExpandRequest( QPersistentModelIndex ) ) ); + connect( m_proxyModel, SIGNAL( selectRequest( QPersistentModelIndex ) ), SLOT( selectRequest( QPersistentModelIndex ) ) ); + connect( m_proxyModel, SIGNAL( expandRequest( QPersistentModelIndex ) ), SLOT( expandRequest( QPersistentModelIndex ) ) ); + connect( m_proxyModel, SIGNAL( toggleExpandRequest( QPersistentModelIndex ) ), SLOT( toggleExpandRequest( QPersistentModelIndex ) ) ); setModel( m_proxyModel ); @@ -104,7 +104,7 @@ SourceTreeView::SourceTreeView( QWidget* parent ) header()->setResizeMode( 0, QHeaderView::Stretch ); connect( this, SIGNAL( clicked( QModelIndex ) ), SLOT( onItemActivated( QModelIndex ) ) ); - connect( this, SIGNAL( expanded( QModelIndex ) ), this, SLOT( onItemExpanded( QModelIndex ) ) ); + connect( this, SIGNAL( expanded( QModelIndex ) ), SLOT( onItemExpanded( QModelIndex ) ) ); // connect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), SLOT( onSelectionChanged() ) ); showOfflineSources( TomahawkSettings::instance()->showOfflineSources() ); @@ -228,8 +228,10 @@ void SourceTreeView::onItemExpanded( const QModelIndex& idx ) { // make sure to expand children nodes for collections - if( idx.data( SourcesModel::SourceTreeItemTypeRole ) == SourcesModel::Collection ) { - for( int i = 0; i < model()->rowCount( idx ); i++ ) { + if( idx.data( SourcesModel::SourceTreeItemTypeRole ) == SourcesModel::Collection ) + { + for( int i = 0; i < model()->rowCount( idx ); i++ ) + { setExpanded( model()->index( i, 0, idx ), true ); } }