1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-03-19 15:29:42 +01:00

Merge branch 'master' into playlistinterface_ptr

Conflicts:
	src/libtomahawk/widgets/whatshotwidget.cpp
This commit is contained in:
Jeff Mitchell 2011-12-26 12:24:27 -05:00
commit 073cbbaad5
24 changed files with 673 additions and 60 deletions

View File

@ -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

View File

@ -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<Tomahawk::query_ptr> ) ), 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<Tomahawk::query_ptr> ) ), 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 );
}

View File

@ -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; }

View File

@ -169,7 +169,7 @@ Pipeline::removeScriptResolver( const QString& scriptPath )
if ( r )
{
r->stop();
connect( r, SIGNAL( stopped() ), r, SLOT( deleteLater() ) );
r->deleteLater();
}
}

View File

@ -300,7 +300,7 @@ ScriptResolver::cmdExited( int code, QProcess::ExitStatus status )
if ( m_stopped )
{
tLog() << "*** Script resolver stopped ";
emit stopped();
emit terminated();
return;
}

View File

@ -55,7 +55,7 @@ public:
virtual bool running() const;
signals:
void stopped();
void terminated();
public slots:
virtual void stop();

View File

@ -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));
}

View File

@ -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"

View File

@ -0,0 +1,258 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
* Copyright 2010-2011, Hugo Lindström <hugolm84@gmail.com>
* Copyright 2010-2011, Stefan Derkits <stefan@derkits.at>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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 <qjson/parser.h>
#include <QtCrypto>
#include <QCoreApplication>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
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; i<length; i++ ) magic[i] = magic[i] ^ wand[i%n2];
m_apiKey = QCA::SymmetricKey( magic );
foreach ( const QString& url, trackUrls )
lookupUrl( url );
}
GroovesharkParser::~GroovesharkParser()
{
}
void
GroovesharkParser::lookupUrl( const QString& link )
{
if( link.contains( "playlist" ) )
{
if( !m_createNewPlaylist )
m_trackMode = true;
else
m_trackMode = false;
lookupGroovesharkPlaylist( link );
}
else
return;
}
void
GroovesharkParser::lookupGroovesharkPlaylist( const QString& linkRaw )
{
tLog() << "Parsing Grooveshark Playlist URI:" << linkRaw;
QStringList urlParts = linkRaw.split( "/" );
bool ok;
QString playlistStr = urlParts.last();
playlistStr.truncate(playlistStr.indexOf("?"));
int playlistID = playlistStr.toInt( &ok, 10 );
if (!ok)
{
tDebug() << "incorrect grooveshark url";
return;
}
m_title = urlParts.at( urlParts.size()-2 );
tDebug() << "should get playlist " << playlistID;
DropJob::DropType type;
if ( linkRaw.contains( "playlist" ) )
type = DropJob::Playlist;
QString base_url( "http://api.grooveshark.com/ws3.php?sig=" );
QByteArray data = QString( "{\"method\":\"getPlaylistSongs\",\"parameters\":{\"playlistID\":\"%1\"},\"header\":{\"wsKey\":\"tomahawkplayer\"}}" ).arg( playlistID ).toLocal8Bit();
QCA::MessageAuthenticationCode hmac( "hmac(md5)", m_apiKey );
QCA::SecureArray secdata( data );
hmac.update(secdata);
QCA::SecureArray resultArray = hmac.final();
QString hash = QCA::arrayToHex( resultArray.toByteArray() );
QUrl url = QUrl( base_url + hash );
tDebug() << "Looking up URL..." << url.toString();
QNetworkReply* reply = TomahawkUtils::nam()->post( 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;
}

View File

@ -0,0 +1,87 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
* Copyright 2010-2011, Hugo Lindström <hugolm84@gmail.com>
* Copyright 2011, Stefan Derkits <stefan@derkits.at>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef GROOVESHARKPARSER_H
#define GROOVESHARKPARSER_H
#include "dllmacro.h"
#include "typedefs.h"
#include "query.h"
#include "jobview/JobStatusItem.h"
#include <qca_basic.h>
#include <QObject>
#include <QSet>
#include <QtCore/QStringList>
/**
* 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

View File

@ -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" ) );
}

View File

@ -0,0 +1,72 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2011, Leo Franchi <lfranchi@kde.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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;
}
}
}

View File

@ -0,0 +1,69 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2011, Leo Franchi <lfranchi@kde.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef CHARTDATALOADER_H
#define CHARTDATALOADER_H
#include "infosystem/infosystem.h"
#include "query.h"
#include "artist.h"
#include "album.h"
#include <QObject>
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<InfoSystem::InfoStringHash> m_data;
QStringList m_artists;
};
}
#endif // CHARTDATALOADER_H

View File

@ -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<album_ptr> 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<query_ptr> 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();
}

View File

@ -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;

View File

@ -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;

View File

@ -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
{

View File

@ -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;

View File

@ -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* );

View File

@ -158,7 +158,6 @@ SourceItem::activate()
p = ViewManager::instance()->showSuperCollection();
else
emit toggleExpandRequest( this );
// p = ViewManager::instance()->show( source()->collection() );
model()->linkSourceItemToPage( this, p );
}

View File

@ -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<Tomahawk::playlist_ptr>& playlists );
void onPlaylistDeleted( const Tomahawk::playlist_ptr& playlists );

View File

@ -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* );

View File

@ -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 ) ) );
}

View File

@ -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 );
}
}