From ad739d0ce8f15fc2551cb58778cf2bbf722db575 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sat, 22 Oct 2011 23:45:21 -0400 Subject: [PATCH 001/109] Set two default items for the charts try listening to any changed and set default source don't listen to indexchanged, do a manual set when setting default --- .../infoplugins/generic/chartsplugin.cpp | 28 ++++++++++++ .../widgets/siblingcrumbbutton.cpp | 19 ++++++-- src/libtomahawk/widgets/whatshotwidget.cpp | 43 +++++++++++++++++-- src/libtomahawk/widgets/whatshotwidget.h | 4 ++ 4 files changed, 88 insertions(+), 6 deletions(-) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp index 50bd25124..75a7a8f66 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp @@ -279,6 +279,7 @@ ChartsPlugin::chartTypes() // We'll populate charts with the data from the server QVariantMap charts; QString chartName; + QStringList defaultChain; if ( source == "itunes" ) { // Itunes has geographic-area based charts. So we build a breadcrumb of @@ -292,6 +293,7 @@ ChartsPlugin::chartTypes() const QString geo = chart.value( "geo" ).toString(); QString name = chart.value( "name" ).toString(); const QString type = chart.value( "type" ).toString(); + const bool isDefault = ( chart.contains( "default" ) && chart[ "default" ].toInt() == 1 ); QString country; if ( !m_cachedCountries.contains( geo ) ) @@ -321,10 +323,20 @@ ChartsPlugin::chartTypes() c[ "id" ] = id; c[ "label" ] = name; c[ "type" ] = "album"; + if ( isDefault ) + c[ "default" ] = "true"; + QList countryTypeData = countries[ country ][ type ].value< QList< InfoStringHash > >(); countryTypeData.append( c ); countries[ country ].insert( type, QVariant::fromValue< QList< InfoStringHash > >( countryTypeData ) ); + if ( isDefault ) + { + defaultChain.clear(); + defaultChain.append( country ); + defaultChain.append( type ); + defaultChain.append( name ); + } } foreach( const QString& c, countries.keys() ) @@ -344,9 +356,14 @@ ChartsPlugin::chartTypes() { const QVariantMap chart = chartObj.toMap(); const QString type = chart.value( "type" ).toString(); + const bool isDefault = ( chart.contains( "default" ) && chart[ "default" ].toInt() == 1 ); + InfoStringHash c; c[ "id" ] = chart.value( "id" ).toString(); c[ "label" ] = chart.value( "name" ).toString(); + if ( isDefault ) + c[ "default" ] = "true"; + if ( type == "Album" ) { c[ "type" ] = "album"; @@ -357,6 +374,13 @@ ChartsPlugin::chartTypes() c[ "type" ] = "tracks"; trackCharts.append( c ); } + + if ( isDefault ) + { + defaultChain.clear(); + defaultChain.append( type + "s" ); //UGLY but it's plural to the user, see below + defaultChain.append( c[ "label" ] ); + } } charts.insert( tr( "Albums" ), QVariant::fromValue< QList< InfoStringHash > >( albumCharts ) ); charts.insert( tr( "Tracks" ), QVariant::fromValue< QList< InfoStringHash > >( trackCharts ) ); @@ -369,6 +393,10 @@ ChartsPlugin::chartTypes() /// Add the possible charts and its types to breadcrumb // qDebug() << "ADDING CHART TYPE TO CHARTS:" << chartName; + QVariantMap defaultMap = m_allChartsMap.value( "defaults" ).value< QVariantMap >(); + defaultMap[ source ] = defaultChain; + m_allChartsMap[ "defaults" ] = defaultMap; + m_allChartsMap[ "defaultSource" ] = "itunes"; m_allChartsMap.insert( chartName , QVariant::fromValue< QVariantMap >( charts ) ); } diff --git a/src/libtomahawk/widgets/siblingcrumbbutton.cpp b/src/libtomahawk/widgets/siblingcrumbbutton.cpp index f11704aac..e6f4f6e49 100644 --- a/src/libtomahawk/widgets/siblingcrumbbutton.cpp +++ b/src/libtomahawk/widgets/siblingcrumbbutton.cpp @@ -27,6 +27,7 @@ #include #include #include +#include "whatshotwidget.h" BreadcrumbButtonBase* SiblingCrumbButtonFactory::newButton(QModelIndex index, BreadcrumbBar *parent) { @@ -129,10 +130,16 @@ void SiblingCrumbButton::fillCombo() { QStringList list; int count = breadcrumbBar()->model()->rowCount(m_index.parent()); - for(int i = 0; i < count; ++i) { + int defaultIndex = -1; + for ( int i = 0; i < count; ++i ) + { QModelIndex sibling = m_index.sibling(i,0); - if( sibling.isValid() ) + if ( sibling.isValid() ) + { list << sibling.data().toString(); + if ( sibling.data( WhatsHotWidget::DefaultRole ).toBool() ) + defaultIndex = i; + } } if ( m_combo->count() && list.count() ) @@ -148,7 +155,13 @@ void SiblingCrumbButton::fillCombo() m_combo->clear(); m_combo->addItems(list); - m_combo->setCurrentIndex( m_combo->findText(text())); + if ( defaultIndex == -1 ) + m_combo->setCurrentIndex( m_combo->findText(text())); + else + { + m_combo->setCurrentIndex( defaultIndex ); + comboboxActivated( defaultIndex ); + } m_combo->adjustSize(); } diff --git a/src/libtomahawk/widgets/whatshotwidget.cpp b/src/libtomahawk/widgets/whatshotwidget.cpp index 89a113fa1..e1a7a0a83 100644 --- a/src/libtomahawk/widgets/whatshotwidget.cpp +++ b/src/libtomahawk/widgets/whatshotwidget.cpp @@ -171,7 +171,12 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat QStandardItem *rootItem= m_crumbModelLeft->invisibleRootItem(); tDebug( LOGVERBOSE ) << "WhatsHot:: " << returnedData.keys(); - foreach( const QString label, returnedData.keys() ) + QVariantMap defaults; + if ( returnedData.contains( "defaults" ) ) + defaults = returnedData.take( "defaults" ).toMap(); + QString defaultSource = returnedData.take( "defaultSource" ).toString(); + + foreach ( const QString label, returnedData.keys() ) { tDebug( LOGVERBOSE ) << "WhatsHot:: parsing " << label; QStandardItem *childItem = parseNode( rootItem, label, returnedData[label] ); @@ -179,9 +184,40 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat rootItem->appendRow(childItem); } + // Set the default source + // Set the default chart for each source + for ( int i = 0; i < rootItem->rowCount(); i++ ) + { + QStandardItem* source = rootItem->child( i, 0 ); + if ( defaultSource.toLower() == source->text().toLower() ) + { + source->setData( true, DefaultRole ); + } + + if ( defaults.contains( source->text().toLower() ) ) + { + QStringList defaultIndices = defaults[ source->text().toLower() ].toStringList(); + QStandardItem* cur = source; + + foreach( const QString& index, defaultIndices ) + { + // Go through the children of the current item, marking the default one as default + for ( int k = 0; k < cur->rowCount(); k++ ) + { + if ( cur->child( k, 0 )->text() == index ) + { +// tDebug() << "Found DEFAULT ITEM:" << index; + cur = cur->child( k, 0 ); // this is the default, drill down into the default to pick the next default + cur->setData( true, DefaultRole ); + break; + } + } + } + } + } + KBreadcrumbSelectionModel *selectionModelLeft = new KBreadcrumbSelectionModel(new QItemSelectionModel(m_crumbModelLeft, this), this); ui->breadCrumbLeft->setSelectionModel(selectionModelLeft); - //ui->breadCrumbRight->setSelectionModel(selectionModelLeft); break; } @@ -368,6 +404,8 @@ WhatsHotWidget::parseNode( QStandardItem* parentItem, const QString &label, cons { QStandardItem *childItem= new QStandardItem( chart[ "label" ] ); childItem->setData( chart[ "id" ] ); + if ( chart.value( "default", "" ) == "true") + sourceItem->setData( WhatsHotWidget::DefaultRole, true ); sourceItem->appendRow( childItem ); } } @@ -386,7 +424,6 @@ WhatsHotWidget::parseNode( QStandardItem* parentItem, const QString &label, cons foreach ( const QVariant value, dataList ) { - qDebug() << "CREATED:" << value.toString(); QStandardItem *childItem= new QStandardItem(value.toString()); sourceItem->appendRow(childItem); } diff --git a/src/libtomahawk/widgets/whatshotwidget.h b/src/libtomahawk/widgets/whatshotwidget.h index e13c0421e..332014d31 100644 --- a/src/libtomahawk/widgets/whatshotwidget.h +++ b/src/libtomahawk/widgets/whatshotwidget.h @@ -53,6 +53,10 @@ class DLLEXPORT WhatsHotWidget : public QWidget, public Tomahawk::ViewPage Q_OBJECT public: + enum ExtraRoles { + DefaultRole = Qt::UserRole + 10 + }; + WhatsHotWidget( QWidget* parent = 0 ); ~WhatsHotWidget(); From 660a7cd0591dbd3baca4b1c58a12977feb57c268 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 23 Oct 2011 15:38:12 -0400 Subject: [PATCH 002/109] Add rovi infoplugin for album tracks --- src/libtomahawk/CMakeLists.txt | 2 + .../infoplugins/generic/RoviPlugin.cpp | 193 ++++++++++++++++++ .../infoplugins/generic/RoviPlugin.h | 67 ++++++ .../infosystem/infosystemworker.cpp | 7 +- src/libtomahawk/playlist/treemodel.cpp | 7 +- src/libtomahawk/playlist/treemodel.h | 1 + 6 files changed, 274 insertions(+), 3 deletions(-) create mode 100644 src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp create mode 100644 src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.h diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 209c721a7..9ab439ce9 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -113,6 +113,7 @@ set( libSources infosystem/infoplugins/generic/chartsplugin.cpp infosystem/infoplugins/generic/musixmatchplugin.cpp infosystem/infoplugins/generic/musicbrainzPlugin.cpp + infosystem/infoplugins/generic/RoviPlugin.cpp playlist/treemodel.cpp playlist/treeproxymodel.cpp @@ -342,6 +343,7 @@ set( libHeaders infosystem/infoplugins/generic/chartsplugin.h infosystem/infoplugins/generic/musixmatchplugin.h infosystem/infoplugins/generic/musicbrainzPlugin.h + infosystem/infoplugins/generic/RoviPlugin.h network/bufferiodevice.h network/msgprocessor.h diff --git a/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp new file mode 100644 index 000000000..d4ce5950a --- /dev/null +++ b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp @@ -0,0 +1,193 @@ +/* === 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 "RoviPlugin.h" + +#include "utils/logger.h" + +#include +#include +#include + +using namespace Tomahawk::InfoSystem; + +static QString +md5( const QByteArray& src ) +{ + QByteArray const digest = QCryptographicHash::hash( src, QCryptographicHash::Md5 ); + return QString::fromLatin1( digest.toHex() ).rightJustified( 32, '0' ); +} + +RoviPlugin::RoviPlugin() + : InfoPlugin() +{ + m_supportedGetTypes << InfoAlbumSongs; + + /* + * Your API Key is 7jxr9zggt45h6rg2n4ss3mrj + * Your secret is XUnYutaAW6 + */ + m_apiKey = "7jxr9zggt45h6rg2n4ss3mrj"; + m_secret = "XUnYutaAW6"; +} + +RoviPlugin::~RoviPlugin() +{ + +} + + + +void +RoviPlugin::namChangedSlot( QNetworkAccessManager* nam ) +{ + if ( !nam ) + return; + + m_nam = nam; +} + + +void +RoviPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) + { + emit info( requestId, requestData, QVariant() ); + return; + } + InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); + if ( !hash.contains( "artist" ) || !hash.contains( "album" ) ) + { + emit info( requestId, requestData, QVariant() ); + return; + } + + Tomahawk::InfoSystem::InfoStringHash criteria; + criteria["album"] = hash["album"]; + + emit getCachedInfo( requestId, criteria, 2419200000, requestData ); +} + +void +RoviPlugin::notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + switch ( requestData.type ) + { + case InfoAlbumSongs: + { + QUrl baseUrl = QUrl( "http://api.rovicorp.com/data/v1/album/tracks" ); + baseUrl.addQueryItem( "album", criteria[ "album" ] ); + + QNetworkReply* reply = makeRequest( baseUrl ); + + reply->setProperty( "requestId", requestId ); + reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); + connect( reply, SIGNAL( finished() ), this, SLOT( albumLookupFinished() ) ); + connect( reply, SIGNAL( error( QNetworkReply::NetworkError ) ), this, SLOT( albumLookupError( QNetworkReply::NetworkError ) ) ); + break; + } + default: + { + Q_ASSERT( false ); + break; + } + } +} + +void +RoviPlugin::albumLookupError( QNetworkReply::NetworkError error ) +{ + if ( error == QNetworkReply::NoError ) + return; + + QNetworkReply* reply = qobject_cast( sender() ); + Q_ASSERT( reply ); + + Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); + int requestId = reply->property( "requestId" ).toUInt(); + + emit info( requestId, requestData, QVariant() ); + +} + +void +RoviPlugin::albumLookupFinished() +{ + QNetworkReply* reply = qobject_cast( sender() ); + Q_ASSERT( reply ); + + if ( reply->error() != QNetworkReply::NoError ) + return; + + Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); + int requestId = reply->property( "requestId" ).toUInt(); + + QByteArray data = reply->readAll(); + qDebug() << "data:" << data; + QJson::Parser p; + bool ok; + QVariantMap result = p.parse( data, &ok ).toMap(); + + if ( !ok || result.isEmpty() || !result.contains( "tracks" ) ) + { + tLog() << "Error parsing JSON from Rovi!" << p.errorString() << result; + emit info( requestId, requestData, QVariant() ); + } + + tDebug() << "Got Rovi results:" << result[ "tracks" ]; + QVariantList tracks = result[ "tracks" ].toList(); + QStringList trackNameList; + foreach ( const QVariant& track, tracks ) + { + const QVariantMap trackData = track.toMap(); + if ( trackData.contains( "title" ) ) + trackNameList << trackData[ "title" ].toString(); + } + tDebug() << "FOUND TRACK LIST FROM ROVI:" << trackNameList; + + QVariantMap returnedData; + returnedData["tracks"] = trackNameList; + + emit info( requestId, requestData, returnedData ); + + Tomahawk::InfoSystem::InfoStringHash criteria; + criteria["artist"] = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>()["artist"]; + + emit updateCache( criteria, 0, requestData.type, returnedData ); +} + + +QNetworkReply* +RoviPlugin::makeRequest( QUrl url ) +{ + url.addQueryItem( "apikey", m_apiKey ); + url.addEncodedQueryItem( "sig", generateSig() ); + + qDebug() << "url:" << url.toString(); + return m_nam->get( QNetworkRequest( url ) ); +} + + +QByteArray +RoviPlugin::generateSig() const +{ + QByteArray raw = m_apiKey + m_secret + QString::number( QDateTime::currentMSecsSinceEpoch() / 1000 ).toLatin1(); + return md5( raw ).toLatin1(); + +} diff --git a/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.h b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.h new file mode 100644 index 000000000..0bc2e9e8c --- /dev/null +++ b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.h @@ -0,0 +1,67 @@ +/* === 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 ROVIPLUGIN_H +#define ROVIPLUGIN_H + +#include "infosystem/infosystem.h" + +#include + +class QNetworkAccessManager; + +namespace Tomahawk +{ + +namespace InfoSystem +{ + +class RoviPlugin : public InfoPlugin +{ + Q_OBJECT +public: + RoviPlugin(); + virtual ~RoviPlugin(); + +protected: + virtual void namChangedSlot( QNetworkAccessManager* nam ); + virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); + + virtual void pushInfo( QString, Tomahawk::InfoSystem::InfoType, QVariant ) + {} + + virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + +private slots: + void albumLookupFinished(); + void albumLookupError( QNetworkReply::NetworkError ); +private: + QNetworkReply* makeRequest( QUrl url ); + QByteArray generateSig() const; + + QNetworkAccessManager* m_nam; + + QByteArray m_apiKey; + QByteArray m_secret; +}; + +} + +} + +#endif // ROVIPLUGIN_H diff --git a/src/libtomahawk/infosystem/infosystemworker.cpp b/src/libtomahawk/infosystem/infosystemworker.cpp index 9ae11951c..965a21581 100644 --- a/src/libtomahawk/infosystem/infosystemworker.cpp +++ b/src/libtomahawk/infosystem/infosystemworker.cpp @@ -40,6 +40,7 @@ #endif #include "lastfm/NetworkAccessManager" +#include "infoplugins/generic/RoviPlugin.h" namespace Tomahawk { @@ -92,7 +93,9 @@ InfoSystemWorker::init( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache> cac InfoPluginPtr sptr( new ChartsPlugin() ); m_plugins.append( sptr ); registerInfoTypes( sptr, sptr.data()->supportedGetTypes(), sptr.data()->supportedPushTypes() ); - + InfoPluginPtr roviptr( new RoviPlugin() ); + m_plugins.append( roviptr ); + registerInfoTypes( roviptr, roviptr.data()->supportedGetTypes(), roviptr.data()->supportedPushTypes() ); #ifdef Q_WS_MAC InfoPluginPtr admptr( new AdiumPlugin() ); @@ -105,7 +108,7 @@ InfoSystemWorker::init( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache> cac registerInfoTypes( fdonotifyptr, fdonotifyptr.data()->supportedGetTypes(), fdonotifyptr.data()->supportedPushTypes() ); InfoPluginPtr mprisptr( new MprisPlugin() ); m_plugins.append( mprisptr ); - registerInfoTypes( mprisptr, mprisptr.data()->supportedGetTypes(), mprisptr.data()->supportedPushTypes() ); + registerInfoTypes( mprisptr, mprisptr.data()->supportedGetTypes(), mprisptr.data()->supportedPushTypes() ); #endif Q_FOREACH( InfoPluginPtr plugin, m_plugins ) diff --git a/src/libtomahawk/playlist/treemodel.cpp b/src/libtomahawk/playlist/treemodel.cpp index 8dd336ea5..14cc9e06f 100644 --- a/src/libtomahawk/playlist/treemodel.cpp +++ b/src/libtomahawk/playlist/treemodel.cpp @@ -629,12 +629,13 @@ TreeModel::addTracks( const album_ptr& album, const QModelIndex& parent ) artistInfo["artist"] = album->artist()->name(); artistInfo["album"] = album->name(); + m_receivedInfoData.remove( artistInfo ); Tomahawk::InfoSystem::InfoRequestData requestData; requestData.caller = m_infoId; requestData.customData["rows"] = QVariant( rows ); requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo ); requestData.type = Tomahawk::InfoSystem::InfoAlbumSongs; - Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); + Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData, 0, true ); } else Q_ASSERT( false ); @@ -845,6 +846,10 @@ TreeModel::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QV case Tomahawk::InfoSystem::InfoAlbumSongs: { + if ( m_receivedInfoData.contains( requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >() ) ) + break; + m_receivedInfoData.insert( requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >() ); + QVariantMap returnedData = output.value< QVariantMap >(); QStringList tracks = returnedData[ "tracks" ].toStringList(); QList ql; diff --git a/src/libtomahawk/playlist/treemodel.h b/src/libtomahawk/playlist/treemodel.h index af4aa6bf0..0c22511cc 100644 --- a/src/libtomahawk/playlist/treemodel.h +++ b/src/libtomahawk/playlist/treemodel.h @@ -171,6 +171,7 @@ private: Tomahawk::collection_ptr m_collection; QHash m_coverHash; + QSet m_receivedInfoData; }; #endif // ALBUMMODEL_H From 49d40186be09ecf885c10fdfa8fbd3d420e5e7a4 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 23 Oct 2011 15:40:12 -0400 Subject: [PATCH 003/109] debug-- --- .../infosystem/infoplugins/generic/RoviPlugin.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp index d4ce5950a..a031d7a37 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp @@ -138,11 +138,9 @@ RoviPlugin::albumLookupFinished() Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); int requestId = reply->property( "requestId" ).toUInt(); - QByteArray data = reply->readAll(); - qDebug() << "data:" << data; QJson::Parser p; bool ok; - QVariantMap result = p.parse( data, &ok ).toMap(); + QVariantMap result = p.parse( reply, &ok ).toMap(); if ( !ok || result.isEmpty() || !result.contains( "tracks" ) ) { @@ -150,7 +148,6 @@ RoviPlugin::albumLookupFinished() emit info( requestId, requestData, QVariant() ); } - tDebug() << "Got Rovi results:" << result[ "tracks" ]; QVariantList tracks = result[ "tracks" ].toList(); QStringList trackNameList; foreach ( const QVariant& track, tracks ) @@ -159,7 +156,6 @@ RoviPlugin::albumLookupFinished() if ( trackData.contains( "title" ) ) trackNameList << trackData[ "title" ].toString(); } - tDebug() << "FOUND TRACK LIST FROM ROVI:" << trackNameList; QVariantMap returnedData; returnedData["tracks"] = trackNameList; @@ -179,7 +175,7 @@ RoviPlugin::makeRequest( QUrl url ) url.addQueryItem( "apikey", m_apiKey ); url.addEncodedQueryItem( "sig", generateSig() ); - qDebug() << "url:" << url.toString(); +// qDebug() << "url:" << url.toString(); return m_nam->get( QNetworkRequest( url ) ); } From 5263b21138cb3a6c63116ae7e6250abe99cfcd47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Mon, 24 Oct 2011 12:45:54 +0200 Subject: [PATCH 004/109] Spotify InfoSystemPlugin --- src/libtomahawk/CMakeLists.txt | 2 + .../infoplugins/generic/spotifyPlugin.cpp | 395 ++++++++++++++++++ .../infoplugins/generic/spotifyPlugin.h | 80 ++++ .../infosystem/infosystemworker.cpp | 4 + 4 files changed, 481 insertions(+) create mode 100644 src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp create mode 100644 src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.h diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 9ab439ce9..fc153c608 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -111,6 +111,7 @@ set( libSources infosystem/infoplugins/generic/echonestplugin.cpp infosystem/infoplugins/generic/lastfmplugin.cpp infosystem/infoplugins/generic/chartsplugin.cpp + infosystem/infoplugins/generic/spotifyPlugin.cpp infosystem/infoplugins/generic/musixmatchplugin.cpp infosystem/infoplugins/generic/musicbrainzPlugin.cpp infosystem/infoplugins/generic/RoviPlugin.cpp @@ -341,6 +342,7 @@ set( libHeaders infosystem/infoplugins/generic/echonestplugin.h infosystem/infoplugins/generic/lastfmplugin.h infosystem/infoplugins/generic/chartsplugin.h + infosystem/infoplugins/generic/spotifyPlugin.h infosystem/infoplugins/generic/musixmatchplugin.h infosystem/infoplugins/generic/musicbrainzPlugin.h infosystem/infoplugins/generic/RoviPlugin.h diff --git a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp new file mode 100644 index 000000000..f6c77eef0 --- /dev/null +++ b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp @@ -0,0 +1,395 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Hugo Lindström + * 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 "spotifyPlugin.h" + +#include +#include +#include +#include +#include +#include + +#include "album.h" +#include "typedefs.h" +#include "audio/audioengine.h" +#include "tomahawksettings.h" +#include "utils/tomahawkutils.h" +#include "utils/logger.h" +#include "chartsplugin_data_p.h" + +#define CHART_URL "http://localhost:5112/" +#include +#include + +using namespace Tomahawk::InfoSystem; + + +SpotifyPlugin::SpotifyPlugin() + : InfoPlugin() +{ + + m_supportedGetTypes << InfoChart << InfoChartCapabilities; + +} + + +SpotifyPlugin::~SpotifyPlugin() +{ + qDebug() << Q_FUNC_INFO; +} + + +void +SpotifyPlugin::namChangedSlot( QNetworkAccessManager *nam ) +{ + tDebug() << "SpotifyPlugin: namChangedSLot"; + qDebug() << Q_FUNC_INFO; + if( !nam ) + return; + + m_nam = QWeakPointer< QNetworkAccessManager >( nam ); + + /// We need to fetch possible types before they are asked for + tDebug() << "SpotifyPlugin: InfoChart fetching possible resources"; + + QUrl url = QUrl( QString( CHART_URL "toplist/charts" ) ); + QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) ); + tDebug() << Q_FUNC_INFO << "fetching:" << url; + connect( reply, SIGNAL( finished() ), SLOT( chartTypes() ) ); + +} + + +void +SpotifyPlugin::dataError( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + emit info( requestId, requestData, QVariant() ); + return; +} + + +void +SpotifyPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + qDebug() << Q_FUNC_INFO << requestData.caller; + qDebug() << Q_FUNC_INFO << requestData.customData; + + InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); + + + switch ( requestData.type ) + { + case InfoChart: + if ( !hash.contains( "chart_source" ) || hash["chart_source"] != "spotify" ) + { + dataError( requestId, requestData ); + break; + } + qDebug() << Q_FUNC_INFO << "InfoCHart req for" << hash["chart_source"]; + fetchChart( requestId, requestData ); + break; + + case InfoChartCapabilities: + fetchChartCapabilities( requestId, requestData ); + break; + default: + dataError( requestId, requestData ); + } +} + + +void +SpotifyPlugin::pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input ) +{ + Q_UNUSED( caller ) + Q_UNUSED( type) + Q_UNUSED( input ) +} + +void +SpotifyPlugin::fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) + { + dataError( requestId, requestData ); + return; + } + InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); + Tomahawk::InfoSystem::InfoStringHash criteria; + if ( !hash.contains( "chart_id" ) ) + { + dataError( requestId, requestData ); + return; + } else { + criteria["chart_id"] = hash["chart_id"]; + } + + emit getCachedInfo( requestId, criteria, 0, requestData ); +} +void +SpotifyPlugin::fetchChartCapabilities( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) + { + dataError( requestId, requestData ); + return; + } + + Tomahawk::InfoSystem::InfoStringHash criteria; + emit getCachedInfo( requestId, criteria, 0, requestData ); +} + +void +SpotifyPlugin::notInCacheSlot( uint requestId, QHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + if ( !m_nam.data() ) + { + tLog() << Q_FUNC_INFO << "Have a null QNAM, uh oh"; + emit info( requestId, requestData, QVariant() ); + return; + } + + + switch ( requestData.type ) + { + + case InfoChart: + { + /// Fetch the chart, we need source and id + QUrl url = QUrl( QString( CHART_URL "toplist/%1/" ).arg( criteria["chart_id"] ) ); + qDebug() << Q_FUNC_INFO << "Getting chart url" << url; + + QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) ); + reply->setProperty( "requestId", requestId ); + reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); + connect( reply, SIGNAL( finished() ), SLOT( chartReturned() ) ); + return; + + + } + case InfoChartCapabilities: + { + qDebug() << Q_FUNC_INFO << "EMITTING CHART" << m_allChartsMap; + emit info( + requestId, + requestData, + m_allChartsMap + ); + return; + } + + default: + { + tLog() << Q_FUNC_INFO << "Couldn't figure out what to do with this type of request after cache miss"; + emit info( requestId, requestData, QVariant() ); + return; + } + } +} + + +void +SpotifyPlugin::chartTypes() +{ + /// Get possible chart type for specificSpotifyPlugin: InfoChart types returned chart source + tDebug() << Q_FUNC_INFO << "Got spotifychart type result"; + QNetworkReply* reply = qobject_cast( sender() ); + + if ( reply->error() == QNetworkReply::NoError ) + { + QJson::Parser p; + bool ok; + const QVariantMap res = p.parse( reply, &ok ).toMap(); + const QVariantMap chartObj = res; + + if ( !ok ) + { + tLog() << Q_FUNC_INFO << "Failed to parse resources" << p.errorString() << "On line" << p.errorLine(); + + return; + } + + QVariantMap charts; + foreach(QVariant geos, chartObj.value("Charts").toList().takeLast().toMap().value("geo").toList() ) + { + + const QString geo = geos.toMap().value( "name" ).toString(); + const QString geoId = geos.toMap().value( "id" ).toString(); + QString country; + + if( geo == "For me" ) + continue; /// country = geo; Lets use this later, when we can get the spotify username from tomahawk + else if( geo == "Everywhere" ) + country = geo; + else + { + + QLocale l( QString( "en_%1" ).arg( geo ) ); + country = Tomahawk::CountryUtils::fullCountryFromCode( geo ); + + for ( int i = 1; i < country.size(); i++ ) + { + if ( country.at( i ).isUpper() ) + { + country.insert( i, " " ); + i++; + } + } + } + + QList< InfoStringHash > chart_types; + foreach(QVariant types, chartObj.value("Charts").toList().takeFirst().toMap().value("types").toList() ) + { + QString type = types.toMap().value( "id" ).toString(); + QString label = types.toMap().value( "name" ).toString(); + + InfoStringHash c; + c[ "id" ] = type + "/" + geoId; + c[ "label" ] = label; + c[ "type" ] = type; + + chart_types.append( c ); + + } + + charts.insert( country.toUtf8(), QVariant::fromValue >( chart_types ) ); + + } + + m_allChartsMap.insert( "Spotify", QVariant::fromValue( charts ) ); + + } + else + { + tLog() << Q_FUNC_INFO << "Error fetching charts:" << reply->errorString(); + } + +} + +void +SpotifyPlugin::chartReturned() +{ + + /// Chart request returned something! Woho + QNetworkReply* reply = qobject_cast( sender() ); + QString url = reply->url().toString(); + QVariantMap returnedData; + if ( reply->error() == QNetworkReply::NoError ) + { + QJson::Parser p; + bool ok; + QVariantMap res = p.parse( reply, &ok ).toMap(); + + if ( !ok ) + { + tLog() << "Failed to parse json from chart lookup:" << p.errorString() << "On line" << p.errorLine(); + return; + } + + /// SO we have a result, parse it! + QList< InfoStringHash > top_tracks; + QList< InfoStringHash > top_albums; + QStringList top_artists; + + if( url.contains( "albums" ) ) + setChartType( Album ); + else if( url.contains( "tracks" ) ) + setChartType( Track ); + else if( url.contains( "artists" ) ) + setChartType( Artist ); + else + setChartType( None ); + + foreach(QVariant result, res.value("toplist").toMap().value("result").toList() ) + { + QString title, artist; + QVariantMap chartMap = result.toMap(); + + if ( !chartMap.isEmpty() ) + { + + title = chartMap.value( "title" ).toString(); + artist = chartMap.value( "artist" ).toString(); + + if( chartType() == Track ) + { + InfoStringHash pair; + pair["artist"] = artist; + pair["track"] = title; + top_tracks << pair; + + qDebug() << "SpotifyChart type is track"; + } + + if( chartType() == Album ) + { + + InfoStringHash pair; + pair["artist"] = artist; + pair["album"] = title; + top_albums << pair; + qDebug() << "SpotifyChart type is album"; + } + + if( chartType() == Artist ) + { + + top_artists << chartMap.value( "name" ).toString(); + qDebug() << "SpotifyChart type is artist"; + + } + } + } + + if( chartType() == Track ) + { + tDebug() << "ChartsPlugin:" << "\tgot " << top_tracks.size() << " tracks"; + returnedData["tracks"] = QVariant::fromValue( top_tracks ); + returnedData["type"] = "tracks"; + } + + if( chartType() == Album ) + { + tDebug() << "ChartsPlugin:" << "\tgot " << top_albums.size() << " albums"; + returnedData["albums"] = QVariant::fromValue( top_albums ); + returnedData["type"] = "albums"; + } + + if( chartType() == Artist ) + { + tDebug() << "ChartsPlugin:" << "\tgot " << top_artists.size() << " artists"; + returnedData["artists"] = top_artists; + returnedData["type"] = "artists"; + } + + Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); + + + emit info( + reply->property( "requestId" ).toUInt(), + requestData, + returnedData + ); + + } + else + qDebug() << "Network error in fetching chart:" << reply->url().toString(); + +} diff --git a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.h b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.h new file mode 100644 index 000000000..b27e0cd26 --- /dev/null +++ b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.h @@ -0,0 +1,80 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Hugo Lindström + * + * 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 SpotifyPlugin_H +#define SpotifyPlugin_H + +#include "infosystem/infosystem.h" +#include "infosystem/infosystemworker.h" +#include +#include + +class QNetworkReply; + +namespace Tomahawk +{ + +namespace InfoSystem +{ + +class SpotifyPlugin : public InfoPlugin +{ + Q_OBJECT + +public: + SpotifyPlugin(); + virtual ~SpotifyPlugin(); + + enum ChartType { + None = 0x00, + Track = 0x01, + Album = 0x02, + Artist = 0x04 + + }; + void setChartType( ChartType type ) { m_chartType = type; } + ChartType chartType() const { return m_chartType; } + +public slots: + void chartReturned(); + void chartTypes(); + void namChangedSlot( QNetworkAccessManager *nam ); + +protected slots: + virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data ); + +private: + void fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchChartCapabilities( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + void dataError( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + + + ChartType m_chartType; + QVariantMap m_allChartsMap; + + + QWeakPointer< QNetworkAccessManager > m_nam; +}; + +} + +} + +#endif // SpotifyPlugin_H diff --git a/src/libtomahawk/infosystem/infosystemworker.cpp b/src/libtomahawk/infosystem/infosystemworker.cpp index 965a21581..3f9478df6 100644 --- a/src/libtomahawk/infosystem/infosystemworker.cpp +++ b/src/libtomahawk/infosystem/infosystemworker.cpp @@ -26,6 +26,7 @@ #include "infoplugins/generic/echonestplugin.h" #include "infoplugins/generic/musixmatchplugin.h" #include "infoplugins/generic/chartsplugin.h" +#include "infoplugins/generic/spotifyPlugin.h" #include "infoplugins/generic/lastfmplugin.h" #include "infoplugins/generic/musicbrainzPlugin.h" #include "utils/tomahawkutils.h" @@ -96,6 +97,9 @@ InfoSystemWorker::init( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache> cac InfoPluginPtr roviptr( new RoviPlugin() ); m_plugins.append( roviptr ); registerInfoTypes( roviptr, roviptr.data()->supportedGetTypes(), roviptr.data()->supportedPushTypes() ); + InfoPluginPtr spotptr( new SpotifyPlugin() ); + m_plugins.append( spotptr ); + registerInfoTypes( spotptr, spotptr.data()->supportedGetTypes(), spotptr.data()->supportedPushTypes() ); #ifdef Q_WS_MAC InfoPluginPtr admptr( new AdiumPlugin() ); From 6c35a435f08a77593f0fb4ee6ba2aa4ba69a3fc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Mon, 24 Oct 2011 13:09:19 +0200 Subject: [PATCH 005/109] Correct api url --- .../infosystem/infoplugins/generic/spotifyPlugin.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp index f6c77eef0..b3f25d836 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp @@ -1,7 +1,6 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Hugo Lindström - * 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 @@ -34,7 +33,7 @@ #include "utils/logger.h" #include "chartsplugin_data_p.h" -#define CHART_URL "http://localhost:5112/" +#define SPOTIFY_API_URL "http://spotikea.tomahawk-player.org:10380/" #include #include @@ -69,7 +68,7 @@ SpotifyPlugin::namChangedSlot( QNetworkAccessManager *nam ) /// We need to fetch possible types before they are asked for tDebug() << "SpotifyPlugin: InfoChart fetching possible resources"; - QUrl url = QUrl( QString( CHART_URL "toplist/charts" ) ); + QUrl url = QUrl( QString( SPOTIFY_API_URL "toplist/charts" ) ); QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) ); tDebug() << Q_FUNC_INFO << "fetching:" << url; connect( reply, SIGNAL( finished() ), SLOT( chartTypes() ) ); From 4e7672fd63d42d80d6be8363d617f4a6b359d2b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Mon, 24 Oct 2011 13:12:59 +0200 Subject: [PATCH 006/109] Really set the correct api url --- .../infosystem/infoplugins/generic/spotifyPlugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp index b3f25d836..8f4dc5378 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp @@ -172,7 +172,7 @@ SpotifyPlugin::notInCacheSlot( uint requestId, QHash criteria, case InfoChart: { /// Fetch the chart, we need source and id - QUrl url = QUrl( QString( CHART_URL "toplist/%1/" ).arg( criteria["chart_id"] ) ); + QUrl url = QUrl( QString( SPOTIFY_API_URL "toplist/%1/" ).arg( criteria["chart_id"] ) ); qDebug() << Q_FUNC_INFO << "Getting chart url" << url; QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) ); From 188dc0b3bda432c199bfc65903bb82adfb974a4d Mon Sep 17 00:00:00 2001 From: Jason Herskowitz Date: Mon, 24 Oct 2011 14:15:09 -0400 Subject: [PATCH 007/109] Fill star rating icons with color --- data/images/star-hover.png | Bin 2188 -> 19778 bytes data/images/starred.png | Bin 2228 -> 19778 bytes data/images/user-avatar.png | Bin 19788 -> 19788 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/images/star-hover.png b/data/images/star-hover.png index 89efe2aef4360c17899963c54eeba1d3b8210f0d..fcf452a4675a39932ba23b40fe2ea53b73704e5e 100644 GIT binary patch literal 19778 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 DLt`+| diff --git a/data/images/starred.png b/data/images/starred.png index a93e9f6da08f6f9b664d9b143bf6cae3729c9508..247c1308d1da66ffe026851cb858a4051a2576d4 100644 GIT binary patch literal 19778 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 delta 2218 zcmV;b2vzsOngO&CkRyKp1am@3R0s$N2z&@+hyVZp32;bRa{vGh*8l(wVFB>Bl12ak z2v$i%K~#90<(hwt9aR;_KX=}|-EH^nZndjL1t}7-N{g6UDXH2D8cE%1gIjDPe{8i1 z)w*4vh>9i#t06!F`vWLg0-Ex}wTh&c9}-D5RzNGULW9)`jh25PwAkJ5*6#Mbo$DXx z&CZ?s=Iy?Dzb1{}WRjV4&b{ZHJNL)A=U(_fM=D|cGxIj&61F1|Tm*~&{{a>zY)c|| z643eQfrM?TJ5B?#1Pw3>G$&z*lY~-W72q1*&A_J1njB{K18_tpJ(tcHs~JkuDV-S z2i!{F!r1-xqTzg1t;Y7Yl^g}}k9F{`2)Stv-Lah;SD_J3?kL;UTi1Zr0TS zcM~}F>=bhUAbOHKrO^PqLEu==6tdr^aG5;iv4U`*^Y$rZ7Z4r>i)(IO9q<5wLrsLt355TLbOoyXX zJJ8beom~#x2?*V807&0&b4obny}D{*Fp@ zPJSr26WDFZ`VH{j{8MdcV66juyatuqIEs<->VpuZb)Yv8kTCi`ZMwa|85HxYXE>&BvrM_v<=t-T!pNw zlju#iEmWxON-=keIxB|u^b3y4t=^D5zRzN1kX`2*%igU(H*)S!fToPHq)T(LwfFu)74#S1ArV` z-ADKkQvLRU5>#rtQtE%S7&;0d^MLB?&JOLhSThmFyDa*Tfv*rfKNKKN z1NidSAlaIGi`PP8Ls!l z(GhnysDWFMDD99zUkp44EH{O(Vr0L<+Z0nrV4SDdML53(*({mP{|z~1isgSil)oL` zjjXkg8?@QL4y1QX);lLVvQ2AmD6Rsn^??mRXbcjV-H8M@Ee3rM_#krT7#o%31CAhn z{M!Ukq62&rSnCU21{26S91og37UgPWjrWJ`IIsa3dLyyx&!f_Wh(;2bpG4B#J~Gkb zr9%~l-e#TuMK*A^SE{F48KHk+tFA}GY&DOV&=QTBc+7a80lXf!&L~u?0nDZGuNWcc zfZK9GIZL5()cgUO&NN6*BbzpmVX&gGRD4vc0Ze0dn2ZVKblAdL>mz7AfTl}py-s`H zR;?+RD7D*Qmi2(0MRb;pYD8*h5bmy_22fgepn`Bid*0T@Tx!!595sJ;d-wFy@(rC< zTa`flw6I19#-#k05sdCjjnb#yfHijfWur*FCSR zdtzi~bTxwBbnE98&VW+P(pry=XNTL3*gt~|1AnKt3fNwW@zodt=JAj-L&NW zG`~<;Z{Bpy18mG5yFY)O9iOi?yOdH*jpokDjPI%pgG!J=LQwxCr_*Hir&}NH zOSgToNZI!zsq#k%=N*Fb?yQY{ZtD*%@_IwxVG_RZeW_v07~u$wep+TnlyQ*F)0k0q z6bW()5K+e&gzp)Cq|9U@{#ZMxr{Cc9`ieOn%pppLWk*D=aaL@R00r6yAa)P3*tD3pf)w_1RahYsz<{eC#s} zxWVEz19wq)DSCe|;p=fWE=0m;vrLY{oFvHB*q8ZD1%8x;vk5!5$lw)o;wEHIKOIVK zyyXTj3%t2(Z^}TAq5KsjqD&)KVeM}GGVswdbbN~N-9dl&T#F2!CS>{ad6$>%O&NH~ zP<%JVC$jq}K9QAzcHrk$y?&RUYw-pv&=U)@kP~2E@h^XgUdk@5qRuX&m(VAS?;%PX zVijWzg@2@+U?wi+B7c4=-Jhs|-U67nzRag+I9o+Hz}E?f(dN@vWBs_u#R6OL(-hgz z)ClJxC)TzdFV|Y}B1_>(;5#I0NMY{ngPt30kEJ92~_}M0B{oKm9~?q3?B&!6&5Na(wPo>v(XHDKo&3!&;S4c delta 38 wcmV+>0NMY{ngPt30kEJ938etG0Mr5LKlYQU3?B&y5e_;maTlWJv(XHDKrHYM+W-In From eb3d9553ed5178103cb2c7e1a780b9a4177dcb53 Mon Sep 17 00:00:00 2001 From: Jason Herskowitz Date: Mon, 24 Oct 2011 17:18:51 -0400 Subject: [PATCH 008/109] Make now playing speaker in sidebar gray instead of black --- data/images/now-playing-speaker-dark.png | Bin 4346 -> 19778 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/data/images/now-playing-speaker-dark.png b/data/images/now-playing-speaker-dark.png index 6aeb9dae6cb99007402d845334c400dad18d32a5..fd0fec99bf3fb707a4e57f362167be62c9518df1 100644 GIT binary patch literal 19778 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 delta 532 zcmXxhO-lkn7zgm#TtjS^EVV+hTnwTYkve2ZhtieYiWuG_(I-+=1Ud^UOH&%#1h3THUe^R*FleKjIfcm`=eiAqL3T zjqk`;Yv(c^ywV_^(Q|~Q)_#|ae>y~{ z+N7?Pq#Vjlbw)YUo<(#Uiq5-R2M!?9`_||#F#W73T$^U3l&JH&#_VUkqq>Eka2$=qQxa1RnCM| zHQBStt$8IbWSap#QwpAWH6}mM1Xfyr_CB4+?drg)4zSKU>_Se;kgBD0)4doYpIS?+ u)pXos;Tp7uc_bFhN;&GiLgxYhnT=a+gzop|&nE`X_{ze`7wwDe_WnOflx_V0 From 27073eb4f4537265d0f94181f0ed60e478324213 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Mon, 24 Oct 2011 18:33:45 -0400 Subject: [PATCH 009/109] Create default constructor for InfoRequest struct; include requestId in InfoRequest struct; rework rest of InfoSystem to use that requestId instead of internal one. --- .../infoplugins/generic/RoviPlugin.cpp | 19 ++- .../infoplugins/generic/RoviPlugin.h | 4 +- .../infoplugins/generic/chartsplugin.cpp | 56 ++++---- .../infoplugins/generic/chartsplugin.h | 12 +- .../infoplugins/generic/echonestplugin.cpp | 77 +++++------ .../infoplugins/generic/echonestplugin.h | 21 ++- .../infoplugins/generic/lastfmplugin.cpp | 123 +++++++----------- .../infoplugins/generic/lastfmplugin.h | 18 +-- .../infoplugins/generic/musicbrainzPlugin.cpp | 35 +++-- .../infoplugins/generic/musicbrainzPlugin.h | 7 +- .../infoplugins/generic/musixmatchplugin.cpp | 22 ++-- .../infoplugins/generic/musixmatchplugin.h | 7 +- .../infoplugins/generic/spotifyPlugin.cpp | 47 +++---- .../infoplugins/generic/spotifyPlugin.h | 10 +- .../infosystem/infoplugins/mac/adiumplugin.h | 6 +- .../infoplugins/unix/fdonotifyplugin.h | 6 +- .../infoplugins/unix/mprisplugin.cpp | 3 +- .../infosystem/infoplugins/unix/mprisplugin.h | 5 +- src/libtomahawk/infosystem/infosystem.cpp | 4 +- src/libtomahawk/infosystem/infosystem.h | 73 +++++++---- .../infosystem/infosystemcache.cpp | 27 ++-- src/libtomahawk/infosystem/infosystemcache.h | 6 +- .../infosystem/infosystemworker.cpp | 19 +-- src/libtomahawk/infosystem/infosystemworker.h | 6 +- src/libtomahawk/utils/dropjobnotifier.cpp | 2 +- src/libtomahawk/utils/tomahawkutils.cpp | 12 ++ src/libtomahawk/utils/tomahawkutils.h | 2 + 27 files changed, 291 insertions(+), 338 deletions(-) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp index a031d7a37..bf6791742 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp @@ -64,28 +64,28 @@ RoviPlugin::namChangedSlot( QNetworkAccessManager* nam ) void -RoviPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +RoviPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return; } InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); if ( !hash.contains( "artist" ) || !hash.contains( "album" ) ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return; } Tomahawk::InfoSystem::InfoStringHash criteria; criteria["album"] = hash["album"]; - emit getCachedInfo( requestId, criteria, 2419200000, requestData ); + emit getCachedInfo( criteria, 2419200000, requestData ); } void -RoviPlugin::notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) +RoviPlugin::notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { switch ( requestData.type ) { @@ -96,7 +96,6 @@ RoviPlugin::notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash QNetworkReply* reply = makeRequest( baseUrl ); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), this, SLOT( albumLookupFinished() ) ); connect( reply, SIGNAL( error( QNetworkReply::NetworkError ) ), this, SLOT( albumLookupError( QNetworkReply::NetworkError ) ) ); @@ -120,9 +119,8 @@ RoviPlugin::albumLookupError( QNetworkReply::NetworkError error ) Q_ASSERT( reply ); Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); - int requestId = reply->property( "requestId" ).toUInt(); - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); } @@ -136,7 +134,6 @@ RoviPlugin::albumLookupFinished() return; Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); - int requestId = reply->property( "requestId" ).toUInt(); QJson::Parser p; bool ok; @@ -145,7 +142,7 @@ RoviPlugin::albumLookupFinished() if ( !ok || result.isEmpty() || !result.contains( "tracks" ) ) { tLog() << "Error parsing JSON from Rovi!" << p.errorString() << result; - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); } QVariantList tracks = result[ "tracks" ].toList(); @@ -160,7 +157,7 @@ RoviPlugin::albumLookupFinished() QVariantMap returnedData; returnedData["tracks"] = trackNameList; - emit info( requestId, requestData, returnedData ); + emit info( requestData, returnedData ); Tomahawk::InfoSystem::InfoStringHash criteria; criteria["artist"] = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>()["artist"]; diff --git a/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.h b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.h index 0bc2e9e8c..21c81c5a9 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.h +++ b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.h @@ -40,12 +40,12 @@ public: protected: virtual void namChangedSlot( QNetworkAccessManager* nam ); - virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); virtual void pushInfo( QString, Tomahawk::InfoSystem::InfoType, QVariant ) {} - virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); private slots: void albumLookupFinished(); diff --git a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp index 75a7a8f66..a0fe25503 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp @@ -93,15 +93,15 @@ ChartsPlugin::namChangedSlot( QNetworkAccessManager *nam ) void -ChartsPlugin::dataError( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +ChartsPlugin::dataError( Tomahawk::InfoSystem::InfoRequestData requestData ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return; } void -ChartsPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +ChartsPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { qDebug() << Q_FUNC_INFO << requestData.caller; qDebug() << Q_FUNC_INFO << requestData.customData; @@ -116,7 +116,7 @@ ChartsPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData req /// We need something to check if the request is actually ment to go to this plugin if ( !hash.contains( "chart_source" ) ) { - dataError( requestId, requestData ); + dataError( requestData ); break; } else @@ -131,19 +131,19 @@ ChartsPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData req if( !foundSource ) { - dataError( requestId, requestData ); + dataError( requestData ); break; } } - fetchChart( requestId, requestData ); + fetchChart( requestData ); break; case InfoChartCapabilities: - fetchChartCapabilities( requestId, requestData ); + fetchChartCapabilities( requestData ); break; default: - dataError( requestId, requestData ); + dataError( requestData ); } } @@ -158,12 +158,12 @@ ChartsPlugin::pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoTy void -ChartsPlugin::fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +ChartsPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } @@ -173,7 +173,7 @@ ChartsPlugin::fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData /// Each request needs to contain both a id and source if ( !hash.contains( "chart_id" ) && !hash.contains( "chart_source" ) ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } @@ -181,29 +181,29 @@ ChartsPlugin::fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData criteria["chart_id"] = hash["chart_id"]; criteria["chart_source"] = hash["chart_source"]; - emit getCachedInfo( requestId, criteria, 0, requestData ); + emit getCachedInfo( criteria, 0, requestData ); } void -ChartsPlugin::fetchChartCapabilities( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +ChartsPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } Tomahawk::InfoSystem::InfoStringHash criteria; - emit getCachedInfo( requestId, criteria, 0, requestData ); + emit getCachedInfo( criteria, 0, requestData ); } void -ChartsPlugin::notInCacheSlot( uint requestId, QHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) +ChartsPlugin::notInCacheSlot( QHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !m_nam.data() ) { tLog() << "Have a null QNAM, uh oh"; - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return; } @@ -217,7 +217,6 @@ ChartsPlugin::notInCacheSlot( uint requestId, QHash criteria, qDebug() << Q_FUNC_INFO << "Getting chart url" << url; QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) ); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( chartReturned() ) ); @@ -230,22 +229,18 @@ ChartsPlugin::notInCacheSlot( uint requestId, QHash criteria, if ( m_chartsFetchJobs > 0 ) { qDebug() << Q_FUNC_INFO << "InfoChartCapabilities still fetching!"; - m_cachedRequests.append( QPair< uint, InfoRequestData >( requestId, requestData ) ); + m_cachedRequests.append( requestData ); return; } - emit info( - requestId, - requestData, - m_allChartsMap - ); + emit info( requestData, m_allChartsMap ); return; } default: { tLog() << Q_FUNC_INFO << "Couldn't figure out what to do with this type of request after cache miss"; - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return; } } @@ -408,10 +403,9 @@ ChartsPlugin::chartTypes() m_chartsFetchJobs--; if ( !m_cachedRequests.isEmpty() && m_chartsFetchJobs == 0 ) { - QPair< uint, InfoRequestData > request; - foreach ( request, m_cachedRequests ) + foreach ( InfoRequestData request, m_cachedRequests ) { - emit info( request.first, request.second, m_allChartsMap ); + emit info( request, m_allChartsMap ); } m_cachedRequests.clear(); } @@ -526,11 +520,7 @@ ChartsPlugin::chartReturned() Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); - emit info( - reply->property( "requestId" ).toUInt(), - requestData, - returnedData - ); + emit info( requestData, returnedData ); // TODO update cache } else diff --git a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.h b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.h index 3d35b39cf..c44b88ccf 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.h +++ b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.h @@ -56,15 +56,15 @@ public slots: void namChangedSlot( QNetworkAccessManager *nam ); protected slots: - virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); - virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data ); private: - void fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); - void fetchChartCapabilities( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); - void dataError( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData ); + void dataError( Tomahawk::InfoSystem::InfoRequestData requestData ); QVariantList m_chartResources; QList m_charts; @@ -73,7 +73,7 @@ private: QVariantMap m_allChartsMap; uint m_chartsFetchJobs; - QList< QPair< uint, InfoRequestData > > m_cachedRequests; + QList< InfoRequestData > m_cachedRequests; QHash< QString, QString > m_cachedCountries; diff --git a/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.cpp index a975dbe08..3d4b1e02b 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.cpp @@ -68,37 +68,37 @@ EchoNestPlugin::namChangedSlot( QNetworkAccessManager *nam ) } void -EchoNestPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +EchoNestPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { switch ( requestData.type ) { case Tomahawk::InfoSystem::InfoArtistBiography: - return getArtistBiography( requestId, requestData ); + return getArtistBiography( requestData ); case Tomahawk::InfoSystem::InfoArtistFamiliarity: - return getArtistFamiliarity( requestId, requestData ); + return getArtistFamiliarity( requestData ); case Tomahawk::InfoSystem::InfoArtistHotttness: - return getArtistHotttnesss( requestId, requestData ); + return getArtistHotttnesss( requestData ); case Tomahawk::InfoSystem::InfoArtistTerms: - return getArtistTerms( requestId, requestData ); + return getArtistTerms( requestData ); case Tomahawk::InfoSystem::InfoTrackEnergy: - return getSongProfile( requestId, requestData, "energy" ); + return getSongProfile( requestData, "energy" ); case Tomahawk::InfoSystem::InfoMiscTopTerms: - return getMiscTopTerms( requestId, requestData ); + return getMiscTopTerms( requestData ); default: { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return; } } } void -EchoNestPlugin::getSongProfile( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData, const QString &item ) +EchoNestPlugin::getSongProfile( const Tomahawk::InfoSystem::InfoRequestData &requestData, const QString &item ) { //WARNING: Totally not implemented yet Q_UNUSED( item ); - if( !isValidTrackData( requestId, requestData ) ) + if( !isValidTrackData( requestData ) ) return; // Track track( input.toString() ); @@ -110,67 +110,62 @@ EchoNestPlugin::getSongProfile( uint requestId, const Tomahawk::InfoSystem::Info } void -EchoNestPlugin::getArtistBiography( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ) +EchoNestPlugin::getArtistBiography( const Tomahawk::InfoSystem::InfoRequestData &requestData ) { - if( !isValidArtistData( requestId, requestData ) ) + if( !isValidArtistData( requestData ) ) return; Echonest::Artist artist( requestData.input.toString() ); QNetworkReply *reply = artist.fetchBiographies(); reply->setProperty( "artist", QVariant::fromValue< Echonest::Artist >( artist ) ); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( getArtistBiographySlot() ) ); } void -EchoNestPlugin::getArtistFamiliarity( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ) +EchoNestPlugin::getArtistFamiliarity( const Tomahawk::InfoSystem::InfoRequestData &requestData ) { - if( !isValidArtistData( requestId, requestData ) ) + if( !isValidArtistData( requestData ) ) return; qDebug() << "Fetching artist familiarity!" << requestData.input; Echonest::Artist artist( requestData.input.toString() ); QNetworkReply* reply = artist.fetchFamiliarity(); reply->setProperty( "artist", QVariant::fromValue< Echonest::Artist >( artist ) ); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( getArtistFamiliaritySlot() ) ); } void -EchoNestPlugin::getArtistHotttnesss( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ) +EchoNestPlugin::getArtistHotttnesss( const Tomahawk::InfoSystem::InfoRequestData &requestData ) { - if( !isValidArtistData( requestId, requestData ) ) + if( !isValidArtistData( requestData ) ) return; Echonest::Artist artist( requestData.input.toString() ); QNetworkReply* reply = artist.fetchHotttnesss(); reply->setProperty( "artist", QVariant::fromValue< Echonest::Artist >( artist ) ); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( getArtistHotttnesssSlot() ) ); } void -EchoNestPlugin::getArtistTerms( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ) +EchoNestPlugin::getArtistTerms( const Tomahawk::InfoSystem::InfoRequestData &requestData ) { - if( !isValidArtistData( requestId, requestData ) ) + if( !isValidArtistData( requestData ) ) return; Echonest::Artist artist( requestData.input.toString() ); QNetworkReply* reply = artist.fetchTerms( Echonest::Artist::Weight ); reply->setProperty( "artist", QVariant::fromValue< Echonest::Artist >( artist ) ); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( getArtistTermsSlot() ) ); } void -EchoNestPlugin::getMiscTopTerms( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ) +EchoNestPlugin::getMiscTopTerms( const Tomahawk::InfoSystem::InfoRequestData &requestData ) { QNetworkReply* reply = Echonest::Artist::topTerms( 20 ); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( getMiscTopSlot() ) ); } @@ -195,9 +190,7 @@ EchoNestPlugin::getArtistBiographySlot() biographyMap[ biography.site() ] = siteData; } Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); - emit info( reply->property( "requestId" ).toUInt(), - requestData, - biographyMap ); + emit info( requestData, biographyMap ); reply->deleteLater(); } @@ -208,9 +201,7 @@ EchoNestPlugin::getArtistFamiliaritySlot() Echonest::Artist artist = artistFromReply( reply ); qreal familiarity = artist.familiarity(); Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); - emit info( reply->property( "requestId" ).toUInt(), - requestData, - familiarity ); + emit info( requestData, familiarity ); reply->deleteLater(); } @@ -221,9 +212,7 @@ EchoNestPlugin::getArtistHotttnesssSlot() Echonest::Artist artist = artistFromReply( reply ); qreal hotttnesss = artist.hotttnesss(); Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); - emit info( reply->property( "requestId" ).toUInt(), - requestData, - hotttnesss ); + emit info( requestData, hotttnesss ); reply->deleteLater(); } @@ -241,9 +230,7 @@ EchoNestPlugin::getArtistTermsSlot() termsMap[ term.name() ] = termHash; } Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); - emit info( reply->property( "requestId" ).toUInt(), - requestData, - termsMap ); + emit info( requestData, termsMap ); reply->deleteLater(); } @@ -260,46 +247,44 @@ EchoNestPlugin::getMiscTopSlot() termsMap[ term.name() ] = termHash; } Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); - emit info( reply->property( "requestId" ).toUInt(), - requestData, - termsMap ); + emit info( requestData, termsMap ); reply->deleteLater(); } bool -EchoNestPlugin::isValidArtistData( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ) +EchoNestPlugin::isValidArtistData( const Tomahawk::InfoSystem::InfoRequestData &requestData ) { if ( requestData.input.isNull() || !requestData.input.isValid() || !requestData.input.canConvert< QString >() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return false; } QString artistName = requestData.input.toString(); if ( artistName.isEmpty() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return false; } return true; } bool -EchoNestPlugin::isValidTrackData( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ) +EchoNestPlugin::isValidTrackData( const Tomahawk::InfoSystem::InfoRequestData &requestData ) { if ( requestData.input.isNull() || !requestData.input.isValid() || !requestData.input.canConvert< QString >() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return false; } QString trackName = requestData.input.toString(); if ( trackName.isEmpty() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return false; } if ( !requestData.customData.contains( "artistName" ) || requestData.customData[ "artistName" ].toString().isEmpty() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return false; } return true; diff --git a/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.h b/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.h index e6e0825ab..bd59193c8 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.h +++ b/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.h @@ -46,7 +46,7 @@ public: virtual ~EchoNestPlugin(); protected slots: - virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); virtual void pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant data ) { @@ -55,9 +55,8 @@ protected slots: Q_UNUSED( data ); } - virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) + virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { - Q_UNUSED( requestId ); Q_UNUSED( criteria ); Q_UNUSED( requestData ); } @@ -66,15 +65,15 @@ public slots: void namChangedSlot( QNetworkAccessManager *nam ); private: - void getSongProfile( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData, const QString &item = QString() ); - void getArtistBiography( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ); - void getArtistFamiliarity( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ); - void getArtistHotttnesss( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ); - void getArtistTerms( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ); - void getMiscTopTerms( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ); + void getSongProfile( const Tomahawk::InfoSystem::InfoRequestData &requestData, const QString &item = QString() ); + void getArtistBiography( const Tomahawk::InfoSystem::InfoRequestData &requestData ); + void getArtistFamiliarity( const Tomahawk::InfoSystem::InfoRequestData &requestData ); + void getArtistHotttnesss( const Tomahawk::InfoSystem::InfoRequestData &requestData ); + void getArtistTerms( const Tomahawk::InfoSystem::InfoRequestData &requestData ); + void getMiscTopTerms( const Tomahawk::InfoSystem::InfoRequestData &requestData ); - bool isValidArtistData( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ); - bool isValidTrackData( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ); + bool isValidArtistData( const Tomahawk::InfoSystem::InfoRequestData &requestData ); + bool isValidTrackData( const Tomahawk::InfoSystem::InfoRequestData &requestData ); Echonest::Artist artistFromReply( QNetworkReply* ); private slots: diff --git a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp index ae99903ec..ed7681642 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp @@ -123,43 +123,43 @@ LastFmPlugin::namChangedSlot( QNetworkAccessManager *nam ) void -LastFmPlugin::dataError( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +LastFmPlugin::dataError( Tomahawk::InfoSystem::InfoRequestData requestData ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return; } void -LastFmPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +LastFmPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { switch ( requestData.type ) { case InfoArtistImages: - fetchArtistImages( requestId, requestData ); + fetchArtistImages( requestData ); break; case InfoAlbumCoverArt: - fetchCoverArt( requestId, requestData ); + fetchCoverArt( requestData ); break; case InfoArtistSimilars: - fetchSimilarArtists( requestId, requestData ); + fetchSimilarArtists( requestData ); break; case InfoArtistSongs: - fetchTopTracks( requestId, requestData ); + fetchTopTracks( requestData ); break; case InfoChart: - fetchChart( requestId, requestData ); + fetchChart( requestData ); break; case InfoChartCapabilities: - fetchChartCapabilities( requestId, requestData ); + fetchChartCapabilities( requestData ); break; default: - dataError( requestId, requestData ); + dataError( requestData ); } } @@ -267,95 +267,95 @@ LastFmPlugin::sendLoveSong( const InfoType type, QVariant input ) void -LastFmPlugin::fetchSimilarArtists( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +LastFmPlugin::fetchSimilarArtists( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); if ( !hash.contains( "artist" ) ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } Tomahawk::InfoSystem::InfoStringHash criteria; criteria["artist"] = hash["artist"]; - emit getCachedInfo( requestId, criteria, 2419200000, requestData ); + emit getCachedInfo( criteria, 2419200000, requestData ); } void -LastFmPlugin::fetchTopTracks( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +LastFmPlugin::fetchTopTracks( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); if ( !hash.contains( "artist" ) ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } Tomahawk::InfoSystem::InfoStringHash criteria; criteria["artist"] = hash["artist"]; - emit getCachedInfo( requestId, criteria, 2419200000, requestData ); + emit getCachedInfo( criteria, 2419200000, requestData ); } void -LastFmPlugin::fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +LastFmPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); Tomahawk::InfoSystem::InfoStringHash criteria; if ( !hash.contains( "chart_id" ) ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } else { criteria["chart_id"] = hash["chart_id"]; } - emit getCachedInfo( requestId, criteria, 0, requestData ); + emit getCachedInfo( criteria, 0, requestData ); } void -LastFmPlugin::fetchChartCapabilities( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +LastFmPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); Tomahawk::InfoSystem::InfoStringHash criteria; - emit getCachedInfo( requestId, criteria, 0, requestData ); + emit getCachedInfo( criteria, 0, requestData ); } void -LastFmPlugin::fetchCoverArt( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +LastFmPlugin::fetchCoverArt( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); if ( !hash.contains( "artist" ) || !hash.contains( "album" ) ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } @@ -363,39 +363,39 @@ LastFmPlugin::fetchCoverArt( uint requestId, Tomahawk::InfoSystem::InfoRequestDa criteria["artist"] = hash["artist"]; criteria["album"] = hash["album"]; - emit getCachedInfo( requestId, criteria, 2419200000, requestData ); + emit getCachedInfo( criteria, 2419200000, requestData ); } void -LastFmPlugin::fetchArtistImages( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +LastFmPlugin::fetchArtistImages( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); if ( !hash.contains( "artist" ) ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } Tomahawk::InfoSystem::InfoStringHash criteria; criteria["artist"] = hash["artist"]; - emit getCachedInfo( requestId, criteria, 2419200000, requestData ); + emit getCachedInfo( criteria, 2419200000, requestData ); } void -LastFmPlugin::notInCacheSlot( uint requestId, QHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) +LastFmPlugin::notInCacheSlot( QHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !lastfm::nam() ) { tLog() << "Have a null QNAM, uh oh"; - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return; } @@ -407,14 +407,14 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash criteria, /// We need something to check if the request is actually ment to go to this plugin if ( !hash.contains( "chart_source" ) ) { - dataError( requestId, requestData ); + dataError( requestData ); break; } else { if( "last.fm" != hash["chart_source"] ) { - dataError( requestId, requestData ); + dataError( requestData ); break; } @@ -425,7 +425,6 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash criteria, args["method"] = criteria["chart_id"]; args["limit"] = "100"; QNetworkReply* reply = lastfm::ws::get(args); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( chartReturned() ) ); @@ -466,11 +465,7 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash criteria, result.insert( "Last.fm", QVariant::fromValue( charts ) ); tDebug() << "LASTFM RETURNING CHART LIST!"; - emit info( - requestId, - requestData, - result - ); + emit info( requestData, result ); return; } @@ -478,7 +473,6 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash criteria, { lastfm::Artist a( criteria["artist"] ); QNetworkReply* reply = a.getSimilar(); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( similarArtistsReturned() ) ); @@ -489,7 +483,6 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash criteria, { lastfm::Artist a( criteria["artist"] ); QNetworkReply* reply = a.getTopTracks(); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( topTracksReturned() ) ); @@ -504,7 +497,6 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash criteria, QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=album.imageredirect&artist=%1&album=%2&autocorrect=1&size=large&api_key=7a90f6672a04b809ee309af169f34b8b"; QNetworkRequest req( imgurl.arg( artistName ).arg( albumName ) ); QNetworkReply* reply = lastfm::nam()->get( req ); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( coverArtReturned() ) ); @@ -518,7 +510,6 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash criteria, QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=artist.imageredirect&artist=%1&autocorrect=1&size=large&api_key=7a90f6672a04b809ee309af169f34b8b"; QNetworkRequest req( imgurl.arg( artistName ) ); QNetworkReply* reply = lastfm::nam()->get( req ); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( artistImagesReturned() ) ); @@ -528,7 +519,7 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash criteria, default: { tLog() << "Couldn't figure out what to do with this type of request after cache miss"; - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return; } } @@ -553,11 +544,7 @@ LastFmPlugin::similarArtistsReturned() Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); - emit info( - reply->property( "requestId" ).toUInt(), - requestData, - returnedData - ); + emit info( requestData, returnedData ); Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>(); Tomahawk::InfoSystem::InfoStringHash criteria; @@ -609,11 +596,7 @@ LastFmPlugin::chartReturned() Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); - emit info( - reply->property( "requestId" ).toUInt(), - requestData, - returnedData - ); + emit info( requestData, returnedData ); // TODO update cache } @@ -629,11 +612,7 @@ LastFmPlugin::topTracksReturned() Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); - emit info( - reply->property( "requestId" ).toUInt(), - requestData, - returnedData - ); + emit info( requestData, returnedData ); Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>(); Tomahawk::InfoSystem::InfoStringHash criteria; @@ -653,7 +632,7 @@ LastFmPlugin::coverArtReturned() if ( ba.isNull() || !ba.length() ) { tLog() << Q_FUNC_INFO << "Uh oh, null byte array"; - emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } foreach ( const QUrl& url, m_badUrls ) @@ -668,11 +647,7 @@ LastFmPlugin::coverArtReturned() Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); - emit info( - reply->property( "requestId" ).toUInt(), - requestData, - returnedData - ); + emit info( requestData, returnedData ); Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>(); Tomahawk::InfoSystem::InfoStringHash criteria; @@ -685,13 +660,12 @@ LastFmPlugin::coverArtReturned() if ( !lastfm::nam() ) { tLog() << Q_FUNC_INFO << "Uh oh, nam is null"; - emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } // Follow HTTP redirect QNetworkRequest req( redir ); QNetworkReply* newReply = lastfm::nam()->get( req ); - newReply->setProperty( "requestId", reply->property( "requestId" ) ); newReply->setProperty( "requestData", reply->property( "requestData" ) ); connect( newReply, SIGNAL( finished() ), SLOT( coverArtReturned() ) ); } @@ -711,7 +685,7 @@ LastFmPlugin::artistImagesReturned() if ( ba.isNull() || !ba.length() ) { tLog() << Q_FUNC_INFO << "Uh oh, null byte array"; - emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } foreach ( const QUrl& url, m_badUrls ) @@ -725,7 +699,7 @@ LastFmPlugin::artistImagesReturned() Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); - emit info( reply->property( "requestId" ).toUInt(), requestData, returnedData ); + emit info( requestData, returnedData ); Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>(); Tomahawk::InfoSystem::InfoStringHash criteria; @@ -737,13 +711,12 @@ LastFmPlugin::artistImagesReturned() if ( !lastfm::nam() ) { tLog() << Q_FUNC_INFO << "Uh oh, nam is null"; - emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } // Follow HTTP redirect QNetworkRequest req( redir ); QNetworkReply* newReply = lastfm::nam()->get( req ); - newReply->setProperty( "requestId", reply->property( "requestId" ) ); newReply->setProperty( "requestData", reply->property( "requestData" ) ); connect( newReply, SIGNAL( finished() ), SLOT( artistImagesReturned() ) ); } diff --git a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.h b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.h index 90f117882..fbd372e02 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.h +++ b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.h @@ -56,25 +56,25 @@ public slots: void namChangedSlot( QNetworkAccessManager *nam ); protected slots: - virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); - virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data ); private: - void fetchCoverArt( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); - void fetchArtistImages( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); - void fetchSimilarArtists( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); - void fetchTopTracks( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); - void fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); - void fetchChartCapabilities( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchCoverArt( Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchArtistImages( Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchSimilarArtists( Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchTopTracks( Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData ); void createScrobbler(); void nowPlaying( const QVariant &input ); void scrobble(); void sendLoveSong( const InfoType type, QVariant input ); - void dataError( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + void dataError( Tomahawk::InfoSystem::InfoRequestData requestData ); QList parseTrackList( QNetworkReply * reply ); diff --git a/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.cpp index 388eda463..c5659f27f 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.cpp @@ -52,17 +52,17 @@ MusicBrainzPlugin::namChangedSlot( QNetworkAccessManager *nam ) void -MusicBrainzPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +MusicBrainzPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return; } InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); if ( !hash.contains( "artist" ) ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return; } @@ -74,7 +74,6 @@ MusicBrainzPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestDat QUrl url( requestString ); url.addQueryItem( "query", hash["artist"] ); QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) ); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( artistSearchSlot() ) ); @@ -87,7 +86,6 @@ MusicBrainzPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestDat QUrl url( requestString ); url.addQueryItem( "query", hash["artist"] ); QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) ); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( albumSearchSlot() ) ); @@ -104,24 +102,24 @@ MusicBrainzPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestDat bool -MusicBrainzPlugin::isValidTrackData( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +MusicBrainzPlugin::isValidTrackData( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( requestData.input.isNull() || !requestData.input.isValid() || !requestData.input.canConvert< QVariantMap >() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); qDebug() << Q_FUNC_INFO << "Data null, invalid, or can't convert"; return false; } QVariantMap hash = requestData.input.value< QVariantMap >(); if ( hash[ "trackName" ].toString().isEmpty() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); qDebug() << Q_FUNC_INFO << "Track name is empty"; return false; } if ( hash[ "artistName" ].toString().isEmpty() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); qDebug() << Q_FUNC_INFO << "No artist name found"; return false; } @@ -141,7 +139,7 @@ MusicBrainzPlugin::artistSearchSlot() QDomNodeList domNodeList = doc.elementsByTagName( "artist" ); if ( domNodeList.isEmpty() ) { - emit info( oldReply->property( "requestId" ).toUInt(), oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + emit info( oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } @@ -151,7 +149,6 @@ MusicBrainzPlugin::artistSearchSlot() url.addQueryItem( "artist", artist_id ); QNetworkReply* newReply = m_nam.data()->get( QNetworkRequest( url ) ); - newReply->setProperty( "requestId", oldReply->property( "requestId" ) ); newReply->setProperty( "requestData", oldReply->property( "requestData" ) ); connect( newReply, SIGNAL( finished() ), SLOT( albumFoundSlot() ) ); } @@ -169,7 +166,7 @@ MusicBrainzPlugin::albumSearchSlot() QDomNodeList domNodeList = doc.elementsByTagName( "artist" ); if ( domNodeList.isEmpty() ) { - emit info( oldReply->property( "requestId" ).toUInt(), oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + emit info( oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } @@ -179,7 +176,6 @@ MusicBrainzPlugin::albumSearchSlot() url.addQueryItem( "artist", artist_id ); QNetworkReply* newReply = m_nam.data()->get( QNetworkRequest( url ) ); - newReply->setProperty( "requestId", oldReply->property( "requestId" ) ); newReply->setProperty( "requestData", oldReply->property( "requestData" ) ); connect( newReply, SIGNAL( finished() ), SLOT( tracksSearchSlot() ) ); } @@ -197,7 +193,7 @@ MusicBrainzPlugin::tracksSearchSlot() QDomNodeList domNodeList = doc.elementsByTagName( "release" ); if ( domNodeList.isEmpty() ) { - emit info( oldReply->property( "requestId" ).toUInt(), oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + emit info( oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } @@ -214,7 +210,7 @@ MusicBrainzPlugin::tracksSearchSlot() if ( element.isNull() ) { - emit info( oldReply->property( "requestId" ).toUInt(), oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + emit info( oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } @@ -223,7 +219,6 @@ MusicBrainzPlugin::tracksSearchSlot() QUrl url( requestString ); QNetworkReply* newReply = m_nam.data()->get( QNetworkRequest( url ) ); - newReply->setProperty( "requestId", oldReply->property( "requestId" ) ); newReply->setProperty( "requestData", oldReply->property( "requestData" ) ); connect( newReply, SIGNAL( finished() ), SLOT( tracksFoundSlot() ) ); } @@ -241,7 +236,7 @@ MusicBrainzPlugin::albumFoundSlot() QDomNodeList domNodeList = doc.elementsByTagName( "title" ); if ( domNodeList.isEmpty() ) { - emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } @@ -256,7 +251,7 @@ MusicBrainzPlugin::albumFoundSlot() Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); QVariantMap returnedData; returnedData["albums"] = albums; - emit info( reply->property( "requestId" ).toUInt(), requestData, returnedData ); + emit info( requestData, returnedData ); Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>(); Tomahawk::InfoSystem::InfoStringHash criteria; @@ -277,7 +272,7 @@ MusicBrainzPlugin::tracksFoundSlot() QDomNodeList domNodeList = doc.elementsByTagName( "recording" ); if ( domNodeList.isEmpty() ) { - emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } @@ -297,7 +292,7 @@ MusicBrainzPlugin::tracksFoundSlot() Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); QVariantMap returnedData; returnedData["tracks"] = tracks; - emit info( reply->property( "requestId" ).toUInt(), requestData, returnedData ); + emit info( requestData, returnedData ); Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>(); Tomahawk::InfoSystem::InfoStringHash criteria; diff --git a/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.h b/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.h index 8e943bae3..77637e37b 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.h +++ b/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.h @@ -42,7 +42,7 @@ public slots: void namChangedSlot( QNetworkAccessManager *nam ); protected slots: - virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data ) { @@ -51,9 +51,8 @@ protected slots: Q_UNUSED( data ); } -virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) +virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { - Q_UNUSED( requestId ); Q_UNUSED( criteria ); Q_UNUSED( requestData ); } @@ -67,7 +66,7 @@ private slots: void tracksFoundSlot(); private: - bool isValidTrackData( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + bool isValidTrackData( Tomahawk::InfoSystem::InfoRequestData requestData ); QWeakPointer< QNetworkAccessManager > m_nam; }; diff --git a/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.cpp index ca2b96858..05811765a 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.cpp @@ -53,17 +53,17 @@ MusixMatchPlugin::namChangedSlot( QNetworkAccessManager *nam ) } void -MusixMatchPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +MusixMatchPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { qDebug() << Q_FUNC_INFO; - if( !isValidTrackData( requestId, requestData ) || !requestData.input.canConvert< QVariantMap >() || m_nam.isNull() || requestData.type != Tomahawk::InfoSystem::InfoTrackLyrics ) + if( !isValidTrackData( requestData ) || !requestData.input.canConvert< QVariantMap >() || m_nam.isNull() || requestData.type != Tomahawk::InfoSystem::InfoTrackLyrics ) return; QVariantMap hash = requestData.input.value< QVariantMap >(); QString artist = hash["artistName"].toString(); QString track = hash["trackName"].toString(); if( artist.isEmpty() || track.isEmpty() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return; } qDebug() << "artist is " << artist << ", track is " << track; @@ -73,32 +73,31 @@ MusixMatchPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData url.addQueryItem( "q_artist", artist ); url.addQueryItem( "q_track", track ); QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) ); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( trackSearchSlot() ) ); } bool -MusixMatchPlugin::isValidTrackData( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +MusixMatchPlugin::isValidTrackData( Tomahawk::InfoSystem::InfoRequestData requestData ) { qDebug() << Q_FUNC_INFO; if ( requestData.input.isNull() || !requestData.input.isValid() || !requestData.input.canConvert< QVariantMap >() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); qDebug() << "MusixMatchPlugin::isValidTrackData: Data null, invalid, or can't convert"; return false; } QVariantMap hash = requestData.input.value< QVariantMap >(); if ( hash[ "trackName" ].toString().isEmpty() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); qDebug() << "MusixMatchPlugin::isValidTrackData: Track name is empty"; return false; } if ( hash[ "artistName" ].toString().isEmpty() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); qDebug() << "MusixMatchPlugin::isValidTrackData: No artist name found"; return false; } @@ -119,7 +118,7 @@ MusixMatchPlugin::trackSearchSlot() QDomNodeList domNodeList = doc.elementsByTagName("track_id"); if ( domNodeList.isEmpty() ) { - emit info( oldReply->property( "requestId" ).toUInt(), oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + emit info( oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } QString track_id = domNodeList.at(0).toElement().text(); @@ -128,7 +127,6 @@ MusixMatchPlugin::trackSearchSlot() url.addQueryItem( "apikey", m_apiKey ); url.addQueryItem( "track_id", track_id ); QNetworkReply* newReply = m_nam.data()->get( QNetworkRequest( url ) ); - newReply->setProperty( "requestId", oldReply->property( "requestId" ) ); newReply->setProperty( "requestData", oldReply->property( "requestData" ) ); connect( newReply, SIGNAL( finished() ), SLOT( trackLyricsSlot() ) ); } @@ -146,10 +144,10 @@ MusixMatchPlugin::trackLyricsSlot() QDomNodeList domNodeList = doc.elementsByTagName( "lyrics_body" ); if ( domNodeList.isEmpty() ) { - emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } QString lyrics = domNodeList.at(0).toElement().text(); qDebug() << "Emitting lyrics: " << lyrics; - emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant( lyrics ) ); + emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant( lyrics ) ); } diff --git a/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.h b/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.h index a6ef3fbe3..40b1cc536 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.h +++ b/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.h @@ -45,7 +45,7 @@ public slots: void namChangedSlot( QNetworkAccessManager *nam ); protected slots: - virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data ) { @@ -54,15 +54,14 @@ protected slots: Q_UNUSED( data ); } -virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) +virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { - Q_UNUSED( requestId ); Q_UNUSED( criteria ); Q_UNUSED( requestData ); } private: - bool isValidTrackData( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + bool isValidTrackData( Tomahawk::InfoSystem::InfoRequestData requestData ); QString m_apiKey; diff --git a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp index 8f4dc5378..e05d739b6 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp @@ -77,15 +77,15 @@ SpotifyPlugin::namChangedSlot( QNetworkAccessManager *nam ) void -SpotifyPlugin::dataError( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +SpotifyPlugin::dataError( Tomahawk::InfoSystem::InfoRequestData requestData ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return; } void -SpotifyPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +SpotifyPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { qDebug() << Q_FUNC_INFO << requestData.caller; qDebug() << Q_FUNC_INFO << requestData.customData; @@ -98,18 +98,18 @@ SpotifyPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData re case InfoChart: if ( !hash.contains( "chart_source" ) || hash["chart_source"] != "spotify" ) { - dataError( requestId, requestData ); + dataError( requestData ); break; } qDebug() << Q_FUNC_INFO << "InfoCHart req for" << hash["chart_source"]; - fetchChart( requestId, requestData ); + fetchChart( requestData ); break; case InfoChartCapabilities: - fetchChartCapabilities( requestId, requestData ); + fetchChartCapabilities( requestData ); break; default: - dataError( requestId, requestData ); + dataError( requestData ); } } @@ -123,45 +123,45 @@ SpotifyPlugin::pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoT } void -SpotifyPlugin::fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +SpotifyPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); Tomahawk::InfoSystem::InfoStringHash criteria; if ( !hash.contains( "chart_id" ) ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } else { criteria["chart_id"] = hash["chart_id"]; } - emit getCachedInfo( requestId, criteria, 0, requestData ); + emit getCachedInfo( criteria, 0, requestData ); } void -SpotifyPlugin::fetchChartCapabilities( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +SpotifyPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } Tomahawk::InfoSystem::InfoStringHash criteria; - emit getCachedInfo( requestId, criteria, 0, requestData ); + emit getCachedInfo( criteria, 0, requestData ); } void -SpotifyPlugin::notInCacheSlot( uint requestId, QHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) +SpotifyPlugin::notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !m_nam.data() ) { tLog() << Q_FUNC_INFO << "Have a null QNAM, uh oh"; - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return; } @@ -176,7 +176,6 @@ SpotifyPlugin::notInCacheSlot( uint requestId, QHash criteria, qDebug() << Q_FUNC_INFO << "Getting chart url" << url; QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) ); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( chartReturned() ) ); return; @@ -186,18 +185,14 @@ SpotifyPlugin::notInCacheSlot( uint requestId, QHash criteria, case InfoChartCapabilities: { qDebug() << Q_FUNC_INFO << "EMITTING CHART" << m_allChartsMap; - emit info( - requestId, - requestData, - m_allChartsMap - ); + emit info( requestData, m_allChartsMap ); return; } default: { tLog() << Q_FUNC_INFO << "Couldn't figure out what to do with this type of request after cache miss"; - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return; } } @@ -381,11 +376,7 @@ SpotifyPlugin::chartReturned() Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); - emit info( - reply->property( "requestId" ).toUInt(), - requestData, - returnedData - ); + emit info( requestData, returnedData ); } else diff --git a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.h b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.h index b27e0cd26..084da24ee 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.h +++ b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.h @@ -56,14 +56,14 @@ public slots: void namChangedSlot( QNetworkAccessManager *nam ); protected slots: - virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); - virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data ); private: - void fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); - void fetchChartCapabilities( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); - void dataError( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData ); + void dataError( Tomahawk::InfoSystem::InfoRequestData requestData ); ChartType m_chartType; diff --git a/src/libtomahawk/infosystem/infoplugins/mac/adiumplugin.h b/src/libtomahawk/infosystem/infoplugins/mac/adiumplugin.h index c602d80fc..5f12a510f 100644 --- a/src/libtomahawk/infosystem/infoplugins/mac/adiumplugin.h +++ b/src/libtomahawk/infosystem/infoplugins/mac/adiumplugin.h @@ -41,9 +41,8 @@ public: virtual ~AdiumPlugin(); protected slots: - virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { - Q_UNUSED( requestId ); Q_UNUSED( requestData ); } @@ -52,9 +51,8 @@ protected slots: public slots: void namChangedSlot( QNetworkAccessManager* nam ); - virtual void notInCacheSlot( uint requestId, const Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) + virtual void notInCacheSlot( const Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { - Q_UNUSED( requestId ); Q_UNUSED( criteria ); Q_UNUSED( requestData ); } diff --git a/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.h b/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.h index ed5dfabf8..d5fbc4759 100644 --- a/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.h +++ b/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.h @@ -38,17 +38,15 @@ public: virtual void namChangedSlot( QNetworkAccessManager* ) {} protected slots: - virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { - Q_UNUSED( requestId ); Q_UNUSED( requestData ); } virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant pushData ); - virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) + virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { - Q_UNUSED( requestId ); Q_UNUSED( criteria ); Q_UNUSED( requestData ); } diff --git a/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.cpp b/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.cpp index 3e1fa3466..7725b68f2 100644 --- a/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.cpp @@ -432,9 +432,8 @@ MprisPlugin::Stop() // InfoPlugin Methods void -MprisPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +MprisPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { - Q_UNUSED( requestId ); Q_UNUSED( requestData ); qDebug() << Q_FUNC_INFO; diff --git a/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.h b/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.h index c708e2a23..81d8d1507 100644 --- a/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.h +++ b/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.h @@ -119,9 +119,8 @@ public: public slots: void namChangedSlot( QNetworkAccessManager* /*nam*/ ) {} // unused - virtual void notInCacheSlot( uint requestId, const Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) + virtual void notInCacheSlot( const Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { - Q_UNUSED( requestId ); Q_UNUSED( criteria ); Q_UNUSED( requestData ); } @@ -143,7 +142,7 @@ public slots: protected slots: - void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input ); private slots: diff --git a/src/libtomahawk/infosystem/infosystem.cpp b/src/libtomahawk/infosystem/infosystem.cpp index 5fa2325bb..c9b814968 100644 --- a/src/libtomahawk/infosystem/infosystem.cpp +++ b/src/libtomahawk/infosystem/infosystem.cpp @@ -74,8 +74,8 @@ InfoSystem::InfoSystem( QObject *parent ) connect( TomahawkSettings::instance(), SIGNAL( changed() ), SLOT( newNam() ) ); - connect( m_cache.data(), SIGNAL( info( uint, Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), - m_worker.data(), SLOT( infoSlot( uint, Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), Qt::UniqueConnection ); + connect( m_cache.data(), SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), + m_worker.data(), SLOT( infoSlot( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), Qt::UniqueConnection ); connect( m_worker.data(), SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), this, SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), Qt::UniqueConnection ); diff --git a/src/libtomahawk/infosystem/infosystem.h b/src/libtomahawk/infosystem/infosystem.h index ebef34a42..ff40c674a 100644 --- a/src/libtomahawk/infosystem/infosystem.h +++ b/src/libtomahawk/infosystem/infosystem.h @@ -32,6 +32,7 @@ #include #include "dllmacro.h" +#include "utils/tomahawkutils.h" class QNetworkAccessManager; @@ -43,28 +44,29 @@ class InfoSystemCache; class InfoSystemWorker; enum InfoType { // as items are saved in cache, mark them here to not change them - InfoTrackID = 0, - InfoTrackArtist = 1, - InfoTrackAlbum = 2, - InfoTrackGenre = 3, - InfoTrackComposer = 4, - InfoTrackDate = 5, - InfoTrackNumber = 6, - InfoTrackDiscNumber = 7, - InfoTrackBitRate = 8, - InfoTrackLength = 9, - InfoTrackSampleRate = 10, - InfoTrackFileSize = 11, - InfoTrackBPM = 12, - InfoTrackReplayGain = 13, - InfoTrackReplayPeakGain = 14, - InfoTrackLyrics = 15, - InfoTrackLocation = 16, - InfoTrackProfile = 17, - InfoTrackEnergy = 18, - InfoTrackDanceability = 19, - InfoTrackTempo = 20, - InfoTrackLoudness = 21, + InfoNoInfo = 0, //WARNING: *ALWAYS* keep this first! + InfoTrackID = 1, + InfoTrackArtist = 2, + InfoTrackAlbum = 3, + InfoTrackGenre = 4, + InfoTrackComposer = 5, + InfoTrackDate = 6, + InfoTrackNumber = 7, + InfoTrackDiscNumber = 8, + InfoTrackBitRate = 9, + InfoTrackLength = 10, + InfoTrackSampleRate = 11, + InfoTrackFileSize = 12, + InfoTrackBPM = 13, + InfoTrackReplayGain = 14, + InfoTrackReplayPeakGain = 15, + InfoTrackLyrics = 16, + InfoTrackLocation = 17, + InfoTrackProfile = 18, + InfoTrackEnergy = 19, + InfoTrackDanceability = 20, + InfoTrackTempo = 21, + InfoTrackLoudness = 22, InfoArtistID = 25, InfoArtistName = 26, @@ -120,14 +122,31 @@ enum InfoType { // as items are saved in cache, mark them here to not change the InfoNotifyUser = 100, - InfoNoInfo = 101 //WARNING: *ALWAYS* keep this last! + InfoLastInfo = 101 //WARNING: *ALWAYS* keep this last! }; struct InfoRequestData { + quint64 requestId; QString caller; Tomahawk::InfoSystem::InfoType type; QVariant input; QVariantMap customData; + + InfoRequestData() + : requestId( TomahawkUtils::infosystemRequestId() ) + , caller( QString() ) + , type( Tomahawk::InfoSystem::InfoNoInfo ) + , input( QVariant() ) + , customData( QVariantMap() ) + {} + + InfoRequestData( const quint64 rId, const QString &callr, const Tomahawk::InfoSystem::InfoType typ, const QVariant &inputvar, const QVariantMap &custom ) + : requestId( rId ) + , caller( callr ) + , type( typ ) + , input( inputvar ) + , customData( custom ) + {} }; typedef QMap< InfoType, QVariant > InfoTypeMap; @@ -147,15 +166,15 @@ public: QSet< InfoType > supportedPushTypes() const { return m_supportedPushTypes; } signals: - void getCachedInfo( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, qint64 newMaxAge, Tomahawk::InfoSystem::InfoRequestData requestData ); - void info( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); + void getCachedInfo( Tomahawk::InfoSystem::InfoStringHash criteria, qint64 newMaxAge, Tomahawk::InfoSystem::InfoRequestData requestData ); + void info( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); void updateCache( Tomahawk::InfoSystem::InfoStringHash criteria, qint64 maxAge, Tomahawk::InfoSystem::InfoType type, QVariant output ); protected slots: - virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) = 0; + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) = 0; virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data ) = 0; - virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) = 0; + virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) = 0; virtual void namChangedSlot( QNetworkAccessManager *nam ) = 0; diff --git a/src/libtomahawk/infosystem/infosystemcache.cpp b/src/libtomahawk/infosystem/infosystemcache.cpp index 7ded3a9a0..7ee11a4cf 100644 --- a/src/libtomahawk/infosystem/infosystemcache.cpp +++ b/src/libtomahawk/infosystem/infosystemcache.cpp @@ -75,7 +75,7 @@ InfoSystemCache::doUpgrade( uint oldVersion, uint newVersion ) { qDebug() << Q_FUNC_INFO << "Wiping cache"; - for ( int i = 0; i <= InfoNoInfo; i++ ) + for ( int i = InfoNoInfo; i <= InfoLastInfo; i++ ) { InfoType type = (InfoType)(i); const QString cacheDirName = m_cacheBaseDir + QString::number( (int)type ); @@ -96,7 +96,7 @@ InfoSystemCache::pruneTimerFired() qDebug() << Q_FUNC_INFO << "Pruning infosystemcache"; qlonglong currentMSecsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); - for ( int i = 0; i <= InfoNoInfo; i++ ) + for ( int i = InfoNoInfo; i <= InfoLastInfo; i++ ) { InfoType type = (InfoType)(i); QHash< QString, QString > fileLocationHash = m_fileLocationCache[type]; @@ -121,7 +121,7 @@ InfoSystemCache::pruneTimerFired() void -InfoSystemCache::getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, qint64 newMaxAge, Tomahawk::InfoSystem::InfoRequestData requestData ) +InfoSystemCache::getCachedInfoSlot( Tomahawk::InfoSystem::InfoStringHash criteria, qint64 newMaxAge, Tomahawk::InfoSystem::InfoRequestData requestData ) { QObject* sendingObj = sender(); const QString criteriaHashVal = criteriaMd5( criteria ); @@ -133,7 +133,7 @@ InfoSystemCache::getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoSt { //We already know of some values, so no need to re-read the directory again as it's already happened qDebug() << Q_FUNC_INFO << "notInCache -- filelocationhash empty"; - notInCache( sendingObj, requestId, criteria, requestData ); + notInCache( sendingObj, criteria, requestData ); return; } @@ -143,7 +143,7 @@ InfoSystemCache::getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoSt { //Dir doesn't exist so clearly not in cache qDebug() << Q_FUNC_INFO << "notInCache -- dir doesn't exist"; - notInCache( sendingObj, requestId, criteria, requestData ); + notInCache( sendingObj, criteria, requestData ); return; } @@ -160,7 +160,7 @@ InfoSystemCache::getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoSt { //Still didn't find it? It's really not in the cache then qDebug() << Q_FUNC_INFO << "notInCache -- filelocationhash doesn't contain criteria val"; - notInCache( sendingObj, requestId, criteria, requestData ); + notInCache( sendingObj, criteria, requestData ); return; } } @@ -180,7 +180,7 @@ InfoSystemCache::getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoSt m_dataCache.remove( criteriaHashValWithType ); qDebug() << Q_FUNC_INFO << "notInCache -- file was stale"; - notInCache( sendingObj, requestId, criteria, requestData ); + notInCache( sendingObj, criteria, requestData ); return; } else if ( newMaxAge > 0 ) @@ -190,7 +190,7 @@ InfoSystemCache::getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoSt if ( !QFile::rename( file.canonicalFilePath(), newFilePath ) ) { qDebug() << Q_FUNC_INFO << "notInCache -- failed to move old cache file to new location"; - notInCache( sendingObj, requestId, criteria, requestData ); + notInCache( sendingObj, criteria, requestData ); return; } @@ -204,25 +204,26 @@ InfoSystemCache::getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoSt QVariant output = cachedSettings.value( "data" ); m_dataCache.insert( criteriaHashValWithType, new QVariant( output ) ); - emit info( requestId, requestData, output ); + emit info( requestData, output ); } else { - emit info( requestId, requestData, QVariant( *( m_dataCache[ criteriaHashValWithType ] ) ) ); + emit info( requestData, QVariant( *( m_dataCache[ criteriaHashValWithType ] ) ) ); } } void -InfoSystemCache::notInCache( QObject *receiver, uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) +InfoSystemCache::notInCache( QObject *receiver, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { - QMetaObject::invokeMethod( receiver, "notInCacheSlot", Q_ARG( uint, requestId ), Q_ARG( Tomahawk::InfoSystem::InfoStringHash, criteria ), Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) ); + QMetaObject::invokeMethod( receiver, "notInCacheSlot", Q_ARG( Tomahawk::InfoSystem::InfoStringHash, criteria ), Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) ); } void InfoSystemCache::updateCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, qint64 maxAge, Tomahawk::InfoSystem::InfoType type, QVariant output ) { + qDebug() << Q_FUNC_INFO; const QString criteriaHashVal = criteriaMd5( criteria ); const QString criteriaHashValWithType = criteriaMd5( criteria, type ); const QString cacheDir = m_cacheBaseDir + QString::number( (int)type ); @@ -283,7 +284,7 @@ InfoSystemCache::criteriaMd5( const Tomahawk::InfoSystem::InfoStringHash &criter md5.addData( key.toUtf8() ); md5.addData( criteria[key].toUtf8() ); } - if ( type != Tomahawk::InfoSystem::InfoNoInfo ) + if ( type != Tomahawk::InfoSystem::InfoNoInfo && type != Tomahawk::InfoSystem::InfoLastInfo ) md5.addData( QString::number( (int)type ).toUtf8() ); return md5.result().toHex(); } diff --git a/src/libtomahawk/infosystem/infosystemcache.h b/src/libtomahawk/infosystem/infosystemcache.h index 5a013d663..8910a6209 100644 --- a/src/libtomahawk/infosystem/infosystemcache.h +++ b/src/libtomahawk/infosystem/infosystemcache.h @@ -43,17 +43,17 @@ public: virtual ~InfoSystemCache(); signals: - void info( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); + void info( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); public slots: - void getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, qint64 newMaxAge, Tomahawk::InfoSystem::InfoRequestData requestData ); + void getCachedInfoSlot( Tomahawk::InfoSystem::InfoStringHash criteria, qint64 newMaxAge, Tomahawk::InfoSystem::InfoRequestData requestData ); void updateCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, qint64 maxAge, Tomahawk::InfoSystem::InfoType type, QVariant output ); private slots: void pruneTimerFired(); private: - void notInCache( QObject *receiver, uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); + void notInCache( QObject *receiver, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); void doUpgrade( uint oldVersion, uint newVersion ); const QString criteriaMd5( const Tomahawk::InfoSystem::InfoStringHash &criteria, Tomahawk::InfoSystem::InfoType type = Tomahawk::InfoSystem::InfoNoInfo ) const; diff --git a/src/libtomahawk/infosystem/infosystemworker.cpp b/src/libtomahawk/infosystem/infosystemworker.cpp index 3f9478df6..b89fee015 100644 --- a/src/libtomahawk/infosystem/infosystemworker.cpp +++ b/src/libtomahawk/infosystem/infosystemworker.cpp @@ -51,7 +51,6 @@ namespace InfoSystem InfoSystemWorker::InfoSystemWorker() : QObject() - , m_nextRequest( 0 ) { // qDebug() << Q_FUNC_INFO; @@ -119,17 +118,17 @@ InfoSystemWorker::init( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache> cac { connect( plugin.data(), - SIGNAL( info( uint, Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), + SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), this, - SLOT( infoSlot( uint, Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), + SLOT( infoSlot( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), Qt::UniqueConnection ); connect( plugin.data(), - SIGNAL( getCachedInfo( uint, Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) ), + SIGNAL( getCachedInfo( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) ), cache.data(), - SLOT( getCachedInfoSlot( uint, Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) ) + SLOT( getCachedInfoSlot( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) ) ); connect( plugin.data(), @@ -194,7 +193,7 @@ InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, ui continue; foundOne = true; - uint requestId = ++m_nextRequest; + quint64 requestId = requestData.requestId; m_requestSatisfiedMap[ requestId ] = false; if ( timeoutMillis != 0 ) { @@ -212,7 +211,7 @@ InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, ui data->customData = requestData.customData; m_savedRequestMap[ requestId ] = data; - QMetaObject::invokeMethod( ptr.data(), "getInfo", Qt::QueuedConnection, Q_ARG( uint, requestId ), Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) ); + QMetaObject::invokeMethod( ptr.data(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) ); } if ( !foundOne ) @@ -237,10 +236,12 @@ InfoSystemWorker::pushInfo( QString caller, InfoType type, QVariant input ) void -InfoSystemWorker::infoSlot( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ) +InfoSystemWorker::infoSlot( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ) { // qDebug() << Q_FUNC_INFO << "with requestId" << requestId; + quint64 requestId = requestData.requestId; + if ( m_dataTracker[ requestData.caller ][ requestData.type ] == 0 ) { // qDebug() << Q_FUNC_INFO << "Caller was not waiting for that type of data!"; @@ -285,7 +286,7 @@ InfoSystemWorker::checkTimeoutsTimerFired() qint64 currTime = QDateTime::currentMSecsSinceEpoch(); Q_FOREACH( qint64 time, m_timeRequestMapper.keys() ) { - Q_FOREACH( uint requestId, m_timeRequestMapper.values( time ) ) + Q_FOREACH( quint64 requestId, m_timeRequestMapper.values( time ) ) { if ( time < currTime ) { diff --git a/src/libtomahawk/infosystem/infosystemworker.h b/src/libtomahawk/infosystem/infosystemworker.h index 347861dd9..a190dd962 100644 --- a/src/libtomahawk/infosystem/infosystemworker.h +++ b/src/libtomahawk/infosystem/infosystemworker.h @@ -60,7 +60,7 @@ public slots: void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, uint timeoutMillis, bool allSources ); void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input ); - void infoSlot( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); + void infoSlot( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); void newNam(); @@ -73,7 +73,7 @@ private: QList< InfoPluginPtr > determineOrderedMatches( const InfoType type ) const; QHash< QString, QHash< InfoType, int > > m_dataTracker; - QMultiMap< qint64, uint > m_timeRequestMapper; + QMultiMap< qint64, quint64 > m_timeRequestMapper; QHash< uint, bool > m_requestSatisfiedMap; QHash< uint, InfoRequestData* > m_savedRequestMap; @@ -85,8 +85,6 @@ private: QWeakPointer< QNetworkAccessManager> m_nam; - uint m_nextRequest; - QTimer m_checkTimeoutsTimer; }; diff --git a/src/libtomahawk/utils/dropjobnotifier.cpp b/src/libtomahawk/utils/dropjobnotifier.cpp index c104ada55..219f51215 100644 --- a/src/libtomahawk/utils/dropjobnotifier.cpp +++ b/src/libtomahawk/utils/dropjobnotifier.cpp @@ -51,8 +51,8 @@ DropJobNotifier::DropJobNotifier( QPixmap servicePixmap, QString service, DropJo DropJobNotifier::DropJobNotifier( QPixmap pixmap, DropJob::DropType type ) : JobStatusItem() - , m_pixmap( pixmap ) , m_job( 0 ) + , m_pixmap( pixmap ) { init( type ); } diff --git a/src/libtomahawk/utils/tomahawkutils.cpp b/src/libtomahawk/utils/tomahawkutils.cpp index e6a2ae830..31cd625f8 100644 --- a/src/libtomahawk/utils/tomahawkutils.cpp +++ b/src/libtomahawk/utils/tomahawkutils.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -64,6 +65,8 @@ namespace TomahawkUtils static int s_headerHeight = 0; +static quint64 s_infosystemRequestId = 0; +static QMutex s_infosystemRequestIdMutex; #ifdef Q_WS_MAC QString @@ -690,4 +693,13 @@ removeDirectory( const QString& dir ) } +quint64 infosystemRequestId() +{ + QMutexLocker locker( &s_infosystemRequestIdMutex ); + quint64 result = s_infosystemRequestId; + s_infosystemRequestId++; + return result; +} + + } // ns diff --git a/src/libtomahawk/utils/tomahawkutils.h b/src/libtomahawk/utils/tomahawkutils.h index 30656d01c..543a0907e 100644 --- a/src/libtomahawk/utils/tomahawkutils.h +++ b/src/libtomahawk/utils/tomahawkutils.h @@ -106,6 +106,8 @@ namespace TomahawkUtils DLLEXPORT void setHeaderHeight( int height ); DLLEXPORT bool removeDirectory( const QString& dir ); + + DLLEXPORT quint64 infosystemRequestId(); } #endif // TOMAHAWKUTILS_H From 16f49b58a8f59e882bcf22717b6b3e4fcd5ee818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Tue, 25 Oct 2011 15:13:17 +0200 Subject: [PATCH 010/109] Menumerge as to TWK-213 --- src/tomahawkwindow.ui | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/tomahawkwindow.ui b/src/tomahawkwindow.ui index 57f53bbbc..97fdca390 100644 --- a/src/tomahawkwindow.ui +++ b/src/tomahawkwindow.ui @@ -67,7 +67,7 @@ 0 0 1000 - 22 + 20 @@ -78,12 +78,16 @@ - &Music Player + &Tomahawk + + + + @@ -112,17 +116,7 @@ - - - &Playlist - - - - - - - From ecf70e7b86591c05ea6ee580749e4fd568ba7697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Tue, 25 Oct 2011 17:02:40 +0200 Subject: [PATCH 011/109] Call the mainmenu something else on Mac --- src/tomahawkwindow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tomahawkwindow.cpp b/src/tomahawkwindow.cpp index 9203e65ce..584204717 100644 --- a/src/tomahawkwindow.cpp +++ b/src/tomahawkwindow.cpp @@ -180,6 +180,8 @@ TomahawkWindow::applyPlatformTweaks() setUnifiedTitleAndToolBarOnMac( true ); delete ui->hline1; delete ui->hline2; + /// Mac users allready have Tomahawk appmenu, change the name + ui->menuApp->setTitle( "&Music Player" ); #else ui->hline1->setStyleSheet( "border: 1px solid gray;" ); ui->hline2->setStyleSheet( "border: 1px solid gray;" ); From 3dbb4fad412f9d09b7c690f965614f2f7fee31d3 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Tue, 25 Oct 2011 14:37:35 -0400 Subject: [PATCH 012/109] Don't crash when dragging to the queue on osx --- src/libtomahawk/playlist/playlistmodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/playlist/playlistmodel.cpp b/src/libtomahawk/playlist/playlistmodel.cpp index c8a2b61dc..14bf12bae 100644 --- a/src/libtomahawk/playlist/playlistmodel.cpp +++ b/src/libtomahawk/playlist/playlistmodel.cpp @@ -384,7 +384,7 @@ PlaylistModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int r // so check if the drag originated in this playlist to determine whether or not to copy #ifdef Q_WS_MAC if ( !data->hasFormat( "application/tomahawk.playlist.id" ) || - data->data( "application/tomahawk.playlist.id" ) != m_playlist->guid() ) + ( !m_playlist.isNull() && data->data( "application/tomahawk.playlist.id" ) != m_playlist->guid() ) ) dj->setDropAction( DropJob::Append ); #else if ( action & Qt::CopyAction ) From a75e827d62aefb960dccd2f3d4e3fbb6a6ad479d Mon Sep 17 00:00:00 2001 From: vinzv Date: Wed, 26 Oct 2011 13:22:31 +0300 Subject: [PATCH 013/109] checked and completed translation except a few tags. --- lang/tomahawk_de.ts | 327 ++++++++++++++++++++++---------------------- 1 file changed, 167 insertions(+), 160 deletions(-) diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index 9a4aebce4..8f079e275 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -6,13 +6,13 @@ Album - Album + Album All albums from %1 - Alle Alben von %1 + Alle Alben von %1 @@ -20,27 +20,27 @@ Form - + Prev - + Zurück Play - + Abspielen Pause - + Pause Next - + Weiter @@ -50,47 +50,47 @@ Artist - + Künstler Album - + Album Owner - + Eigentümer Time - + Zeit Time Left - + Zeit verbleibend Shuffle - + Zufall Repeat - + Wiederholen Low - + Niedrig High - + Hoch @@ -98,7 +98,7 @@ Clear - Leeren + Leeren @@ -106,12 +106,12 @@ Your Collection - Deine Sammlung + Deine Sammlung Collection of %1 - Deine Sammlung von %1 + Deine Sammlung von %1 @@ -160,7 +160,7 @@ InfoBar - + Infoleiste @@ -195,12 +195,12 @@ Authorize User - Benutzer authorisieren + Benutzer authorisieren Do you want to grant <b>%1</b> access to your Collection? - Willst du <b>%1</b> wirklich den Zugriff auf deine Sammlung erlauben? + Willst du <b>%1</b> wirklich den Zugriff auf deine Sammlung erlauben? @@ -236,7 +236,7 @@ %1 tracks - %1 Stücke + %1 Stücke @@ -244,12 +244,12 @@ All available tracks - Alle verfügbaren Stücke + Alle verfügbaren Stücke All available albums - Alle verfügbaren Alben + Alle verfügbaren Alben @@ -257,12 +257,12 @@ A playlist by %1 - Eine Playliste von %1 + Eine Playliste von %1 you - dir + dir @@ -290,7 +290,7 @@ This playlist is currently empty. Add some tracks to it and enjoy the music! - Die Playliste ist derzeit leer. Füge einige Stücke hinzu und genieße die Musik! + Diese Playliste ist derzeit leer. Füge einige Stücke hinzu und genieße die Musik! @@ -298,32 +298,32 @@ Proxy Settings - Proxy-Einstellungen + Proxy-Einstellungen Host - Rechnername + Rechnername Port - Port + Port User - Benutzer + Benutzer Password - Passwort + Passwort Type - Typ + Typ @@ -332,12 +332,12 @@ Click to show queue - Klicke hier, um die Warteschlange anzuzeigen + Klicke hier, um die Warteschlange anzuzeigen Click to hide queue - Klicke hier, um die Warteschlange auszublenden + Klicke hier, um die Warteschlange auszublenden @@ -345,7 +345,7 @@ Search - Suchen + Suchen @@ -354,7 +354,7 @@ Music Player Settings Übersetzung eher dürftig - Einstellungen für das Musikabspielprogramm + Einstellungen für das Musikabspielprogramm @@ -375,7 +375,7 @@ Advanced Jabber Settings - Erweiterte Einstellungen für Jabber + Erweiterte Einstellungen für Jabber @@ -565,7 +565,7 @@ Super Collection - Komplettsammlung + Komplettsammlung @@ -573,7 +573,7 @@ Form - + Form @@ -586,67 +586,67 @@ Off - + Aus Info - + Info Super Collection - Komplettsammlung + Komplettsammlung All available tracks - Alle verfügbaren Stücke + Alle verfügbaren Stücke Idle - + Warte %L1 tracks - %L1 Stücke + %L1 Stücke Checking - Teste + Überprüfe Fetching - Hole + Sammle Parsing - Parse + Parse Saving - Speichere + Speichere Synced - Synchronisiert + Synchronisiert Scanning (%L1 tracks) - Durchsuche (%L1 Stücke) + Durchsuche (%L1 Stücke) Offline - Nicht Verbunden + Nicht verbunden @@ -672,7 +672,7 @@ Click to collapse - Klicken zum Zusammenfalten + Klicken um einzuklappen @@ -727,7 +727,7 @@ Bitte ändere den Filter oder versuche es erneut. is - ist + ist @@ -737,7 +737,7 @@ Bitte ändere den Filter oder versuche es erneut. Less - Kleiner + Kleiner @@ -747,178 +747,180 @@ Bitte ändere den Filter oder versuche es erneut. More - Größer + Größer 0 BPM - + 0 BPM 500 BPM - + 500 BPM 0 secs - 0 s + 0 sec 3600 secs - 3600 s + 3600 sec -100 dB - + -100 dB 100 dB - + 100 dB Major - Dur + Dur Minor - Moll + Moll C - C + C C Sharp - Cis + Cis D - D + D E Flat - Es + Es E - E + E F - F + F F Sharp - Fis + Fis G - G + G A Flat - As + As A - A + A B Flat - stimmt das? - B + B B - H + H Ascending - Aufsteigend + Aufsteigend Descending - Absteigend + Absteigend Tempo - Tempo + Tempo Duration - Dauer + Dauer Loudness - Lautstärke + Lautstärke Artist Familiarity - + nicht sicher ob das passtÄhnlichkeit Artist Hotttnesss + keine ahnung Song Hotttnesss + keine ahnung Latitude - Breitengrad + Breitengrad Longitude - Längengrad + Längengrad Mode - Modus + Modus Key - Schlüssel + Schlüssel Energy - Energie + Energie Danceability - Tanzbarkeit + Tanzbarkeit @@ -926,97 +928,100 @@ Bitte ändere den Filter oder versuche es erneut. Steer this station: - Steuere diese Station: + Steuere diese Station: Takes effect on track change - Wird nach dem Wechsel eines Stückes aktiv + Wird nach dem Wechsel eines Stückes aktiv Much less - Viel Weniger + Viel weniger Less - Weniger + Weniger A bit less - Etwas Weniger + Etwas weniger Keep at current - So belassen + So belassen A bit more - Etwas Mehr + Etwas mehr More - Mehr + Mehr Much more - Viel Mehr + Viel mehr Tempo - Tempo + Tempo Loudness - Lautstärke + Lautstärke Danceability - Tanzbarkeit + Tanzbarkeit Energy - Energie + Energie Song Hotttnesss + keine ahnung Artist Hotttnesss + keine ahnung Artist Familiarity - + nicht sicher ob das stimmt + Ähnlichkeit Künstler By Description - Von der Beschreibung + Von der Beschreibung Enter a description - Gib eine Beschreibung ein + Gib eine Beschreibung ein Reset all steering commands - Setze alle Steuerkommandos zurück + Setze alle Steuerkommandos zurück @@ -1025,27 +1030,27 @@ Bitte ändere den Filter oder versuche es erneut. Scanning (%L1 tracks) - Scanne (%L1 Stücke) + Scanne (%L1 Stücke) Checking - Teste + Überprüfe Fetching - Hole + Sammle Parsing - Parse + Parse Saving - Speichere + Speichere @@ -1053,37 +1058,37 @@ Bitte ändere den Filter oder versuche es erneut. Play - Abspielen + Abspielen Pause - Pause + Pause Stop - Anhalten + Anhalten Previous Track - Vorheriges Stück + Vorheriges Stück Next Track - Nächstes Stück + Nächstes Stück Quit - Verlassen + Verlassen Currently not playing. - Derzeit wird nichts gespielt. + Derzeit wird nichts gespielt. @@ -1142,7 +1147,7 @@ Bitte ändere den Filter oder versuche es erneut. Re&scan Collection... - Sammlung neu&laden… + Sammlung neu&laden… @@ -1188,7 +1193,7 @@ Bitte ändere den Filter oder versuche es erneut. Check for updates... - Suche nach Updates… + Suche nach Updates… @@ -1203,7 +1208,7 @@ Bitte ändere den Filter oder versuche es erneut. Home - + Anfang @@ -1245,7 +1250,7 @@ Bitte ändere den Filter oder versuche es erneut. <h2><b>Tomahawk %1</h2>Copyright 2010, 2011<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Harald Sitter and Steve Robertson - + <h2><b>Tomahawk %1</h2>Copyright 2010, 2011<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Danke an: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Harald Sitter und Steve Robertson @@ -1253,47 +1258,47 @@ Bitte ändere den Filter oder versuche es erneut. Form - + Form 0 Sources - + 0 Quellen 0 Tracks - + 0 Titel 0 Artists - + 0 Künstler 0 Shown - + 0 angezeigt Tracks - Stücke + Stücke Artists - Künstler + Künstler Sources - Quellen + Quellen Shown - Angezeigt + Angezeigt @@ -1301,47 +1306,47 @@ Bitte ändere den Filter oder versuche es erneut. Artist - Künstler + Künstler Track - Titel + Titel Album - Album + Album Duration - Spieldauer + Spieldauer Bitrate - Bitrate + Bitrate Age - Alter + Alter Year - Jahr + Jahr Size - Größe + Größe Origin - Quelle + Quelle @@ -1349,7 +1354,7 @@ Bitte ändere den Filter oder versuche es erneut. Sorry, your filter '%1' did not match any results. - Entschuldige, dein Filter '%1' erzeugte keine Ergebnisse. + Entschuldige, dein Filter '%1' erzeugte keine Ergebnisse. @@ -1375,59 +1380,61 @@ Bitte ändere den Filter oder versuche es erneut. Authenticating with Twitter allows you to discover and play music from your Twitter friends running Tomahawk. - + Die Authentifizierung mit Twitter ermöglicht es dir Musik von den Twitter-Freunden zu hören, die Tomahawk verwenden This feature works best when you have set a static host name in the "Network" settings tab under Advanced Settings, but may work even if you do not. Tomahawk uses Direct Messages and this will only work when both Twitter users have followed each other. - + Dies funktioniert am Besten, wenn du einen statischen Hostnamen im "Netzwerk"-Tab unter Erweiterte Netzwerkeinstellungen eingetragen hast. Es kann aber auch ohne funktionieren. Tomahawk verwendet Direktnachrichten, was voraussetzt, dass sich beide Twitternutzer gegenseitig folgen. Status: No saved credentials - + Status: Keine gespeicherten Zugangsdaten Authenticate with Twitter - + Mit Twitter authentifizieren Here's how it works: just press one of the buttons below to tweet "Got Tomahawk?" and some necessary information. Then be (very) patient. Twitter is an asynchronous protocol so it can take a bit! If connections to peers seem to have been lost, just press the appropriate button again to re-post a tweet for resynchronization. - + So geht's: klicke auf einen der Buttons unten um "Got Tomahawk?" und einige notwendige Informationen zu twittern. Dann heisst es (sehr) geduldig sein. Twitter ist ein asynchrones Protokoll, also kann es etwas dauern! + +Sollte die Verbindung zu anderen Gegenstellen verloren gehen, klicke einfach erneut auf den entsprechenden Button um einen Tweet zur Resynchronisierung zu senden. Select the kind of tweet you would like, then press the button to post it: - + Wähle die gewünschte Tweet-Form aus und klicke auf den Button um ihn zu senden: Global Tweet - + Globaler Tweet @Mention - + @Erwähnung Direct Message - + Direktnachricht e.g. @tomahawkplayer - + z.B. @tomahawkplayer Tweet! - + Twittern! @@ -1458,27 +1465,27 @@ If connections to peers seem to have been lost, just press the appropriate butto New Playlist - Neue Playliste + Neue Playliste Failed to save tracks - Konnte Stücke nicht abspeichern + Konnte Stücke nicht abspeichern Some tracks in the playlist do not contain an artist and a title. They will be ignored. - Einige Stücke in der Playliste enthalten weder Künstler noch Titel. Diese werden ignoriert. + Einige Stücke in der Playliste enthalten weder Künstler noch Titel. Diese werden ignoriert. XSPF Error - XSPF-Fehler + XSPF-Fehler This is not a valid XSPF playlist. - Dies ist keine valide XSPF-Playliste. + Dies ist keine gültige XSPF-Playliste. - + \ No newline at end of file From 6ec80ee427c9ea352a7c46e51962acb1415c4a66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Wed, 26 Oct 2011 14:06:54 +0200 Subject: [PATCH 014/109] Fix for fail --- lang/tomahawk_de.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index 8f079e275..558d60af2 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -636,7 +636,7 @@ Synced - Synchronisiert + Synchronisiert @@ -877,7 +877,7 @@ Bitte ändere den Filter oder versuche es erneut. Artist Familiarity - nicht sicher ob das passtnicht sicher ob das passt Ähnlichkeit From e341120f64f67df30cf6f8cfe7873165b8b70fb0 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Wed, 26 Oct 2011 14:01:03 -0400 Subject: [PATCH 015/109] Finish making InfoSystem accept a requestId that clients can use for tracking --- src/libtomahawk/infosystem/infosystem.h | 3 +++ src/libtomahawk/infosystem/infosystemworker.cpp | 12 +++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/infosystem/infosystem.h b/src/libtomahawk/infosystem/infosystem.h index ff40c674a..2e95aff1f 100644 --- a/src/libtomahawk/infosystem/infosystem.h +++ b/src/libtomahawk/infosystem/infosystem.h @@ -127,6 +127,7 @@ enum InfoType { // as items are saved in cache, mark them here to not change the struct InfoRequestData { quint64 requestId; + quint64 internalId; //do not assign to this; it may get overwritten by the InfoSystem QString caller; Tomahawk::InfoSystem::InfoType type; QVariant input; @@ -134,6 +135,7 @@ struct InfoRequestData { InfoRequestData() : requestId( TomahawkUtils::infosystemRequestId() ) + , internalId( TomahawkUtils::infosystemRequestId() ) , caller( QString() ) , type( Tomahawk::InfoSystem::InfoNoInfo ) , input( QVariant() ) @@ -142,6 +144,7 @@ struct InfoRequestData { InfoRequestData( const quint64 rId, const QString &callr, const Tomahawk::InfoSystem::InfoType typ, const QVariant &inputvar, const QVariantMap &custom ) : requestId( rId ) + , internalId( TomahawkUtils::infosystemRequestId() ) , caller( callr ) , type( typ ) , input( inputvar ) diff --git a/src/libtomahawk/infosystem/infosystemworker.cpp b/src/libtomahawk/infosystem/infosystemworker.cpp index b89fee015..95cf8e63b 100644 --- a/src/libtomahawk/infosystem/infosystemworker.cpp +++ b/src/libtomahawk/infosystem/infosystemworker.cpp @@ -173,7 +173,7 @@ InfoSystemWorker::determineOrderedMatches( const InfoType type ) const void InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, uint timeoutMillis, bool allSources ) { -// qDebug() << Q_FUNC_INFO; + //qDebug() << Q_FUNC_INFO << "type is " << requestData.type << " and allSources = " << (allSources ? "true" : "false" ); QList< InfoPluginPtr > providers = determineOrderedMatches( requestData.type ); if ( providers.isEmpty() ) @@ -193,7 +193,13 @@ InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, ui continue; foundOne = true; - quint64 requestId = requestData.requestId; + + if ( allSources ) + requestData.internalId = TomahawkUtils::infosystemRequestId(); + else + requestData.internalId = requestData.requestId; + + quint64 requestId = requestData.internalId; m_requestSatisfiedMap[ requestId ] = false; if ( timeoutMillis != 0 ) { @@ -240,7 +246,7 @@ InfoSystemWorker::infoSlot( Tomahawk::InfoSystem::InfoRequestData requestData, Q { // qDebug() << Q_FUNC_INFO << "with requestId" << requestId; - quint64 requestId = requestData.requestId; + quint64 requestId = requestData.internalId; if ( m_dataTracker[ requestData.caller ][ requestData.type ] == 0 ) { From 746d24da50debe7d1b285a60a994a204639bd41e Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 27 Oct 2011 08:43:04 -0400 Subject: [PATCH 016/109] shrink icon size --- data/images/charts.png | Bin 1054090 -> 4510 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/data/images/charts.png b/data/images/charts.png index f0e2f9472a95842089f790a5900d15955d689224..6e56456a2c2ca0c4f6f29b97048b68686d5c8aac 100644 GIT binary patch literal 4510 zcmai$0r!jRT;I>6$#P5*(Gl|@Xw%b zFoXvHkkS2DARsfF2>?j798^?vb?sd}T|Mkw-MBPVRJh#SU2PqlZ2-V`I!DjPIQfJ@ ze*VOU%20U{Uz6?}0`Tx+NcyP}h1Bl=%Qw`tiap^`q=vWXX|iDk-J#>8u$-u+{5x1X zrRb;^F$ShmyPUJ7p9B5&c6Kg)U9_%euU`(MMySv;V8g0m?VXvs&R2^vM+f18(E@Z`1ZI-h?&l@*4qMNx%XR?K&AK z5f1d<*YCdt^fLj~dUdZzffN96Mg=JI02LI#_K}7>Auw5`w(0e2#shER|lip2m$YS<3uV&3jm#=EHnskNr3?s4!R)% zE=_`b(Xoz&a&xzEEDQwT22zL8pN&1Zg-!j=;&XssSmIv#;#{L_$#|FA7P`Wdou0)@ z2U{o593}?(A7y?jhkL*pGudYrI zxb=amy*ixAO@L`bz<&PB?7<8){{`uHzq1&W)TJUvZh>z9V|F%DGO`^$tAaMsMYRL|M$`WS#fAFFg1v{_g0KM2)Es zee}NZTdXdRRv)Sfp7ezKYw8m1X0Z4^XWA7s*@ptaMCjnO8;Cy-IOBqLLEsol<1ueQ zG3Zb@mW3<{pth3`N1Xsba>rSMq96zuN-tgpfYuEr)3Ovs&0Z1!c#?Y zN15Hp-il)cTM|7{B)^S&Os!0`f@UKvU`kVB*2l?ALOxd$u;kNZE>hQbib+wdFVLEX zY`HNKSuo{(VQP(lLX`SR=q$NqqG+t*CwYcR7&GD_JeDwtcj#iS&8SYaGhbAVf8*3V?l<{rz*3&kV7(v6{h@NiNf==kudI>-M#h zsYx_e!|?BNb8zSKy%eCNGf#%yru_PDMVpkHFHu~BOv4E_0~0RRE0!OlcF5M`-%3P& z{k=j`i^g!JCp9KgCF;Dp_}aD7cuS@yYDp7XcpF}vLi_ZgjlwGAL!P*DMt;Tfq4M0H zBo$FIc`r1O`QExBdXeRH5?+~nLs<+e1I5Bc>N()MK(Q~p>tszs_Ea;$DB5Z zH`h1Aj(o@|A}GT8U{-gSotXWZOPLy(MKUx?2!Et!^y`cM5@*e@dr$S=)xh}P-+KY{ zN#AHwWl{}OgHqQk^bI@=6vj~%WcnWss6M_l3@lMIsMB38hyG;vI9|?f&{8g0H4OKG zC+LnmH!o^8zY0CKrl^ewSIdup73o_y_A>DdjxEm5|2|2yjvNEYX+t&0Z zykPYf@z>N-@u;xH*e2QLti2urQTEm>;H9UspAK^mQw;kH69_8^*GxE8+ExZtKAQHM zZlAW=ao=Is*_z(-a?4T75z0B1LCTzXL7OF;pErj$&v=dek^f=6h1!H3e>Iz6_uauA;>?k?_GX}>#^;*@FIJX-(5b8xU&Okdh`TEGBdnpWBg=Dnf3A*?aOV_?aXbW9qGT8fBF6*|BkQ^ zveB|n$a=~KxO;jHdON$dZWS*|bd`6PcWoNkvDy`Q)M$jHmtoDXjj$oI;F^ZfE6+e_Mc=H=RX zgC>G9f`~73FJ#XLt`bf=7EuQb``UySpia;#s4avgq>jLefD@7d`9S=TFpjW=NQ7LT zUYQ(9f#7sccEvFsu%1~zn6(qi9Hsw8l0*N3^;~q2Uz1~kCO%?>WS%<6wd725bFhIV zYC+vO3d`xhoWXb&c^ zm70?cR5`bg&{6a1eE`*g3!;*k-!<_bY8gJ6_>CEvdOam9osylBT?D6aNUIn8CE9qW zQO8zLFqvWnwTAr0njHDrngLeWw7U%K5k+de2+2J9OH&5t2(V# z!>%nEX`7D+9HWra)4!iKe%slCZ5401U``MnpPsfkDvf`Sz*AJR?#HdNnj@e7_AKyo zJdPi#ANn>#@X+$%j>%vHX6B$kvgog0PO#a4;Z^0w#`Y=x>4}axAOAzjFm`&`-+L+B zt)B&bjhxz41CL$Cc0=b9Sgcr^JJy2cuvYymrq5@$7xyT?D6Mu1%$EfCFY9g;40p|T4b5i+Hy)F0!BB!@G96wR z;UZ+dTtVC3Mr=_I`%8B9_Qso_J+WnLufB%~M!ESpcjWfZRzj1POJ9}d=4dO({mhs} zooHTWIGH^+YqL`s$yH$V+|_I}Klyt2dp*Gn-|BY6b7D2a*Z8bbu1Zcl$n9zo_Q}L` z%jRTY$LMjD=aJTy?C9Q);B!aZxW}*4U-!-OP;Gm?+pLW){BvwrzUJdo@4q{YKBT*v zvu1t?zMn4Jr?37HpHuw_uD`Ca!66q;EpQeiT?nc@N@!&6b;`|;$i*@BLG^K7HQu;{ z98OMF3YNBmSHsDRrM0DWBj17yZ%|tvAJ^xvmM)%+7++4}$STwjWuyg9t!8_Rp} zuWfQ!!{Ax~;K%o`g@ysZ+08%S1OP8l0Qh4805b0ZfYJ4xd5;Tj)j&FD;=Nu=Uhn;zd-lz?=wpC ze+)GednIuz#Tx>jw%$P;I>-_sH`G1-Tm<&y*M{u*I5)gc=WXEBaRHc$ZY?%aPHgmvz@O?rem6mHA2a>0u_!$6Uaq(C% z_})5bLFh+i)=IMOZIrn5|)Z7~p93VS%94`Hkyrm@*o(yF;^Z=aX z`yM@am-0r@$qAaQUCzWgwb)mP=z5z%snyx~J~S2JdOw?eo*S_hmOK{eYtVexUS-8Y zh)2_1$n@JdbyPYz-K#BWiGxq20RRS9C}unY17ZX9+75vC0-710fiD$C=j&Q+8a5QjI?NZF6lsDtQyr_( zSGbR-1N8xVcgX_e+|XgX*2ZafK@8&Ohs^#Z8;{V$=&bdcETYSC`6`@w;p&IYTE&qH zhTj*?=9oIg?VrN396OcI?4qw*)(~fU4xn5@Nv#tbI}`Ts0RD-#2rB_J2xI>VOQ|B_ z=G5OZ2=>uAQ5DN8_)x&K^;KZWXJ0%&aX|mvn~b^#*=f75ve|Br6zXp7)5SpWkNc+? zdID5I=e)C5(GGzUeR&ZhIc25b*^b4@m@+!6Z5B^Q<#y!Er(NgEvknOw5HKm#PN$iRdL} zzj2S?(Hq~pzKqz{Vv=A5KpV_K98}3okwDM^q5^G&lItdQh7}Epa(IBTEC1vnQnW@&wKy>)uk6 zCez<6i*&`Nvxzq_U(|%u&|7e;!mN4()X+E4->3DD7-NkM`^JZ_gN-1jv7TnuZ{Xc&yhzRw)oo<#WKiRUqvj$@93idBQ5YC3Nb@Af~xI3&FYAi(X4bW|?%v~d%eP=@N zj8k@sUC>LtjN*t!^hL;iIaTO=5)NupXnlb|h(;HfXrB|1qlU;wq<=T4QT5ueN&Np1 dNz%UN0EA-Cw-M2x=zkFiXsBvGsZzEG{SPc`Z^r-t literal 1054090 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 From 359fd9761defe37f2d925d740ae9eba122930bc0 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 27 Oct 2011 15:08:24 -0400 Subject: [PATCH 017/109] Don't show all stars as hovered over on launch till a mouse move --- src/GetNewStuffDelegate.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/GetNewStuffDelegate.cpp b/src/GetNewStuffDelegate.cpp index 5329e0591..d2d2d98e1 100644 --- a/src/GetNewStuffDelegate.cpp +++ b/src/GetNewStuffDelegate.cpp @@ -40,6 +40,7 @@ GetNewStuffDelegate::GetNewStuffDelegate( QObject* parent ) : QStyledItemDelegate ( parent ) , m_widestTextWidth( 0 ) + , m_hoveringOver( -1 ) { m_defaultCover.load( RESPATH "images/sipplugin-online.png" ); m_ratingStarPositive.load( RESPATH "images/starred.png" ); From 2277c148829f16467a1e6a54f637c070ccaae68e Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 27 Oct 2011 17:24:39 -0400 Subject: [PATCH 018/109] Shrink some iiimages --- data/images/add.png | Bin 2035058 -> 4990 bytes data/images/drop-album.png | Bin 1054090 -> 11480 bytes data/images/drop-all-songs.png | Bin 1054090 -> 12070 bytes data/images/drop-local-songs.png | Bin 1054090 -> 11391 bytes data/images/drop-song.png | Bin 1054090 -> 7893 bytes data/images/drop-top-songs.png | Bin 1054090 -> 12061 bytes data/images/loved_playlist.png | Bin 1054090 -> 6923 bytes data/images/recently-played.png | Bin 1215800 -> 5175 bytes 8 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/images/add.png b/data/images/add.png index 14e3c7e84895304a58bcdaaa457ebaf695a91848..59fa7fb1df2685d1291e9d926ea030fc09e5fb8d 100644 GIT binary patch literal 4990 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뚹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 literal 1054090 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 diff --git a/data/images/drop-all-songs.png b/data/images/drop-all-songs.png index 5e743fa5b8c8c700f80fde5b21c6228cddf09a0a..d8d5113998b16ff3e041211534cc14c4f8143b81 100644 GIT binary patch literal 12070 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 literal 1054090 zcmeI*2bdMr-N*5{yGsXYD>l@%p`xIuRD&)83St)vb_7I}CW2ty)u6E@Mq});zt%*J z1q+F;Mq?~du}4i_h_A65j2gT9-T@EH4!6wAozv!gd7k99Dd&9tbLRZ++yT$oYmZ$z zwO_lv=Xsq*j~cO$=T+sOR(Wk(=N~?w_wqNM*YSuc!-wxRdie0VnX_gbHD&4~&zqOa zjp}&+XZg=Y?KWlljH)i4H*NatS^Mm=Q{92%4ytSOTz(7fyw+Yn&l^AC_?f%!yYtxm z=Dg7(hu0mSU&w#H^3Q)?^>Y5Jm->v_tFCV4`;ne?oiKCOY|qQ>m0x<>iIa|>kbgcu z|9tA)*)#q3m*$_>9(k<)ysE~3J}ckA{PQ*a=aZK|-*VY9%b(Z#&nHfsJ~6+H-{zSU zr%m*qKc0Vn_6c()<)2sYmVbWM2~#G`%|CxR|9s=AbEZwnKmRKKyms28@yB~!O-KLz z*^?$5oqxV{{&~k)WA_=JfBqBCYuj;h;Q5h(=d&lx%eT|>hR>LJ;;bo?kDgt(*@VsO z`VAgDsBYAxxl~1n{mD84f-N)^UIa*9eKIu-G8R% zt@*~v_cp%P^E%J>yamq(`qRTdUXGqUd*+a>wwgP4Zr>@BCiL||SNtivz=|b{KMCxw zufM+)c6B2r9W{Q=)Y)}D>Vz3nXUv&Zcl^xp6DHO52_M9XR}3^O(2gzlnRL{oS(B#c zceQ{1D4Q~Ua=zTtCr+6?WybWnDbtHpIN@r6Huz7MAJ_RO-EaKCt3AB0*X8+IulnO> zy_)VFyy`=)&VQ0yQMcXN?eF;`?|}8+U4Go>pXWbc_Wv#Zuj+#Q%c|q2Os@0a9=^}m zx(Rb;o!~#uj|u<(T6yig&R(szmbad_k++%G$J@pm=xy)Sdn3JFy*<6L-Z<|tZ-RHU zH_e;n&Gk<4PWR68&hvih{o1?2yT-fGyVbkHyW6|p`-AtW_h;`J?*;D_?@e#9_m20W zx77Qu_iZkhYm@7g>z-RHw|;Ju+!ncQa)WX^W6w zS~a=qxT=$?=2!iq>awcqtA1Oxpz6`8XR2PQT3q#E)fd%Xb%*L6)f-gzsUB24yn4^- zgQ}0Jo>hHX^|{rTR9|0xNA*M1PgcKFy}0_L>aS~B*L1I`tLam-ea)zv{c0xE99MH% z&G|J~)Z9|Dpyu(Kmui;OEUo#WRi{=xTW!&5NUPDU4s12K)!bHRwYsF$O|9;0^+c;z zTD{ZizpY!h?$LVF)&pCQYCW#?F|AK-eSYiTw7#=-Q|m>o-){Xyn>KCMYO_U~p>6hT zGrrC2HfOcDyv=XhJksXHHt)3gs%?k1>$lye?aplvYCFB{8Er3Vdt2Lw+rHTL-L~Jh z>(XwMc0=0j*=}OH6Wd+T?#6ZxwR^tZJMF%0-?jaw?T5A>+y0pLr?0Nj9#eZ>?eA)z zt6jQ!x7D{>egDf33f-U9)!YwfA0o?%G$c{lwZI zty8tDS7BkO-qw|d>Mx~X-S)jeLfbc3E7jM`w%2G?!y+y>un zxao%bZFt6pcW(IijoNQCXrrSxx_G0myZaR9?lQzA5)3-M3wAqfE9k&*vke(dJI+5Cmx zRlWQ7KDzgny`SHrYKs9|OxfZ$TP)hL)s};|oWAAtTfWw(W1nGtPUv%cpCx^J^xduR z8GY~X`^i=tZ*|aC7jE_BRzGarf9t7R-?;Ug{krwrwci>29_sg>ZF+ArVVf(rd3oDT z+wQz=!?q7>`{{ODY&U7UtG9c-fA{{o_y1Y{$NGOiVBmmR1MV2`{=kg~9x?E$fv*j! z9kkb=^9TK9aI3*P4Q?3xhrwSD88Bq_kh_Qcd;31yAG`f++rRgdO@A`!CpZ4&?V%eE z9Y6HCp^JB@+u?{EuH9krjvMSae#h%~{Ac|}^%LuFs(*Lb=EIH|_S<2f?6mbxvv#^~ zr!R*O8Gh>UrV-U6b{TQ*i04Lj8@b=eD@Oif=M8tByz}ilFWsg8E+_BOG^*98F{3UV z_1frlM^6}i>*%Gs4&3$BT^}FQam>DBt{C&r-FojfYqy7XuiAar-G8$Mik! z-_zT3^q#-k^Y43Yy4P`gE!?~H-h1tR<=+3=r{6xO?DOQl-S<6w-`n>6@7SHjUNH8} z{WjZg&VG;W-+BK-_P=%iFAo@Tz(ofvK5(l8Pd)INachs8GVXzc+8nh1K{p-r#la&F z{`JA{9Mb=gvkrOn&`l4Wcj!}xt$ocCqc1u7qbb9u zTruU-V@4fw%`smdyXUbtAKN_jfT?#*Ydh_TX%9~CHvQP?kIz_d#=IGeW^OU_teK0C z8*<#Qk6Sux%&eP^&mDj0@ej9Hr+?n>=a>Dw`4>n1;<*b3TyXU-TmN$UFJHZI#|v+{ zsOv>1UG(;^cK_9ai|Z~v_u|igefY1Rx@5acuDP`RrE@M_eA#Z7J#hKPm;dte@2)ud zidU}O>B>8=>Uq^USAF@LiNAUA>iVnixMtmJ&b#KDYo}cM`gOZpx8V9-*I#l&>l;qE z;a@lIf8&$?Gw6SAy=m>6&b#URo2TEr_?CTcdHmLax88bN&)Y7zz3TQkx4-|}Lw@_h z??(LYfjj!#aowFg?mX`<@2)v_eenAue*fy-WA1+JoKJNPQRiAA3$-V!s|M#;?4_x}rr?Wn7{?GZJt@YW>pKtg1V_)p`#XtT#?Z4lC zdG1$heRa#%gT8+9n*+Xi_uIMOwfXL{?>GPcp&!Ql@K*D*=H@TwfA;O6iyz8oW3Tm* zV|E$g2LjLgdicVI{6o8$qsHYEb%*Z$|8m|HSNYMs&Fs;;k8Ja1+cmo9*1PxJzXfB+D{;3)zKAb5J132fhDuAZY*~%fan)tKuGY200IagfPg~+1OSJM zO;-^>009IL5CQ}MA;BX82q1s}0uBif030edT}1!^1Q2jTpvRXz&yIR8fGKmY**5U^Wd zi!rB;h;uK1-&?|f-8HB62q1s}0th4$AOIv{V=qyZ*j+zbj{pJ)Ab>z3 z0RliGHdaCa0R#|0z&wGsKB~T;@Vx+y%?;(6OCl&I#ODYgfB*srWKe(rkinwTB?J&a z009Kb2@n9v3Gq1s2q1s}0vQw_0A#SJbO`|j5J12wf#)y%wo~DI0U|OG1|mYS7y<|& zfB*tn5+DF%shac!0R#|00D*`C1b~Q8EQSCA2q1t!mIMd@S*j*|K>z^+5O7Uk@DZ)v zujpO?AB-^I+F?U~5kLR|1Q5sz5CAv~5I_I{1Q2jgAm?D8=`sQcAb-qsX+MW00Mwges+d{r2=s${$kxE5EPT)eFUTn z5CEhTvjYS&CXm+TUxjOam+T zvho=TVWkDn5J*{o0FW{$n?oS80;(tfLXZT4LMpt6Kq7%~0N}3$Bw}PG1Q1Xn5DX!N z0VU+@#2o?d3UsH|^b~>U0>Ozl`dVt2BoL_eW^aZFJaS|Et4rJq5V4h94TuQEVhA8$ zxIj3t5DpA47|k16L~TbU(Qo;B@)vJOT(56bJ_&0zpA0-prN&Hw0#@p7aI*1kw;75Tv2VHVhZw zCji3>MDq|(CJ>(VlWW?0`PNy?Z*S{M*so+0^#69IM81c zS`b@+s{ygeSOI|y3WO)L40cgzmk0z(x!GAX0j>ZI1zJzoDfeHk|lTQVkVpT~31OiF0 z?roMU0PfYC9wUH&%>v<&Mj)`cn(^9x*GJX;%iRkQZ;M5j<_^K4G<<*n0tgr=KsYe4 zD0Vi9p9Acy8?8p5a)IzfQu!9yf^Gq>6zHyi(fjA;0He!8;}AdqfeHk|p^iXM0cQ4B z}t z?*#~74Im7tE-?E;zytwBVW4?Y0Q&6NGXi=9!Y6%6tq4!>ad$%?h$|*xc>;k1RDGM|3V=R) z_KW}mF$97Ef-n#RF3m;5VUa+Prf%_VO+NekXC&SWP-w?o4Jf3-dkADzAbfI{)X(5e zfiiYMIFO+lbDIQ%m|*%B4_^%+0O+p?EkHn>fT92p{3frWa|8kv%4POJASeT*|6>Ax z{u0UDbdqHVGGQsG1p{qG2tJDX7L;_7^Y00LSC!YPG>prIV$IfOYw zLlIi;viwZ};i9!%zwj#xTiAtn5zr-&WE>}GYj~nfun8@z5D2uex7;c7MoSL~ufkUV ziU)u+P8bwaLJ8R<0tp1t7|#)QYij@qc8xF))X?;txkCzkv;dIy$%0}^DJC05Ad*1Z z!x)&fN{_CQA(TSp9tNb@9SWy~2Kp?SzqH*o& zxBn=0ArM4SlDv%J^UI-6?()9MdjWiHbHji*77Q<%XeN&c7%!kG2>1?!foMlVQ&ZE( zVKbU)qb4S@{nHg%JdXNskT{#w2#yVxfSd z@f@DOER-_3$piue$|HH+IqL3hhpN68z*n{FYC!ln5GOdv#R>10*9ahhfDVCh*diQg zsDhYx#I7?m|JZdkKomecW~dONOFSY_u|W8QRq+OkZy-ED6i;56zHqQ}AT|iL)VDeABausioKu|GZ{s)=l-)}L0pn#)#feZec(_@-MgRc> zbO?k)8-YMW6~w$GcCC4SYIy~~Z&bK}qM=!M)Qp`4Li%8^fWomGo=gl@p=l8X2m}$K zWG*IojtXA^a4P_a!bwIJyWtT5r2>iq0F%Gcs%qOAfj}Dl&>!Vr0d=o%&WaWQ6rM~BAT4_vrx9(W5eSqL zVDc}e!)GZA@LxYl88qqUVg>>NKso4`pyf8l=LjefAOI*~W+#OOxH?eSMd`Z{=C0@g zz;C&tlZsNLs5^^*8Ff=Ehk#T8h2xaV|57WNnT-<&ik9-bOFn)l{=EQ2H&^C^67yKR zFyQY;;fbb9xAc5m0#i?e*ee1d0Yzh!AP{orWn=-uL1e0Q7A!Jvg#$o#{>i{}*jC+& zIJp)9U3CTCK>z^+5C{r{!y|zps3YOIKW{~tvsV45gL*U(dTnPC9}fu!Pw=Bn@&fM@~0Z=0eMjU)y+n+@U0*$G=kpoD;;aY+CuQ3F0o zT_CH0VBoz^tGgGVP*nnRRyYjsb3h@msPBmraR4~#sw{^<#RB5^t9V0f00FZEg5fi( z!N3IZ=vM>$)^frrT{D6}X&*jKS3uEt44#bB?EqUtAie;BAikNnH=wdB0R9B0=%kaN zi~3gy+7Jfx*Mk-yfI#L1!hw`Pki5k4t^kPr1SdN29*>0u2m*y&c(*bEMPo30vaPJq ztc`$t0Yd}9z^g7gC+58Xjm-_xxPF|ND!U{gt#ujRusCy*3^H6~x`qG(rU-;ntf2|P z2(Dz81pKCpiOq;^SkwrDfJMbJw5bZmUif5cs0htMAhLjyfuP70faC$d=T>+kid}@x*JO(fB*t13Dnou zFKk{^zhZ0YUmbYigpZDhc`v|<)~J0_+0_8Qt-(o6ZQHZAH$p-7YMOSB6pc5o1f*R! zwr8V2`r$y6g0!mv{)QEulr+()+pTaU2&gk>PY57@KoNm(5Y9{>__4qI98je2THhuJ zXyrF{=i<09wt+MZfouz8Ef7@x6M(2UCnq5x=aH)da$IfNs-iI#KFQfcL|YNiDbVr? z!3T%`H2)R5&Ko^q*jSyd3hX;DkI4J?0r*)UATHslmR`wf8POabV+bf3L*>Ryj25#f0tf^IihVH+ERyci66gEyPXKcN zfgryjicVm01cU!ZwDRJ_X=qx@25V5_;7DtCYzF}Z5O7!E^v9pOYt?%J%Fi{8%?-NI zGWDB)z$`B>mh?P+FIZ{8Gs6UeNzSmQ(HsO2Fi;@oH$J()2?!reC$c2hIpc%}(LJjl zV;(S8_tmz>D;hUR#))l|v=;%@0?Ox-2R-eR|MjUu{;6@ zAb^040&1=R_%e#V&4i1P#OB&0e&$eHZbkFrBMj35Tsodwub-$2xLB;P#XkUWaY)n`wmyo{r|TPXrJ^K#IUlI}ARwz`X!c_9bbf z>TV7UcOW>igg=w`IyiyL*o_mRAP7e&2x?URT+z6YJq{{|U<(M?C7}G=pWxi9?}or| zEfX|8ya^Lu`zKbXPOkohrD)PJvEH-^0R#}RRY3jy0Gg##r8I(o=0e-JdxDgWRin)a z82eWM%aumoPXMB2nofE;zNM=QAA(lvV@9v=NHtY77IYl@Qr*}%0vZK!)x)Q^P<{@t@EmI16#yTz!cGP15$mb);)Po0 z)D;20?_3cG9Ra*V009K_37ByOK=fbHw;-`Q@o3VwBHf`xm?(MF6#SI*EzV999``04 z>v4!!76H=)mYu7Af6+B<#JvEq8`#*~5PO~A3MrgG&AcQaI+;QT#ine1bm}sJxJ00E zMzr;~NTc1Pfix7^1_GY?zY-t`-MPObSjf@Dui;Jw0lz3B3=}F5?;(Hy0xk6)G&n3PWKz(x~?a|Du0e;gJeM<@} z+4QYRc`6NKseYZckf`t&x9PZ6eOR$S1fmKkKj)@=&a>lcfM1Q2J8o=&qc*I-a^ea> zfseBJ7FP%o*H!t6D{)I%5dj2L37C5YKnN)OmLzs*>$fH6PV$;UqBu?}e$;Sm`)#)M z_^#NS4Iof~fb#RJg!5bY3IGA1$N^#VNmBWNTEtM<+Y$zflD2CK0KvZ5e4GdS!E*#; z3IxxoR}A^8R{6aE63%h9{V`pDY~fh5E&N z2&5sP{5&e@JZ5JA@GGe4WSNx{qw)hdjZs(0HY5$8N}9CFx|)vZRoW4HBP+*zs9U^5 zAd!IbbEm{}m%RbNFP(-HX4X!I$`9s5LoUUYBo3xTt<-P~Cu(PYCI7FY*4NiBOu9bn zA&>yR^=PUw1f+I3(A;2e4eo!ZHPjwZEgQ{dZEt>F4^&^X8pj3bP3;|yte4hO6 z2J6=;yB9$6Im_-a;8#b}$u-L-!|(y`GiUaXDZ_*7Z1ERO}1j_k{90WIf6$>%5g z1Au?bXb1r=ob?D$D%H>@P1tw6X8Dd+8qJq=fkwlNhL}|04RY|?ju)D zc*+m_a`3V~k2~NTp6tt&z?HF4&VkRfA)x#m824P1kU1Lx!0)`Kld(%DI^}~P;Xz%| zYNZCAremz4{%Bs=rSYMen%zqjD0J?7^yD!+RCX^wV{=2~HkUdvMczpH1+$X~{Jv^9 zW4n6BD>OpNA8wW`AqJm@C1V7XpXS}i~ju)2JiY>CklL{z5XK9%HeVgnC0Ka#H0AInFKgtJ{F+Hatl?o6BDivh?I0DMgRRn-I z<%zMZh7iEXs&svn53bV9;Zw^57+;nZ%(CW|I6sXY+3ga!_X21N0)C0IeKo-Ep@uN9 z40ciD&Hf{QiVHL~HH{oLqp7y|5`2O{aREX>aT`8KCQu>>B->z=wFm%Flqy(;5KsZ0 znpL$7JT+ZoFA4+*0}7BG*oE@*kjBa1FK70z0EmNDLkQrcB=Dm@Dj$H+o5qqB2r%X> zD3YN~RDSN!Jo)=&AOJ`{7&##&16Dp5lQomI3>IMQ8C)C#n^xwWv*rsgcb0cA0AWD! z5uxFVfZsz-O3CY7W`HK&Fl#y^!1!~f)QLN){5)g8@=N!H^~>Kc5&f z`TP9tT>T6@YIJg$_G8?{R{bAHafwl^S%F z9N_-Cm8@T?U3`Xss{)KGGb+!yEm|Giz5?JINxljo>l;DZ2e_00;mU1^}4{ca~08GM#KV3DQQh1hO=?%#v-{n)u`Xhr<>R zPU&6%OM-we77hb${~XZr7_#D3`~y8+xBTtP-bcI^C10A8Z*n` zY5{luc7SSW%l0QuQcR+j6^`awAt1&V|4or}%OzaX-D-)gS@i9{P%j7og;WfEPx--} zcL?s&#xUTwIoJn7OG~pH6^@Jf8UA# zpyY^gV<$G8pa=nO9E(}*r5I<*C%!D9F1ftl*D2f!kR=j+4#-k94e5(GN%8le3@HfB z$gF@EYp#Y|(SKzH&d`+r-;4mD?8tF%N2dP$C;w!r{KVm2`RTDjf#C7%N*DOc`%!m7}6@+v|(oPgQa+Kt_T9|2{xkM5Bj5ggbFKhX za^$hDGy$F8P;5OBN-G8%c0xdmxu&M3kxq1&POc)r6#xsb0Qgl@o`P37bmYIGdSZ6L zRDRXigUte}$6Q>iipS{LJ1+cr(t80ibS1!d4_^)7uLs6G8Vy-q4BCnZhavqb)C|!# z(bq!D@E!sPq$eQ8oWDiW#$B*&>Gjy{E%Da_Ew8_6gH#5mKk!uyeitld)Q4?mQ$W?Y zGgdES-~J0Z^K*cuKL->nW=8(%V4=PR-;+I2s|Xvx{_q?DTLff}yhKREsWW&J;oEBl*?u9KXFha63IOhES7lge3l-|3^0-V0E$VU5iV1>d*$ zO)d#!=#oI8<4}2&F4PFg@5z|KU2v0J7B=sQfQ<1Mq*Fc?GkQ6|Hw|ABAOK`M0EmI4 zd=QBpD|}4*zf$CanZok0tBecC2m)HhAORrbM{2NpjsyVFWi10r>|EhvS|=k{ycCv( zT{$3N?fA>^_m76~6#xPN0brH0gz~|+N(KwpJFu>`~bRyI&$wJyDd zMgK&9gIo6RY92PDsW$i?&k;aiRe?g|XVsaWR@q!LZP6(zJ8(SHH|Z?BD4kpy#nH#ptltf_U_=7fI=z+fI=#~7fZmO44W6# zFIz`Uma#Tq$-<&<;w0xkiuWw(0?kAqRRJ*;Qr##UQzk$FP)6;>j>HKe%M+vMoBxwQ zzP}5_Yd0!PFI^Q7W83=v^X-2{2?+ozD0q^lfH+xXd2(F-vj;x|q6$#OEVrWiC7fg%E8oEG_*w=*h00LW-z zP3x98!4L#Ys|@XNO+buA*ZNI=R}~-ttZKqL5(UJ`haeyknN1@Q5D;T7@D@)ISSCOK z;6TtK5IiXn2DFrbon}xVIIc3-y>eZe`qsPGrhhL$xxHrObHYGI3TbmU#0iX0U~?5| zyAA;{c698UecCTT0I|=XSUbk zBux;=Y>Ab0OdLl_cE(N+&?GcV{?Pfp6S>(i9m-s`&2IA z|MP3ji|Uu{m&*jo`L*tl1fZ3lof|Ab05G^XG)<0xm=Fj8a(s1eRn%5z8`w7jwg?aa zY^fCOOkF@s76gIRX_ah{e^_4$Gdsz!00AJw#ieV}1PB7roOCZG>ZH3J?B4`|y4!cZ zQPRBtCUj3j8wdj$(AlM#0%Ee^>VW?Gh+61x2`xY%y8;A&?ADf^B@_^o4?!RyV1m_z zd4g401px#S3J?Gi!m(QR1%i_jVIaMF2VK+KDqBJTfw%$$fVg5T@1%g3v3SD(FF+xgO5P(3RHDba&IyQ#jZjdy^unj`r@V^*0wxI%08FX|ZLvl` zOmr>>0byWG(P$_F2(%C&0JPxX1p@H|#3blK5D~&dVL*l|+jLeSIEmU841#TUwt#dT0fz*t9O^4wML?;5 zKcOE{PxuRiqDA36Kn|Di@7KAhQAlfXtSbj+rh%5HP($v=4!73-H$h zvRz+#XMunp1pH<;H8qVKHlwN5f+o{M1Q5u800AHaMWhSa5U8)OU)a2;epzR1`#Xi% zr~th%Qs9LHE;v-?y#PjbT79GVIY7NRdqkkD06!I!C1L>t5U5aq08k+yt0N#&fFK}K zmu(~9wg3UZ?fTPuO9aF{1C}(GW+H%q1p)*B3yMS&5kLR|Ck6I6Y##RlsHr?*K#end zK>z^+5J12v0Rn(g<)ASLAb0R#{*Utr*Vhs~6JFM#}zp(8A765kLR|1Q5_GKmgF|&)yM0009K@0vr76;a_ks zfWAY4Frd$#JtKes0tg_WO@IKPjh-DNfB*srAfQix0HDvFJtKes0tg_WO@IKPjh-DN zfB*sr_yX>U9F5I_I{Edm4pE$r+R0R#|000F%M1OUDM>>U9F z5I_I{Edm4pE$r+R0R#|0z!%v4)2%jFb1y(+a|7h11xN&?efSgs1Q0*~flLYz05Vxt zI)wlN2q1t!X#oO2X&*jC009ILKp>L>1b|GIl};gm00Kq}G)+2vXEpZ%7|mc#;|K%h z)Pj~EfB*srAfQZu0HBPT9U*`K0tg^rjsO9`oLbNl1Q0*~0R)r@5CD`>vm*o$KmdWf zz-`@YHsD?W!wv<)fMErpIS3$t00Ic85Fh}kP-Y(pAb)aKmY+50t5gVs%#Sh1Q0*~0mB6d0EQQc<{^Lp0tg@=Lx2Dv zLzQhJfB*sr{1+d{zAPm@0CEADp0tg_0K*|CHfRsVm90CX+fB*tE2oL~l zs1j{N009ILKpX5CAF!WOW1(KmY**Y!n~>*jP2% zi~s@%Ab>!H0tA2x0a+aZ1Q0+VFK}tY=QnaMfVGDLVZhqb(QpJ1KmY**k_!+3lH0K+ z0tg_000Pzu5CE(#9SuhS0R#|0Ah`emAh{iDB7gt_2>1fuEnG5?djYII76=1YmygCH zfB*srAdpOe0Fca$wGco60R#}RT7Upxb@^yK0tg_000PMb2ms04SPKCJ5J12en0Uc; z4O;F6Xl%~!mp^F=kO1qc9XYO*Z^5I_I{ z1S}CC09aBenu!1c2$&==>DX6S*K#j_NeCHg3t=Eb#iT0;Ab20R#|0pu7M9puCZ^ zB?to2XxT6V2q1t!836)786XuO^Rs{oW%hvp0%-{BdH?9X+zXI~q*>b_3>Z=mt_&Db z2%3Qa0th%FKmahT&|DcXtROT80R#}pr~m=Lz`}Ahz`&x=Bm@vZAY%dq0D}uk5HPqn zGz|d<1%{k9=XHJe0yH)^IB2f4%OrvqPrpT}c!HhrJ3#_8RzUy(1P~}GKmaJoW6plW`LfB*t91g`w~ z-FtB_KnyTj7bOgos+^@)28PXOs^uqwQibI+1Q0*~0RljjW5D7o0|bF61!frp5J12^ z0RlkOnp=7`!0#R*AZn3W4gmxZa7Tau5VzKr1_3_=5Cq~Do8=L3TA+XZ#`kb9fFp+h zVIXOt`H3K@9P1%~00IaYAV2`9qzIN?8SuNzm4QkM&$ z2q1ufLIDCmTG(6-NK2CKAb@~=f&aW&J5=+%0FBKJ`W@VA0f`{(dRv+dxPvh51A*-! zfB*t!3lIQg)ra2+WXZB!1Q0*~ffNJ?04X3A-GrrA1N{DQ*I?1A@&N(}Adm?G0)UD# zS{elSSwO|X!9EZ$N#LQ!SDT~xUI3Fg9BK<;KxMHl{)s>(6Z=8{0R#|`FF*j0Z`^VZ zTp4KT!7Bt1KmY+P0t5gp?D?Hqd^Lc31#}%W>=yw95J*p;CcRCurPu-@1oUkaemEA_Q3W z6M-1eTn$)RN}eHrz;Xd`e!#;l0Rn(o^;p>&KL}VB0{q5{fld%uSxTNEV7|b@ev|H$ zdoO@EH<)kXW(x@eCfD84D+4~MKkx|zCLdh14FNd2rP}FsT}}1p(&;M99xWF!@_}G!Ou6 zE1IQO1AJzF7O?GLqrGkjJURE9dz0M@5C{M%P&GC;q_7n>QGo!7paMLtt8#fjD?d9& zz%_w%XMfl9%Se30zk!thHii>1BMocW+9+oz{!bUd^yt3 zZ%hja0LGL;LPNv>&(#14xNH&uy9At^^hJrpxlp|3ahU)Cz=Z=r9Q>9A0dd0OX8{)u zQu-)MK#Zdn+aLMq|J_yOUVs+M@xmDa!hkcS&Ue%g1(pQ@zdYh(MksLUV5P5>2#7IL z$u=^vuJ|I9iO!_o2?9X+re<$Z3^jzUam+i`?ZVv^ z=dHq@@@@nH0zd>LXBM&e%79-Zf`Btes#6_h(hrrJi9ZCA{3QV(xm`Bb6obpMs{wv7 z{1YW1Ae)D!YrSe}Y8p9gMpLb8{Vv*Hab7CA03Rd~$R$~cbsQ3~FbMe7$zl+Y{Y@~7 zX10w~*){eKxS)BiG4}#AHaEmB5J@05S*aD8o10sF%q4*q9K2XXz#o7XrUL)W`pAX# zAJx~_FI=Thyn%p00@71}L4A$X6ej&hOld5{C4n>)UD<|(LBKCfR=(=Wo+z^7WXo`r zT~AvambH^fUrD*R0-%IBqdT$qYJgu6Cb*0qmlk!4Nk3F9CjJnJ+L!zUpw`>|y@>&9 z;ne{D&`vE31-`zmp+M%O zVylU%qe*71Ozm^Yb}{jn_tk_kl%}@{{i(POO#BrS z>exW|3IG8>M-^P#r==mlFO$iWV;Yl^$^FwkOw#bx-G@~7&!nF!r;d#g0CcFc&*cJ^ z1_6^N$x0?Ell!G}$W&sgv)zUEJ@t*5yQX?C03o0dvz6}=2COVwh8k^YAn+?->f{-B z@-np_I*;F0O z{~<|bvwvv6yfi|90ANHJXhcZ?f<=5U{IWid)O19|C{z^pm&l@{+h0;Fi#*yhLDy0AXMS1y8II zurv_(zT7Jy=O`lt1YG&nAHd*gDFFgNDIGpDTfovFKnO_Zuwv2=IWqBwKzLbBfB;ZV zh|kRzAPAUWqm)|6q#uH0(hq^~GL8TNAdVKxnk!&w2=GCNImp zaW6n<1H43_r2t`|r3bHU7qB!G5C~R1@CX1QlV|}Tv=T27C@w$%C~m_iwhCAp0(@?Q zfafvs2L>LKegK1~X$uel(iUZV<_K6C1Y8UOO!}c=!^9r~;iU`#0)PxvwrQ3CLBOnf z{MZ^M{ZLubo%E-4@AOyh1qc!IvbX?Yptud6WJ$o%K;V}kYgY#d0il7FZU_i%oRx8aj43RoHhY!3lU`k}$Zq#pv|r49iCfDU!`saL?#5a6TP76h311BW`3egK1~ zCI}D!OsE2F&?8`J5HLLiFzJU5XY~{Rm3Qs;nt}HMG&VPcXn1Lu0Exh`g3ufd0wjZq z(VLo@Mh=_NR9o?eva$j7DIhCy^h2Ql0YD)(yE0h7(h%T_>byk2*Z9|GZ}Edm4pTPj66)d~;8$J$=3jpB;@*08Y0tA5QRxIhNfP+DRZ~lRUok>4{ z!P6`W5CF1NP5L5Fz|s)lgG9YofJr}e_%QK@KzNxo0RlkQDoTIk3RoHhiiH3s{m|iJ z?WA9~+bI4U0ij~@GL8UYAdVKxA|M1T4+Vq(QOF-3tqlR8e((~37y<-<7+5TdKnnp& zLqLl@xcq`i-{tDl_q+fBfWrWRN(Bf4l?tj`pGjYxHG48%fB;~8iD;k|0)zmw%arMf zzvKNEZ_mFAVD3Go_>>#tIMwb(Gs z%?)xY&sJp#kO*YSvRwp%0;GbVMrF@Q0+so(HUd8i5CAw75Kt*V5J;xYq@T=0dwy-|y#T>> z^4uB$!hkiUqM?};AQUVwEg>MYhd3QGQ-AAFRXU;f*djS&Sv05T7$RU}5MbgrFg*w`>6>-D z&>95F2oM0u0P!&bMhTb}0+{sG9v(0K@4j1dFMwKeNA^Y-aHP<5Gh+g}LjfTmV+R!7 zaZ!K(;9}M3b2bE2hX5x1Y#cZA0)Za|2ml-k2-q$lI|wlG+kS-5egp~%5C96Y@FoHl z3ZxkXnDos%0M^`k{K-b&3((lyV4fN+iX%WGh@-`_2$(KVNh%-(m|k7lhd`T$yVqVIXrQ zr9%iHfB*srloucXls94t1Q0*~0R%E9Kmf>GN$C&*2q1s}0_6n=0OgHX0s#aNkScJ} z1v{^8{=EQ=%?(l&*tm895`p#-&;SGwKmY**)C&**)SI(M1Q0*~0R*%Q5CF87fCeCd z00Iagpk9Cgpx&H4B7gt_zQBAVtMGZM;PEBKmY**5J12|0Rn)7 zMW@RMAbbfB*sr#1|j{#5ZFN1Q0*~0R*fUAOKijLOOr|0_F--9oB9d z_X3z(A6g~^2m?ZbM+6W+009IX5+DFLRBXD600IagfPfGn00;>l5kLR|1Q2jYfB@i7 zvFR!T2&fcT?WEgJ^1Q0sM~@gbR^@(^?JMV1HGiLMRo2Riz^G>qopaTa_S^aW89j23 L5qIo##2Nn&?#?#9{qM#5 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 HhF|^{@YGwP literal 1054090 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�Og@F^<}9Y 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 Nh8#M0#lQ*Y{y$H_f6@Q| diff --git a/data/images/drop-song.png b/data/images/drop-song.png index 582ded3272261835141e69e9d58a018944e01c29..7e25ab572e97f3046b2f134d24db32e2cdf43f8f 100644 GIT binary patch literal 7893 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 WnEqXk`x0X6(50@dg{V-p3jZI;hgTc` literal 1054090 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 literal 1054090 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 diff --git a/data/images/loved_playlist.png b/data/images/loved_playlist.png index 7e2f4fd64d3120402df9521a46d577306f9677a1..79c488e8e319e419a61232c341686292d6aa5893 100644 GIT binary patch literal 6923 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 z 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 literal 1215800 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 iAV7csfrSFkPMEyR From 4db10f819559e2f901eaecdea7856addc52c7907 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Tue, 25 Oct 2011 17:59:12 -0400 Subject: [PATCH 019/109] first work on new breadcrumb --- src/GetNewStuffDelegate.cpp | 1 + src/libtomahawk/CMakeLists.txt | 4 + .../infoplugins/generic/RoviPlugin.cpp | 2 +- src/libtomahawk/widgets/Breadcrumb.cpp | 170 ++++++++++++++++++ src/libtomahawk/widgets/Breadcrumb.h | 84 +++++++++ src/libtomahawk/widgets/BreadcrumbButton.cpp | 169 +++++++++++++++++ src/libtomahawk/widgets/BreadcrumbButton.h | 69 +++++++ src/libtomahawk/widgets/whatshotwidget.cpp | 26 +-- src/libtomahawk/widgets/whatshotwidget.ui | 7 +- 9 files changed, 511 insertions(+), 21 deletions(-) create mode 100644 src/libtomahawk/widgets/Breadcrumb.cpp create mode 100644 src/libtomahawk/widgets/Breadcrumb.h create mode 100644 src/libtomahawk/widgets/BreadcrumbButton.cpp create mode 100644 src/libtomahawk/widgets/BreadcrumbButton.h diff --git a/src/GetNewStuffDelegate.cpp b/src/GetNewStuffDelegate.cpp index d2d2d98e1..a6275feee 100644 --- a/src/GetNewStuffDelegate.cpp +++ b/src/GetNewStuffDelegate.cpp @@ -39,6 +39,7 @@ GetNewStuffDelegate::GetNewStuffDelegate( QObject* parent ) : QStyledItemDelegate ( parent ) + , m_hoveringOver( -1 ) , m_widestTextWidth( 0 ) , m_hoveringOver( -1 ) { diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index fc153c608..64c0e4b31 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -229,6 +229,8 @@ set( libSources widgets/breadcrumbbuttonbase.cpp widgets/headerbreadcrumb.cpp widgets/siblingcrumbbutton.cpp + widgets/Breadcrumb.cpp + widgets/BreadcrumbButton.cpp jobview/JobStatusView.cpp jobview/JobStatusModel.cpp @@ -457,6 +459,8 @@ set( libHeaders widgets/breadcrumbbuttonbase.h widgets/headerbreadcrumb.h widgets/siblingcrumbbutton.h + widgets/Breadcrumb.h + widgets/BreadcrumbButton.h jobview/JobStatusView.h jobview/JobStatusModel.h diff --git a/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp index bf6791742..f4fe5fb32 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp @@ -172,7 +172,7 @@ RoviPlugin::makeRequest( QUrl url ) url.addQueryItem( "apikey", m_apiKey ); url.addEncodedQueryItem( "sig", generateSig() ); -// qDebug() << "url:" << url.toString(); + qDebug() << "Rovi request url:" << url.toString(); return m_nam->get( QNetworkRequest( url ) ); } diff --git a/src/libtomahawk/widgets/Breadcrumb.cpp b/src/libtomahawk/widgets/Breadcrumb.cpp new file mode 100644 index 000000000..8ada21765 --- /dev/null +++ b/src/libtomahawk/widgets/Breadcrumb.cpp @@ -0,0 +1,170 @@ +/* === 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 "Breadcrumb.h" + +#include "BreadcrumbButton.h" +#include "utils/stylehelper.h" +#include "kbreadcrumbselectionmodel.h" +#include "utils/logger.h" + +#include +#include +#include +#include +#include + +using namespace Tomahawk; + +Breadcrumb::Breadcrumb( QWidget* parent, Qt::WindowFlags f ) + : QWidget( parent, f ) + , m_model( 0 ) + , m_selModel( 0 ) + , m_buttonlayout( new QHBoxLayout( this ) ) +{ + m_buttonlayout->setSpacing( 0 ); + m_buttonlayout->setMargin( 0 ); + m_buttonlayout->setAlignment( Qt::AlignLeft ); + + setAutoFillBackground( true ); + setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); + + setLayoutDirection( Qt::LeftToRight ); + setLayout( m_buttonlayout ); + show(); +} + +Breadcrumb::~Breadcrumb() +{ + +} + +void +Breadcrumb::setModel( QAbstractItemModel* model ) +{ + m_model = model; + + if ( m_selModel ) + delete m_selModel; + + m_selModel = new KBreadcrumbSelectionModel( new QItemSelectionModel( m_model ) ); +// connect( m_selMode, SIGNAL( currentChanged( QModelIndex, QModelIndex) ), this, SLOT( updateButtons( QModelIndex, QModelIndex ) ) ); + updateButtons( QModelIndex() ); +} + +void +Breadcrumb::setRootIcon( const QPixmap& pm ) +{ + m_rootIcon = pm; + + QPushButton* button = new QPushButton( QIcon( m_rootIcon ), "", this ); + button->setFlat( true ); + button->setStyleSheet( "QPushButton{ background-color: transparent; border: none; width:16px; height:16px;}" ); + m_buttonlayout->insertWidget( 0, button ); + m_buttonlayout->insertSpacing( 0,5 ); + m_buttonlayout->insertSpacing( 2,5 ); +} + + +void +Breadcrumb::paintEvent( QPaintEvent* ) +{ + QStylePainter p( this ); + StyleHelper::horizontalHeader( &p, rect() ); +} + +// updateFrom is the item that has changed---all children must be recomputed +// if invalid, redo the whole breadcrumb +void +Breadcrumb::updateButtons( const QModelIndex& updateFrom ) +{ + qDebug() << "Updating buttons:" << updateFrom.data(); + int cur = 0; + QModelIndex idx; + for ( cur = 0; cur < m_buttons.count(); cur++ ) + { + qDebug() << "Checking if this breadcrumb item changed:" << sel[ cur ].data() << updateFrom.data() << ( sel[ cur ] != updateFrom); + if ( m_buttons[ cur ].currentIndex() == updateFrom ) + break; + idx = m_buttons[ cur ].currentIndex(); + } + + // Ok, changed all indices that are at cur or past it. lets update them + // When we get to the "end" of the tree, the leaf node is the chart itself + qDebug() << "DONE and beginning iteration:" << idx.data(); + while ( m_model->rowCount( idx ) > 0 ) + { + qDebug() << "CHANGED AND iterating:" << idx.data(); + BreadcrumbButton* btn = 0; + if ( m_buttons.size() <= cur ) + { + // We have to create a new button, doesn't exist yet + btn = new BreadcrumbButton( this, m_model ); + connect( btn, SIGNAL( currentIndexChanged( QModelIndex ) ), this, SLOT( breadcrumbComboChanged( QModelIndex ) ) ); + + m_buttonlayout->addWidget( btn ); + + // Animate all buttons except the first + if ( m_buttons.count() > 0 ) + { + QWidget* neighbor = m_buttonlayout->itemAt( m_buttonlayout->count() - 2 )->widget(); + QPropertyAnimation* animation = new QPropertyAnimation( btn, "pos" ); + animation->setDuration( 300 ); + animation->setStartValue( neighbor->pos() ); + animation->setEndValue( btn->pos() ); + animation->start( QAbstractAnimation::DeleteWhenStopped ); + } + + m_buttons.append( btn ); + } + else + { + // Got a button already, we just want to change the contents + btn = m_buttons[ cur ]; + } + + // The children of idx are what populates this combobox. + // It takes care of setting the default/user-populated value. + btn->setParentIndex( idx ); + + // Repeat with children + idx = btn->currentIndex(); + + cur++; + } + + // Now we're at the leaf, lets activate the chart + emit activateIndex( idx ); +} + +void +Breadcrumb::breadcrumbComboChanged( const QModelIndex& childSelected ) +{ + // Some breadcrumb buttons' combobox changed. lets update the child breadcrumbs + tDebug() << "Combo changed:" << childSelected.data(); + m_selModel->select( childSelected, QItemSelectionModel::SelectCurrent ); + updateButtons( childSelected ); +} + + +// void +// Breadcrumb::currentIndexChanged( const QModelIndex& current, const QModelIndex& previous ) +// { +// +// } +// diff --git a/src/libtomahawk/widgets/Breadcrumb.h b/src/libtomahawk/widgets/Breadcrumb.h new file mode 100644 index 000000000..611cdc985 --- /dev/null +++ b/src/libtomahawk/widgets/Breadcrumb.h @@ -0,0 +1,84 @@ +/* === 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 TOMAHAWK_BREADCRUMB_H +#define TOMAHAWK_BREADCRUMB_H + +#include +#include + +class QHBoxLayout; +class QAbstractItemModel; +class KBreadcrumbSelectionModel; + +namespace Tomahawk { + +class BreadcrumbButton; + +/** + * A breadcrumb widget for Tomahawk's needs. + * + * This breadcrumb operates on a QAIM. It uses a KBreadcrumbSelectionModel to manage the visible + * breadcrumb selection. + * This breadcrumb always expands fully to the deepest child. + * Selections are remembered when switching to/from in parent nodes + * Items that have a DefaultRole set will automatically select the default unless the user has + * made a previous selection, which is saved in the UserSelection role + */ +class Breadcrumb : public QWidget +{ + Q_OBJECT +public: + enum ExtraRoles { + DefaultRole = Qt::UserRole + 1, + UserSelectedRole = Qt::UserRole + 2 + }; + + explicit Breadcrumb( QWidget* parent = 0, Qt::WindowFlags f = 0 ); + virtual ~Breadcrumb(); + + void setModel( QAbstractItemModel* model ); + QAbstractItemModel* model() const { return m_model; } + + void setRootIcon( const QPixmap& pm ); + +protected: + virtual void paintEvent( QPaintEvent* ); + +signals: + void activateIndex( const QModelIndex& idx ); + +private slots: + void breadcrumbComboChanged( const QModelIndex& ); + +private: + // Takes an index in the selection model to update from (updates from that to all children) + void updateButtons( const QModelIndex& fromIndex ); + + QAbstractItemModel* m_model; + KBreadcrumbSelectionModel* m_selModel; + QPixmap m_rootIcon; + + QHBoxLayout* m_buttonlayout; + QList m_buttons; +}; + +} + +#endif // TOMAHAWK_BREADCRUMB_H + diff --git a/src/libtomahawk/widgets/BreadcrumbButton.cpp b/src/libtomahawk/widgets/BreadcrumbButton.cpp new file mode 100644 index 000000000..f3ab39c1d --- /dev/null +++ b/src/libtomahawk/widgets/BreadcrumbButton.cpp @@ -0,0 +1,169 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2011, Casey Link + * 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 "BreadcrumbButton.h" + +#include "Breadcrumb.h" +#include "combobox.h" +#include "utils/stylehelper.h" +#include "utils/tomahawkutils.h" + +#include +#include + +using namespace Tomahawk; + +BreadcrumbButton::BreadcrumbButton( Breadcrumb* parent, QAbstractItemModel* model ) + : QWidget( parent ) + , m_breadcrumb( parent ) + , m_model( model ) + , m_combo( new ComboBox( this ) ) +{ + setFixedHeight( TomahawkUtils::headerHeight() ); + m_combo->setSizeAdjustPolicy( QComboBox::AdjustToContents ); + + connect( m_combo, SIGNAL( activated( int ) ), SLOT( comboboxActivated( int ) ) ); +} + +void +BreadcrumbButton::paintEvent( QPaintEvent* ) +{ + QPainter p( this ); + QStyleOption opt; + opt.initFrom( this ); + QRect r = opt.rect; + + StyleHelper::horizontalHeader( &p, r ); // draw the background + + if( !hasChildren() ) + return; + + bool reverse = opt.direction == Qt::RightToLeft; + int menuButtonWidth = 12; + int rightSpacing = 10; + int left = !reverse ? r.right()-rightSpacing - menuButtonWidth : r.left(); + int right = !reverse ? r.right()-rightSpacing : r.left() + menuButtonWidth; + int height = sizeHint().height(); + QRect arrowRect( ( left + right ) / 2 + ( reverse ? 6 : -6 ), 0, height, height ); + + QStyleOption arrowOpt = opt; + arrowOpt.rect = arrowRect; + + + QLine l1( left, 0, right, height/2 ); + QLine l2( left, height, right, height/2 ); + + + p.setRenderHint( QPainter::Antialiasing, true ); + + // Draw the shadow + QColor shadow( 0, 0, 0, 100 ); + p.translate( 0, -1 ); + p.setPen( shadow ); + p.drawLine( l1 ); + p.drawLine( l2 ); + + // Draw the main arrow + QColor foreGround( "#747474" ); + p.translate( 0, 1 ); + p.setPen( foreGround ); + p.drawLine( l1 ); + p.drawLine( l2 ); +} + +QSize +BreadcrumbButton::sizeHint() const +{ + // our width = width of combo + 20px for right-arrow and spacing + const int padding = hasChildren() ? 20 : 5; + return m_combo->sizeHint() + QSize( padding, 0 ); +} + + +void +BreadcrumbButton::setParentIndex( const QModelIndex& idx ) +{ + m_parentIndex = idx; + // Populate listview with list of children. + // Then try to find a default + QStringList list; + int count = m_model->rowCount( m_parentIndex ); + int defaultIndex = -1, userSelected = -1; + for ( int i = 0; i < count; ++i ) + { + QModelIndex idx = m_model->index( i, 0, m_parentIndex ); + if ( idx.isValid() ) + { + list << idx.data().toString(); + if ( idx.data( Breadcrumb::DefaultRole ).toBool() ) + defaultIndex = i; + if ( idx.data( Breadcrumb::UserSelectedRole ).toBool() ) + userSelected = i; + } + } + + + m_combo->clear(); + m_combo->addItems( list ); + + if ( userSelected > -1 ) + m_combo->setCurrentIndex( userSelected ); + else if ( defaultIndex > -1 ) + m_combo->setCurrentIndex( defaultIndex ); + + m_curIndex = m_model->index( m_combo->currentIndex(), 0, m_parentIndex ); + m_combo->adjustSize(); +/* + if ( m_combo->count() && list.count() ) + { + // Check if it's the same, Don't change if it is, as it'll cause flickering + QStringList old; + for ( int i = 0; i < m_combo->count(); i++ ) + old << m_combo->itemText( i ); + + if ( list == old ) + return; + }*/ + +} + +void +BreadcrumbButton::comboboxActivated( int idx ) +{ + m_model->setData( m_curIndex, false, Breadcrumb::UserSelectedRole ); + + QModelIndex selected = m_model->index( idx, 0, m_parentIndex ); + m_curIndex = selected; + m_model->setData( selected, true, Breadcrumb::UserSelectedRole ); + + emit currentIndexChanged( selected ); +} + + +bool +BreadcrumbButton::hasChildren() const +{ + return m_model->rowCount( m_model->index( m_combo->currentIndex(), 0, m_parentIndex ) ) > 0; +} + +QModelIndex +BreadcrumbButton::currentIndex() const +{ + return m_model->index( m_combo->currentIndex(), 0, m_parentIndex ); +} diff --git a/src/libtomahawk/widgets/BreadcrumbButton.h b/src/libtomahawk/widgets/BreadcrumbButton.h new file mode 100644 index 000000000..d7efaa96d --- /dev/null +++ b/src/libtomahawk/widgets/BreadcrumbButton.h @@ -0,0 +1,69 @@ +/* === 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 TOMAHAWK_BREADCRUMBBUTTON_H +#define TOMAHAWK_BREADCRUMBBUTTON_H + +#include +#include + +class ComboBox; +class QPaintEvent; + +namespace Tomahawk { + +class Breadcrumb; +class BreadcrumbModel; + +class BreadcrumbButton : public QWidget +{ + Q_OBJECT +public: + explicit BreadcrumbButton( Breadcrumb* parent, QAbstractItemModel* model ); + + void setParentIndex( const QModelIndex& idx ); + + // Which index is currently visible. This is the first, default or last selected + // calculated immediately after loading, or what the user has selected if he has made + // a manua;l selection + QModelIndex currentIndex() const; +protected: + virtual void paintEvent( QPaintEvent* ); + virtual QSize sizeHint() const; + +signals: + // Some combobox value is changed + void currentIndexChanged( const QModelIndex& childSelected ); + +private slots: + void comboboxActivated( int ); + +private: + bool hasChildren() const; + + Breadcrumb* m_breadcrumb; + QAbstractItemModel* m_model; + + QPersistentModelIndex m_parentIndex; + QPersistentModelIndex m_curIndex; + ComboBox* m_combo; +}; + +} + +#endif // TOMAHAWK_BREADCRUMBBUTTON_H diff --git a/src/libtomahawk/widgets/whatshotwidget.cpp b/src/libtomahawk/widgets/whatshotwidget.cpp index e1a7a0a83..6ed5fa63e 100644 --- a/src/libtomahawk/widgets/whatshotwidget.cpp +++ b/src/libtomahawk/widgets/whatshotwidget.cpp @@ -65,17 +65,11 @@ WhatsHotWidget::WhatsHotWidget( QWidget* parent ) TomahawkUtils::unmarginLayout( ui->breadCrumbLeft->layout() ); TomahawkUtils::unmarginLayout( ui->verticalLayout->layout() ); - //set crumb widgets - SiblingCrumbButtonFactory * crumbFactory = new SiblingCrumbButtonFactory; - m_crumbModelLeft = new QStandardItemModel( this ); - ui->breadCrumbLeft->setButtonFactory( crumbFactory ); - ui->breadCrumbLeft->setModel( m_crumbModelLeft ); - ui->breadCrumbLeft->setRootIcon(QIcon( RESPATH "images/charts.png" )); - ui->breadCrumbLeft->setUseAnimation( true ); + ui->breadCrumbLeft->setRootIcon( QPixmap( RESPATH "images/charts.png" ) ); - connect( ui->breadCrumbLeft, SIGNAL( currentIndexChanged( QModelIndex ) ), SLOT( leftCrumbIndexChanged(QModelIndex) ) ); + connect( ui->breadCrumbLeft, SIGNAL( activateIndex( QModelIndex ) ), SLOT( leftCrumbIndexChanged(QModelIndex) ) ); ui->tracksViewLeft->setFrameShape( QFrame::NoFrame ); ui->tracksViewLeft->setAttribute( Qt::WA_MacShowFocusRect, 0 ); @@ -216,9 +210,7 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat } } - KBreadcrumbSelectionModel *selectionModelLeft = new KBreadcrumbSelectionModel(new QItemSelectionModel(m_crumbModelLeft, this), this); - ui->breadCrumbLeft->setSelectionModel(selectionModelLeft); - //ui->breadCrumbRight->setSelectionModel(selectionModelLeft); + ui->breadCrumbLeft->setModel( m_crumbModelLeft ); break; } case InfoSystem::InfoChart: @@ -321,12 +313,12 @@ WhatsHotWidget::leftCrumbIndexChanged( QModelIndex index ) return; - QList indexes; - while ( index.parent().isValid() ) - { - indexes.prepend(index); - index = index.parent(); - } + QList indexes; + while ( index.parent().isValid() ) + { + indexes.prepend(index); + index = index.parent(); + } const QString chartId = item->data().toString(); diff --git a/src/libtomahawk/widgets/whatshotwidget.ui b/src/libtomahawk/widgets/whatshotwidget.ui index 4caa5e32b..7c635acf8 100644 --- a/src/libtomahawk/widgets/whatshotwidget.ui +++ b/src/libtomahawk/widgets/whatshotwidget.ui @@ -12,7 +12,7 @@ - + @@ -79,9 +79,10 @@
playlist/playlistview.h
- HeaderBreadCrumb + Tomahawk::Breadcrumb QWidget -
widgets/headerbreadcrumb.h
+
widgets/Breadcrumb.h
+ 1
From 84d817c9d4822cb744616c41d6f34e2529dae181 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 27 Oct 2011 21:01:49 -0400 Subject: [PATCH 020/109] Fix and remove stale files --- src/libtomahawk/CMakeLists.txt | 11 - src/libtomahawk/widgets/Breadcrumb.cpp | 49 ++- src/libtomahawk/widgets/Breadcrumb.h | 2 - src/libtomahawk/widgets/BreadcrumbButton.cpp | 30 +- src/libtomahawk/widgets/breadcrumbbar.cpp | 345 ------------------ src/libtomahawk/widgets/breadcrumbbar.h | 198 ---------- .../widgets/breadcrumbbuttonbase.cpp | 39 -- .../widgets/breadcrumbbuttonbase.h | 94 ----- .../widgets/kbreadcrumbselectionmodel.cpp | 202 ---------- .../widgets/kbreadcrumbselectionmodel.h | 164 --------- .../widgets/kbreadcrumbselectionmodel_p.h | 71 ---- .../widgets/siblingcrumbbutton.cpp | 189 ---------- src/libtomahawk/widgets/siblingcrumbbutton.h | 80 ---- src/libtomahawk/widgets/whatshotwidget.cpp | 11 +- src/libtomahawk/widgets/whatshotwidget.h | 4 - 15 files changed, 53 insertions(+), 1436 deletions(-) delete mode 100644 src/libtomahawk/widgets/breadcrumbbar.cpp delete mode 100644 src/libtomahawk/widgets/breadcrumbbar.h delete mode 100644 src/libtomahawk/widgets/breadcrumbbuttonbase.cpp delete mode 100644 src/libtomahawk/widgets/breadcrumbbuttonbase.h delete mode 100644 src/libtomahawk/widgets/kbreadcrumbselectionmodel.cpp delete mode 100644 src/libtomahawk/widgets/kbreadcrumbselectionmodel.h delete mode 100644 src/libtomahawk/widgets/kbreadcrumbselectionmodel_p.h delete mode 100644 src/libtomahawk/widgets/siblingcrumbbutton.cpp delete mode 100644 src/libtomahawk/widgets/siblingcrumbbutton.h diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 64c0e4b31..7cb24f02f 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -224,11 +224,6 @@ set( libSources widgets/infowidgets/sourceinfowidget.cpp widgets/infowidgets/ArtistInfoWidget.cpp widgets/infowidgets/AlbumInfoWidget.cpp - widgets/kbreadcrumbselectionmodel.cpp - widgets/breadcrumbbar.cpp - widgets/breadcrumbbuttonbase.cpp - widgets/headerbreadcrumb.cpp - widgets/siblingcrumbbutton.cpp widgets/Breadcrumb.cpp widgets/BreadcrumbButton.cpp @@ -453,12 +448,6 @@ set( libHeaders widgets/infowidgets/sourceinfowidget.h widgets/infowidgets/ArtistInfoWidget.h widgets/infowidgets/AlbumInfoWidget.h - widgets/kbreadcrumbselectionmodel.h - widgets/kbreadcrumbselectionmodel_p.h - widgets/breadcrumbbar.h - widgets/breadcrumbbuttonbase.h - widgets/headerbreadcrumb.h - widgets/siblingcrumbbutton.h widgets/Breadcrumb.h widgets/BreadcrumbButton.h diff --git a/src/libtomahawk/widgets/Breadcrumb.cpp b/src/libtomahawk/widgets/Breadcrumb.cpp index 8ada21765..c61dc6ca3 100644 --- a/src/libtomahawk/widgets/Breadcrumb.cpp +++ b/src/libtomahawk/widgets/Breadcrumb.cpp @@ -20,7 +20,6 @@ #include "BreadcrumbButton.h" #include "utils/stylehelper.h" -#include "kbreadcrumbselectionmodel.h" #include "utils/logger.h" #include @@ -34,7 +33,7 @@ using namespace Tomahawk; Breadcrumb::Breadcrumb( QWidget* parent, Qt::WindowFlags f ) : QWidget( parent, f ) , m_model( 0 ) - , m_selModel( 0 ) + , m_buttonlayout( new QHBoxLayout( this ) ) { m_buttonlayout->setSpacing( 0 ); @@ -58,12 +57,6 @@ void Breadcrumb::setModel( QAbstractItemModel* model ) { m_model = model; - - if ( m_selModel ) - delete m_selModel; - - m_selModel = new KBreadcrumbSelectionModel( new QItemSelectionModel( m_model ) ); -// connect( m_selMode, SIGNAL( currentChanged( QModelIndex, QModelIndex) ), this, SLOT( updateButtons( QModelIndex, QModelIndex ) ) ); updateButtons( QModelIndex() ); } @@ -95,15 +88,20 @@ Breadcrumb::updateButtons( const QModelIndex& updateFrom ) { qDebug() << "Updating buttons:" << updateFrom.data(); int cur = 0; - QModelIndex idx; - for ( cur = 0; cur < m_buttons.count(); cur++ ) + QModelIndex idx = updateFrom; + for ( int i = 0; i < m_buttons.count(); i++ ) { - qDebug() << "Checking if this breadcrumb item changed:" << sel[ cur ].data() << updateFrom.data() << ( sel[ cur ] != updateFrom); - if ( m_buttons[ cur ].currentIndex() == updateFrom ) + qDebug() << "Checking if this breadcrumb item changed:" << m_buttons[ i ]->currentIndex().data() << updateFrom.data() << ( m_buttons[ i ]->currentIndex() != updateFrom); + if ( m_buttons[ i ]->currentIndex() == updateFrom ) + { + cur = i; break; - idx = m_buttons[ cur ].currentIndex(); + } } + // We set the parent index, so go up one + idx = idx.parent(); + // Ok, changed all indices that are at cur or past it. lets update them // When we get to the "end" of the tree, the leaf node is the chart itself qDebug() << "DONE and beginning iteration:" << idx.data(); @@ -118,14 +116,14 @@ Breadcrumb::updateButtons( const QModelIndex& updateFrom ) connect( btn, SIGNAL( currentIndexChanged( QModelIndex ) ), this, SLOT( breadcrumbComboChanged( QModelIndex ) ) ); m_buttonlayout->addWidget( btn ); + btn->show(); // Animate all buttons except the first if ( m_buttons.count() > 0 ) { - QWidget* neighbor = m_buttonlayout->itemAt( m_buttonlayout->count() - 2 )->widget(); QPropertyAnimation* animation = new QPropertyAnimation( btn, "pos" ); animation->setDuration( 300 ); - animation->setStartValue( neighbor->pos() ); + animation->setStartValue( m_buttons.last()->pos() ); animation->setEndValue( btn->pos() ); animation->start( QAbstractAnimation::DeleteWhenStopped ); } @@ -148,6 +146,26 @@ Breadcrumb::updateButtons( const QModelIndex& updateFrom ) cur++; } + // extra buttons to delete! (cur is 0-indexed) + while ( m_buttons.size() > cur ) + { + BreadcrumbButton* b = m_buttons.takeLast(); + + m_buttonlayout->removeWidget( b ); + b->show(); + + if ( m_buttons.size() ) + { + QPropertyAnimation* animation = new QPropertyAnimation( b, "pos" ); + animation->setDuration( 300 ); + animation->setStartValue( b->pos() ); + animation->setEndValue( m_buttons.last()->pos() ); + animation->start( QAbstractAnimation::DeleteWhenStopped ); + } + + b->deleteLater(); + } + // Now we're at the leaf, lets activate the chart emit activateIndex( idx ); } @@ -157,7 +175,6 @@ Breadcrumb::breadcrumbComboChanged( const QModelIndex& childSelected ) { // Some breadcrumb buttons' combobox changed. lets update the child breadcrumbs tDebug() << "Combo changed:" << childSelected.data(); - m_selModel->select( childSelected, QItemSelectionModel::SelectCurrent ); updateButtons( childSelected ); } diff --git a/src/libtomahawk/widgets/Breadcrumb.h b/src/libtomahawk/widgets/Breadcrumb.h index 611cdc985..ecc2279b2 100644 --- a/src/libtomahawk/widgets/Breadcrumb.h +++ b/src/libtomahawk/widgets/Breadcrumb.h @@ -24,7 +24,6 @@ class QHBoxLayout; class QAbstractItemModel; -class KBreadcrumbSelectionModel; namespace Tomahawk { @@ -71,7 +70,6 @@ private: void updateButtons( const QModelIndex& fromIndex ); QAbstractItemModel* m_model; - KBreadcrumbSelectionModel* m_selModel; QPixmap m_rootIcon; QHBoxLayout* m_buttonlayout; diff --git a/src/libtomahawk/widgets/BreadcrumbButton.cpp b/src/libtomahawk/widgets/BreadcrumbButton.cpp index f3ab39c1d..17b591c51 100644 --- a/src/libtomahawk/widgets/BreadcrumbButton.cpp +++ b/src/libtomahawk/widgets/BreadcrumbButton.cpp @@ -38,6 +38,8 @@ BreadcrumbButton::BreadcrumbButton( Breadcrumb* parent, QAbstractItemModel* mode setFixedHeight( TomahawkUtils::headerHeight() ); m_combo->setSizeAdjustPolicy( QComboBox::AdjustToContents ); + setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Expanding ); + connect( m_combo, SIGNAL( activated( int ) ), SLOT( comboboxActivated( int ) ) ); } @@ -47,7 +49,7 @@ BreadcrumbButton::paintEvent( QPaintEvent* ) QPainter p( this ); QStyleOption opt; opt.initFrom( this ); - QRect r = opt.rect; + QRect r = rect(); StyleHelper::horizontalHeader( &p, r ); // draw the background @@ -59,13 +61,12 @@ BreadcrumbButton::paintEvent( QPaintEvent* ) int rightSpacing = 10; int left = !reverse ? r.right()-rightSpacing - menuButtonWidth : r.left(); int right = !reverse ? r.right()-rightSpacing : r.left() + menuButtonWidth; - int height = sizeHint().height(); + int height = r.height(); QRect arrowRect( ( left + right ) / 2 + ( reverse ? 6 : -6 ), 0, height, height ); QStyleOption arrowOpt = opt; arrowOpt.rect = arrowRect; - QLine l1( left, 0, right, height/2 ); QLine l2( left, height, right, height/2 ); @@ -119,6 +120,17 @@ BreadcrumbButton::setParentIndex( const QModelIndex& idx ) } + if ( m_combo->count() && list.count() ) + { + // Check if it's the same, Don't change if it is, as it'll cause flickering + QStringList old; + for ( int i = 0; i < m_combo->count(); i++ ) + old << m_combo->itemText( i ); + + if ( list == old ) + return; + } + m_combo->clear(); m_combo->addItems( list ); @@ -129,18 +141,6 @@ BreadcrumbButton::setParentIndex( const QModelIndex& idx ) m_curIndex = m_model->index( m_combo->currentIndex(), 0, m_parentIndex ); m_combo->adjustSize(); -/* - if ( m_combo->count() && list.count() ) - { - // Check if it's the same, Don't change if it is, as it'll cause flickering - QStringList old; - for ( int i = 0; i < m_combo->count(); i++ ) - old << m_combo->itemText( i ); - - if ( list == old ) - return; - }*/ - } void diff --git a/src/libtomahawk/widgets/breadcrumbbar.cpp b/src/libtomahawk/widgets/breadcrumbbar.cpp deleted file mode 100644 index dea514c0e..000000000 --- a/src/libtomahawk/widgets/breadcrumbbar.cpp +++ /dev/null @@ -1,345 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2011, Casey Link - * Copyright (C) 2004-2011 Glenn Van Loon, glenn@startupmanager.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 . - */ - -#include "breadcrumbbar.h" -#include "breadcrumbbuttonbase.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "utils/logger.h" - - -BreadcrumbBar::BreadcrumbBar( BreadcrumbButtonFactory *buttonFactory, QWidget *parent ) - : QWidget( parent ) - , m_buttonFactory( buttonFactory ) - , m_model( 0 ) - , m_selectionModel( 0 ) - , m_layout( new QHBoxLayout( this ) ) - , m_useAnimation( false ) - -{ - m_layout->setSpacing( 0 ); - m_layout->setMargin( 0 ); - m_layout->setAlignment( Qt::AlignLeft ); - - setAutoFillBackground( false ); - setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); - - setLayoutDirection( Qt::LeftToRight ); - setLayout( m_layout ); - setMinimumWidth( 100 ); - show(); -} - - -BreadcrumbBar::BreadcrumbBar( QWidget *parent ) - : QWidget( parent ) - , m_buttonFactory( 0 ) - , m_model( 0 ) - , m_selectionModel( 0 ) - , m_layout( new QHBoxLayout( this ) ) - , m_useAnimation( false ) - -{ - m_layout->setSpacing( 0 ); - m_layout->setMargin( 0 ); - m_layout->setAlignment( Qt::AlignLeft ); - - setAutoFillBackground( false ); - setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); - - setLayoutDirection( Qt::LeftToRight ); - setLayout( m_layout ); - setMinimumWidth( 100 ); - show(); -} - - -BreadcrumbBar::~BreadcrumbBar() -{ -} - - -void BreadcrumbBar::setButtonFactory(BreadcrumbButtonFactory *buttonFactory) -{ - tDebug( LOGVERBOSE ) << "Breadcrumbbar:: got me some button factory!"; - m_buttonFactory = buttonFactory; -} - - -BreadcrumbButtonFactory* BreadcrumbBar::buttonFactory() const -{ - return m_buttonFactory; -} - - -void BreadcrumbBar::appendButton(BreadcrumbButtonBase *widget, int stretch) -{ - m_layout->insertWidget(m_layout->count(), widget, stretch); - if( !m_useAnimation ) - return; //we're done here. - - // A nifty trick to force the widget to calculate its position and geometry - //widget->setAttribute(Qt::WA_DontShowOnScreen); - widget->show(); - //widget->setAttribute(Qt::WA_DontShowOnScreen, false); - - if( m_navButtons.size() > 0 ) { - QWidget* neighbor = m_layout->itemAt(m_layout->count()-2)->widget(); - QPropertyAnimation *animation = new QPropertyAnimation(widget,"pos"); - animation->setDuration(300); - animation->setStartValue(neighbor->pos()); - animation->setEndValue(widget->pos()); - animation->start(QAbstractAnimation::DeleteWhenStopped); - } -} - - -void BreadcrumbBar::deleteAnimationFinished() -{ - QPropertyAnimation *anim = qobject_cast(sender()); - - if( !anim ) - return; - QObject *obj = anim->targetObject(); - obj->deleteLater(); - anim->deleteLater(); -} - - -void BreadcrumbBar::deleteButton(BreadcrumbButtonBase *widget) -{ - widget->hide(); - widget->deleteLater(); - return; // all done here - - // Don't animate on delete. We expand a child recursively until it has no more children---this makes - // the deleting and creating animations overlap. - -// int index = m_layout->indexOf(widget); -// if( index != 0 && m_navButtons.size() > 0 ) { -// QWidget* neighbor = m_layout->itemAt(index-1)->widget(); -// QPropertyAnimation *animation = new QPropertyAnimation(widget,"pos"); -// m_layout->removeWidget(widget); -// connect(animation, SIGNAL(finished()), SLOT(deleteAnimationFinished())); -// animation->setDuration(300); -// animation->setStartValue(widget->pos()); -// animation->setEndValue(neighbor->pos()); -// animation->start(); -// } else { -// widget->hide(); -// widget->deleteLater(); -// } -} - - -void BreadcrumbBar::updateButtons() -{ - tDebug( LOGVERBOSE ) << "Breadcrumbbar:: updateButtons" << m_buttonFactory << m_selectionModel ; - if ( m_selectionModel ) - tDebug( LOGVERBOSE ) <<"Breadcrumbbar:: update buttoms current index"<< m_selectionModel->currentIndex().isValid(); - if ( !m_buttonFactory || !m_selectionModel || !m_selectionModel->currentIndex().isValid() ) - { - tDebug( LOGVERBOSE ) << "Breadcrumb:: updatebuttons failed!"; - return; - } - - QLinkedList::iterator it = m_navButtons.begin(); - QLinkedList::const_iterator const itEnd = m_navButtons.end(); - bool createButton = false; - - QModelIndex index = m_selectionModel->currentIndex(); - QList indexes; - while (index.parent().isValid()) - { - indexes.prepend(index); - index = index.parent(); - } - tDebug( LOGVERBOSE ) << "BreadcrumbBar::updateButtons:: " << index.data().toString(); - indexes.prepend(index); - - int count = indexes.size(), i = 0; - foreach (index, indexes) - { - createButton = (it == itEnd); - bool isLastButton = (++i == count); - - QString const dirName = index.data().toString(); - BreadcrumbButtonBase *button = 0; - if (createButton) - { - button = m_buttonFactory->newButton(index, this); - appendButton(button); - button->setActive(isLastButton); - m_navButtons.append(button); - } - else - { - button = *it; - button->setIndex(index); - button->setActive(isLastButton); - ++it; - } - } - - QLinkedList::Iterator itBegin = it; - while (it != itEnd) - { - deleteButton(*it); - ++it; - } - m_navButtons.erase(itBegin, m_navButtons.end()); - - collapseButtons(); - - adjustSize(); -} - - -void BreadcrumbBar::collapseButtons() -{ - foreach (BreadcrumbButtonBase *button, m_navButtons) { - button->show(); - } - - const int desired_width = size().width(); - int current_width = sizeHint().width(); - - QLinkedList::iterator it = m_navButtons.begin(); - QLinkedList::const_iterator const itEnd = m_navButtons.end(); - it = m_navButtons.begin(); - while( current_width > desired_width && it != itEnd ) { - (*it)->hide(); - ++it; - current_width = sizeHint().width(); - } -} - - -void BreadcrumbBar::clearButtons() -{ - foreach (BreadcrumbButtonBase *button, m_navButtons) - { - button->hide(); - button->deleteLater(); - } - m_navButtons.clear(); -} - - -void BreadcrumbBar::currentIndexChanged() -{ - updateButtons(); -} - - -void BreadcrumbBar::setRootIcon(const QIcon &icon) -{ - - m_rootIcon = icon; - QPushButton* button = new QPushButton(icon, "", this); - button->setFlat(true); - button->setStyleSheet( "QPushButton{ background-color: transparent; border: none; width:16px; height:16px;}" ); - m_layout->insertWidget(0, button); - m_layout->insertSpacing(0,5); - m_layout->insertSpacing(2,5); - connect(button, SIGNAL(clicked()), this, SIGNAL(rootClicked())); -} - - -void BreadcrumbBar::setRootText(const QString &text) -{ - //TODO: implement this - m_rootText = text; - /*QLabel *label= new QLabel(this); - label->setPixmap(icon.pixmap(16,16)); - m_layout->insertWidget(0, label); - m_layout->insertSpacing(0,5); - m_layout->insertSpacing(2,5);*/ -} - - -void BreadcrumbBar::setUseAnimation(bool use) -{ - m_useAnimation = use; -} - - -bool BreadcrumbBar::useAnimation() const -{ - return m_useAnimation; -} - - -void BreadcrumbBar::setModel(QAbstractItemModel *model) -{ - m_model = model; - updateButtons(); -} - - -QAbstractItemModel* BreadcrumbBar::model() -{ - return m_model; -} - - -void BreadcrumbBar::setSelectionModel(QItemSelectionModel *selectionModel) -{ - m_selectionModel = selectionModel; - m_selectionModel->setCurrentIndex(m_model->index(0,0), QItemSelectionModel::SelectCurrent); - connect(m_selectionModel, - SIGNAL(currentChanged(QModelIndex const&, QModelIndex const&)), - this, SLOT(currentIndexChanged())); - updateButtons(); -} - - -QItemSelectionModel* BreadcrumbBar::selectionModel() -{ - return m_selectionModel; -} - - -QModelIndex BreadcrumbBar::currentIndex() -{ - return m_selectionModel->currentIndex(); -} - - -void BreadcrumbBar::currentChangedTriggered(QModelIndex const& index) -{ - Q_ASSERT(m_selectionModel); - m_selectionModel->setCurrentIndex( index, QItemSelectionModel::SelectCurrent); - emit currentIndexChanged(index); -} - - -void BreadcrumbBar::resizeEvent ( QResizeEvent * event ) -{ - Q_UNUSED( event ); - collapseButtons(); -} diff --git a/src/libtomahawk/widgets/breadcrumbbar.h b/src/libtomahawk/widgets/breadcrumbbar.h deleted file mode 100644 index 0ebe68df0..000000000 --- a/src/libtomahawk/widgets/breadcrumbbar.h +++ /dev/null @@ -1,198 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2011, Casey Link - * Copyright (C) 2004-2011 Glenn Van Loon, glenn@startupmanager.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 . - */ - -#ifndef BREADCRUMBBAR_H -#define BREADCRUMBBAR_H - -#include "breadcrumbbuttonbase.h" - -#include -#include -#include -#include - -class QAbstractItemModel; -class QItemSelectionModel; -class QAbstractItemModel; -class QAbstractProxyModel; -class QHBoxLayout; -class QItemSelectionModel; -class QResizeEvent; - -/** - * \brief A breadcrumb view for a QAbstractItemModel - * - * This a lean Breadcrumb navigation bar that supports the following features: - * - a QAbstractItemModel data source for MV goodness - * - client provided crumb button widgets (to use your own style and behavior) - * - client provided crumb animation [optional] - * - client provided root item (icon or text) [optional] - * - */ -class BreadcrumbBar : public QWidget -{ - Q_OBJECT - -public: - /** - * \brief breadcrumb bar constructor - * \param buttonFactory the button factory to instantiate bread crumbs from - */ - BreadcrumbBar(BreadcrumbButtonFactory *buttonFactory, QWidget *parent = 0); - /** - * \brief breadcrumb bar constructor - * You must set the button factory using BreadcrumbBar::setButtonFactory - */ - BreadcrumbBar(QWidget *parent = 0); - - virtual ~BreadcrumbBar(); - - /** - * \brief sets the button factory to use to create buttons - * \param buttonFactory the button factory - */ - void setButtonFactory(BreadcrumbButtonFactory *buttonFactory); - BreadcrumbButtonFactory* buttonFactory() const; - - /** - * \brief Set the icon that should be displayed at the root - * \param icon the icon - */ - void setRootIcon(const QIcon &icon); - /** - * \brief Set the text that should be displayed at the root - * \param test the text - */ - void setRootText(const QString &text); - - /** - * \brief Set whether the crumb animation should be used (default: false) - */ - void setUseAnimation(bool use); - bool useAnimation() const; - - /** - * \brief set the item model to use as a data source - * \param model the item model - * - * Although the item model can be of any structure, the breadcrumb bar - * works best when the model's structure is a tree. - */ - void setModel(QAbstractItemModel *model); - QAbstractItemModel *model(); - - /** - * \brief set the selection model used to determine the crumb items - * \param selectionModel the selection model - * - * The selection model is just as important as your model. In fact your - * model can be of any structure (even not-tree-like) as long as your - * selection model understands what it means for an item to be a crumb, - * and knows how to select it. - * - * If you have a standard tree model, you probably should use - * KBreadCrumbSelectionModel which encapsulates the concept of "When an - * item is selected, make a selection which includes its parent and - * ancestor items until the top is reached". - * - * \sa See Stephen Kelley's blog post here http://steveire.wordpress.com/2010/04/21/breadcrumbs-for-your-view - */ - void setSelectionModel(QItemSelectionModel *selectionModel); - QItemSelectionModel *selectionModel(); - - /** - * \brief get the index of the currently active item - * \return the active index - */ - QModelIndex currentIndex(); - - /** - * \brief used by crumbs to notify that the current index has changed - * \param index the new current index - */ - void currentChangedTriggered(QModelIndex const& index); - -signals: - void rootClicked(); - void currentIndexChanged(QModelIndex); - -protected: - /** - * \brief append a crumb widget - * \param button the crumb button to add - * \param stretch widget stretch factor - * Respects the useAnimation() setting. - */ - void appendButton(BreadcrumbButtonBase *button, int stretch = 0); - - /** - * \brief deletes a crumb from the bar - * \param button the crumb button to delete - * Respects the useAnimation() setting. - */ - void deleteButton(BreadcrumbButtonBase *button); - - /** - * \brief collapses crumbs when there isnt enough room for all of them - * Starts hiding from the left until we can fit all the buttons. - */ - void collapseButtons(); - /** - * \brief reimpl from QWidget - */ - void resizeEvent ( QResizeEvent * event ); - - -protected slots: - - /** - * \brief The current index has changed in the selection model - * When the selection model changes, we get notified here - */ - void currentIndexChanged(); - - /** - * \brief Recreate the button bar from the selection model - */ - void updateButtons(); - /** - * \brief clear breadcrumb buttons - */ - void clearButtons(); - - /** - * Called when the delete animation finishes so we can delete the button - * object. - */ - void deleteAnimationFinished(); - - -private: - BreadcrumbButtonFactory *m_buttonFactory; /*!< Factory used to create new crumbs */ - QAbstractItemModel *m_model; /*!< The source model */ - QItemSelectionModel *m_selectionModel; /*!< The selection model */ - QHBoxLayout *m_layout; /*!< The layout holding out crumb buttons */ - QLinkedList m_navButtons; /*< Our list of crumbs! */ - QIcon m_rootIcon; /*!< The root icon */ - QString m_rootText; /*!< The root text */ - bool m_useAnimation; /* === - * - * Copyright 2011, Casey Link - * Copyright (C) 2004-2011 Glenn Van Loon, glenn@startupmanager.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 . - */ - -#include "breadcrumbbuttonbase.h" -#include "breadcrumbbar.h" - -BreadcrumbButtonBase::BreadcrumbButtonBase(BreadcrumbBar *parent) - : QPushButton(parent), m_breadcrumbBar(parent) -{ - setFocusPolicy(Qt::NoFocus); - setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed); - setMinimumHeight(parent->minimumHeight()); -} - -BreadcrumbButtonBase::~BreadcrumbButtonBase() -{ -} - -BreadcrumbBar* BreadcrumbButtonBase::breadcrumbBar() const -{ - return m_breadcrumbBar; -} - diff --git a/src/libtomahawk/widgets/breadcrumbbuttonbase.h b/src/libtomahawk/widgets/breadcrumbbuttonbase.h deleted file mode 100644 index 7625b6bae..000000000 --- a/src/libtomahawk/widgets/breadcrumbbuttonbase.h +++ /dev/null @@ -1,94 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2011, Casey Link - * Copyright (C) 2004-2011 Glenn Van Loon, glenn@startupmanager.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 . - */ - -#ifndef BREADCRUMBBUTTONBASE_P_H -#define BREADCRUMBBUTTONBASE_P_H - -#include -#include - -class BreadcrumbBar; -class BreadcrumbButtonBase; - -/** - * \brief an abstract factory class to create crumb buttons - * Subclass this class and make it return bread crumb buttons of your type. - */ -class BreadcrumbButtonFactory { - - public: - BreadcrumbButtonFactory(){} - - virtual ~BreadcrumbButtonFactory(){} - - /** - * \brief instantiates a new bread crumb button - * \param index the initial index this crumb should hold - * \param parent the breadcrumb bar this button will belong to - * \returns a new bread crumb button allocated on the heap. - * - * This method can be as simple as: - * \code - * return new MyBreadCrumbButton(index, parent); - * \endcode - * - */ - virtual BreadcrumbButtonBase* newButton(QModelIndex index, BreadcrumbBar *parent) = 0; - -}; - -/** - * \brief The button base class for the BreadcrumbBar - * Re-implement this to provide your own crumb buttons. Don't forget to supply - * a BreadcrumbButtonFactory as well. - */ -class BreadcrumbButtonBase : public QPushButton -{ - Q_OBJECT - -public: - explicit BreadcrumbButtonBase(BreadcrumbBar *parent); - virtual ~BreadcrumbButtonBase(); - - /** - * \brief retrieve the breadcrumb bar that this button belongs to - * \return the parent breadcrumb bar - */ - BreadcrumbBar* breadcrumbBar() const; - - /** - * \brief set the model item that this button represents - * \param index the index of the model items to display - */ - virtual void setIndex(QModelIndex index) = 0; - virtual QModelIndex index() const = 0; - - /** - * \brief sets whether this button is active or not - * \param active true for active, false for inactive - * You could, for example, make the active button bold. - */ - virtual void setActive(bool active) = 0; - virtual bool isActive() const = 0; - -private: - BreadcrumbBar *m_breadcrumbBar; -}; - -#endif // BREADCRUMBBUTTONBASE_P_H diff --git a/src/libtomahawk/widgets/kbreadcrumbselectionmodel.cpp b/src/libtomahawk/widgets/kbreadcrumbselectionmodel.cpp deleted file mode 100644 index 92af04c96..000000000 --- a/src/libtomahawk/widgets/kbreadcrumbselectionmodel.cpp +++ /dev/null @@ -1,202 +0,0 @@ -/* - Copyright (C) 2010 Klarälvdalens Datakonsult AB, - a KDAB Group company, info@kdab.net, - author Stephen Kelly - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - - -#include "kbreadcrumbselectionmodel.h" -#include "kbreadcrumbselectionmodel_p.h" - -#include - -KBreadcrumbSelectionModel::KBreadcrumbSelectionModel(QItemSelectionModel *selectionModel, QObject* parent) - : QItemSelectionModel(const_cast(selectionModel->model()), parent), - d_ptr(new KBreadcrumbSelectionModelPrivate(this, selectionModel, MakeBreadcrumbSelectionInSelf)) -{ - d_ptr->init(); -} - -KBreadcrumbSelectionModel::KBreadcrumbSelectionModel(QItemSelectionModel *selectionModel, BreadcrumbTarget direction, QObject* parent) - : QItemSelectionModel(const_cast(selectionModel->model()), parent), - d_ptr(new KBreadcrumbSelectionModelPrivate(this, selectionModel, direction)) -{ - if ( direction != MakeBreadcrumbSelectionInSelf) - connect(selectionModel, SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)), - this, SLOT(sourceSelectionChanged(const QItemSelection&,const QItemSelection&))); - - d_ptr->init(); -} - -KBreadcrumbSelectionModel::~KBreadcrumbSelectionModel() -{ - delete d_ptr; -} - -bool KBreadcrumbSelectionModel::isActualSelectionIncluded() const -{ - Q_D(const KBreadcrumbSelectionModel); - return d->m_includeActualSelection; -} - -void KBreadcrumbSelectionModel::setActualSelectionIncluded(bool includeActualSelection) -{ - Q_D(KBreadcrumbSelectionModel); - d->m_includeActualSelection = includeActualSelection; -} - -int KBreadcrumbSelectionModel::breadcrumbLength() const -{ - Q_D(const KBreadcrumbSelectionModel); - return d->m_selectionDepth; -} - -void KBreadcrumbSelectionModel::setBreadcrumbLength(int breadcrumbLength) -{ - Q_D(KBreadcrumbSelectionModel); - d->m_selectionDepth = breadcrumbLength; -} - -QItemSelection KBreadcrumbSelectionModelPrivate::getBreadcrumbSelection(const QModelIndex& index) -{ - QItemSelection breadcrumbSelection; - - if (m_includeActualSelection) - breadcrumbSelection.append(QItemSelectionRange(index)); - - QModelIndex parent = index.parent(); - int sumBreadcrumbs = 0; - bool includeAll = m_selectionDepth < 0; - while (parent.isValid() && (includeAll || sumBreadcrumbs < m_selectionDepth)) { - breadcrumbSelection.append(QItemSelectionRange(parent)); - parent = parent.parent(); - } - return breadcrumbSelection; -} - -QItemSelection KBreadcrumbSelectionModelPrivate::getBreadcrumbSelection(const QItemSelection& selection) -{ - QItemSelection breadcrumbSelection; - - if (m_includeActualSelection) - breadcrumbSelection = selection; - - QItemSelection::const_iterator it = selection.constBegin(); - const QItemSelection::const_iterator end = selection.constEnd(); - - for ( ; it != end; ++it) - { - QModelIndex parent = it->parent(); - - if (breadcrumbSelection.contains(parent)) - continue; - - int sumBreadcrumbs = 0; - bool includeAll = m_selectionDepth < 0; - - while (parent.isValid() && (includeAll || sumBreadcrumbs < m_selectionDepth)) - { - breadcrumbSelection.append(QItemSelectionRange(parent)); - parent = parent.parent(); - - if (breadcrumbSelection.contains(parent)) - break; - - ++sumBreadcrumbs; - } - } - return breadcrumbSelection; -} - -void KBreadcrumbSelectionModelPrivate::sourceSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) -{ - Q_Q(KBreadcrumbSelectionModel); - QItemSelection deselectedCrumbs = getBreadcrumbSelection(deselected); - QItemSelection selectedCrumbs = getBreadcrumbSelection(selected); - - QItemSelection removed = deselectedCrumbs; - foreach(const QItemSelectionRange &range, selectedCrumbs) - { - removed.removeAll(range); - } - - QItemSelection added = selectedCrumbs; - foreach(const QItemSelectionRange &range, deselectedCrumbs) - { - added.removeAll(range); - } - - if (!removed.isEmpty()) - { - q->QItemSelectionModel::select(removed, QItemSelectionModel::Deselect); - } - if (!added.isEmpty()) - { - q->QItemSelectionModel::select(added, QItemSelectionModel::Select); - } -} - -void KBreadcrumbSelectionModel::select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) -{ - Q_D(KBreadcrumbSelectionModel); - // When an item is removed, the current index is set to the top index in the model. - // That causes a selectionChanged signal with a selection which we do not want. - if ( d->m_ignoreCurrentChanged ) - { - d->m_ignoreCurrentChanged = false; - return; - } - if ( d->m_direction == MakeBreadcrumbSelectionInOther ) - { - d->m_selectionModel->select(d->getBreadcrumbSelection(index), command); - QItemSelectionModel::select(index, command); - } else { - d->m_selectionModel->select(index, command); - QItemSelectionModel::select(d->getBreadcrumbSelection(index), command); - } -} - -void KBreadcrumbSelectionModel::select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) -{ - Q_D(KBreadcrumbSelectionModel); - QItemSelection bcc = d->getBreadcrumbSelection(selection); - if ( d->m_direction == MakeBreadcrumbSelectionInOther ) - { - d->m_selectionModel->select(selection, command); - QItemSelectionModel::select(bcc, command); - } else { - d->m_selectionModel->select(bcc, command); - QItemSelectionModel::select(selection, command); - } -} - -void KBreadcrumbSelectionModelPrivate::init() -{ - Q_Q(KBreadcrumbSelectionModel); - q->connect(m_selectionModel->model(), SIGNAL(layoutChanged()), SLOT(syncBreadcrumbs())); - q->connect(m_selectionModel->model(), SIGNAL(modelReset()), SLOT(syncBreadcrumbs())); - q->connect(m_selectionModel->model(), SIGNAL(rowsMoved(QModelIndex, int, int, QModelIndex, int)), SLOT(syncBreadcrumbs())); - // Don't need to handle insert & remove because they can't change the breadcrumbs on their own. -} - -void KBreadcrumbSelectionModelPrivate::syncBreadcrumbs() -{ - Q_Q(KBreadcrumbSelectionModel); - q->select(m_selectionModel->selection(), QItemSelectionModel::ClearAndSelect); -} - diff --git a/src/libtomahawk/widgets/kbreadcrumbselectionmodel.h b/src/libtomahawk/widgets/kbreadcrumbselectionmodel.h deleted file mode 100644 index 751be28ef..000000000 --- a/src/libtomahawk/widgets/kbreadcrumbselectionmodel.h +++ /dev/null @@ -1,164 +0,0 @@ -/* - Copyright (C) 2010 Klarälvdalens Datakonsult AB, - a KDAB Group company, info@kdab.net, - author Stephen Kelly - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#ifndef KBREADCRUMBSPROXYMODEL_H -#define KBREADCRUMBSPROXYMODEL_H - -#include -#include -#include - - -class KBreadcrumbSelectionModelPrivate; - -/** - @class KBreadcrumbSelectionModel kbreadcrumbselectionmodel.h - - @brief Selects the parents of selected items to create breadcrumbs - - For example, if the tree is - @verbatim - - A - - B - - - C - - - D - - - - E - - - - - F - @endverbatim - - and E is selected, the selection can contain - - @verbatim - - B - - D - @endverbatim - - or - - @verbatim - - B - - D - - E - @endverbatim - - if isActualSelectionIncluded is true. - - The depth of the selection may also be set. For example if the breadcrumbLength is 1: - - @verbatim - - D - - E - @endverbatim - - And if breadcrumbLength is 2: - - @verbatim - - B - - D - - E - @endverbatim - - A KBreadcrumbsProxyModel with a breadcrumbLength of 0 and including the actual selection is - the same as a KSelectionProxyModel in the KSelectionProxyModel::ExactSelection configuration. - - @code - view1->setModel(rootModel); - - QItemSelectionModel *breadcrumbSelectionModel = new QItemSelectionModel(rootModel, this); - - KBreadcrumbSelectionModel *breadcrumbProxySelector = new KBreadcrumbSelectionModel(breadcrumbSelectionModel, rootModel, this); - - view1->setSelectionModel(breadcrumbProxySelector); - - KSelectionProxyModel *breadcrumbSelectionProxyModel = new KSelectionProxyModel( breadcrumbSelectionModel, this); - breadcrumbSelectionProxyModel->setSourceModel( rootModel ); - breadcrumbSelectionProxyModel->setFilterBehavior( KSelectionProxyModel::ExactSelection ); - - view2->setModel(breadcrumbSelectionProxyModel); - @endcode - - @image html kbreadcrumbselectionmodel.png "KBreadcrumbSelectionModel in several configurations" - - This can work in two directions. One option is for a single selection in the KBreadcrumbSelectionModel to invoke - the breadcrumb selection in its constructor argument. - - The other is for a selection in the itemselectionmodel in the constructor argument to cause a breadcrumb selection - in @p this. - - @since 4.5 - -*/ -class KBreadcrumbSelectionModel : public QItemSelectionModel -{ - Q_OBJECT -public: - enum BreadcrumbTarget - { - MakeBreadcrumbSelectionInOther, - MakeBreadcrumbSelectionInSelf - }; - - explicit KBreadcrumbSelectionModel(QItemSelectionModel *selectionModel, QObject* parent = 0); - KBreadcrumbSelectionModel(QItemSelectionModel *selectionModel, BreadcrumbTarget target, QObject* parent = 0); - virtual ~KBreadcrumbSelectionModel(); - - /** - Returns whether the actual selection in included in the proxy. - - The default is true. - */ - bool isActualSelectionIncluded() const; - - /** - Set whether the actual selection in included in the proxy to @p isActualSelectionIncluded. - */ - void setActualSelectionIncluded(bool isActualSelectionIncluded); - - /** - Returns the depth that the breadcrumb selection should go to. - */ - int breadcrumbLength() const; - - /** - Sets the depth that the breadcrumb selection should go to. - - If the @p breadcrumbLength is -1, all breadcrumbs are selected. - The default is -1 - */ - void setBreadcrumbLength(int breadcrumbLength); - - /* reimp */ void select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command); - - /* reimp */ void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command); - -protected: - KBreadcrumbSelectionModelPrivate * const d_ptr; -private: - //@cond PRIVATE - Q_DECLARE_PRIVATE(KBreadcrumbSelectionModel) - Q_PRIVATE_SLOT( d_func(),void sourceSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)) - Q_PRIVATE_SLOT( d_func(),void syncBreadcrumbs()) - //@cond PRIVATE -}; - - -#include "kbreadcrumbselectionmodel_p.h" //HACK ALERT - Why doesn't it compile without this?! -#endif diff --git a/src/libtomahawk/widgets/kbreadcrumbselectionmodel_p.h b/src/libtomahawk/widgets/kbreadcrumbselectionmodel_p.h deleted file mode 100644 index 93aac514c..000000000 --- a/src/libtomahawk/widgets/kbreadcrumbselectionmodel_p.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - Copyright (C) 2010 Klarälvdalens Datakonsult AB, - a KDAB Group company, info@kdab.net, - author Stephen Kelly - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#ifndef KBREADCRUMBSPROXYMODEL_P_H -#define KBREADCRUMBSPROXYMODEL_P_H - -#include "kbreadcrumbselectionmodel.h" - -#include -#include -#include - -class KBreadcrumbSelectionModelPrivate -{ - Q_DECLARE_PUBLIC(KBreadcrumbSelectionModel) - KBreadcrumbSelectionModel * const q_ptr; -public: - KBreadcrumbSelectionModelPrivate(KBreadcrumbSelectionModel *breadcrumbSelector, QItemSelectionModel *selectionModel, KBreadcrumbSelectionModel::BreadcrumbTarget direction) - : q_ptr(breadcrumbSelector), - m_includeActualSelection(true), - m_selectionDepth(-1), - m_showHiddenAscendantData(false), - m_selectionModel(selectionModel), - m_direction(direction), - m_ignoreCurrentChanged(false) - { - - } - - /** - Returns a selection containing the breadcrumbs for @p index - */ - QItemSelection getBreadcrumbSelection(const QModelIndex &index); - - /** - Returns a selection containing the breadcrumbs for @p selection - */ - QItemSelection getBreadcrumbSelection(const QItemSelection &selection); - - void sourceSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); - - void init(); - void syncBreadcrumbs(); - - bool m_includeActualSelection; - int m_selectionDepth; - bool m_showHiddenAscendantData; - QItemSelectionModel *m_selectionModel; - KBreadcrumbSelectionModel::BreadcrumbTarget m_direction; - bool m_ignoreCurrentChanged; -}; - -#endif diff --git a/src/libtomahawk/widgets/siblingcrumbbutton.cpp b/src/libtomahawk/widgets/siblingcrumbbutton.cpp deleted file mode 100644 index e6f4f6e49..000000000 --- a/src/libtomahawk/widgets/siblingcrumbbutton.cpp +++ /dev/null @@ -1,189 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2011, Casey Link - * - * 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 "siblingcrumbbutton.h" - -#include "combobox.h" -#include "utils/stylehelper.h" -#include "utils/tomahawkutils.h" - -#include -#include -#include -#include -#include -#include "whatshotwidget.h" - -BreadcrumbButtonBase* SiblingCrumbButtonFactory::newButton(QModelIndex index, BreadcrumbBar *parent) -{ - return new SiblingCrumbButton(index, parent); -} - -SiblingCrumbButton::SiblingCrumbButton( - QModelIndex index, BreadcrumbBar *parent) - : BreadcrumbButtonBase(parent), - m_index(index), m_combo( new ComboBox(this) ) -{ - setFixedHeight( TomahawkUtils::headerHeight() ); - m_combo->setSizeAdjustPolicy( QComboBox::AdjustToContents ); - setIndex(index); - connect(m_combo, SIGNAL(activated(int)), SLOT(comboboxActivated(int))); -} - -void SiblingCrumbButton::setIndex( QModelIndex index ) -{ - if ( !(m_index == index && text() == index.data().toString()) ) - { - m_index = index; - setText( index.data().toString() ); - } - fillCombo(); -} - -QModelIndex SiblingCrumbButton::index() const -{ - return m_index; -} - -void SiblingCrumbButton::setActive( bool active ) -{ - Q_UNUSED( active ); - if ( active ) - QTimer::singleShot( 0, this, SLOT( activateSelf() ) ); -} - -bool SiblingCrumbButton::isActive() const -{ - return false; -} - -QSize SiblingCrumbButton::sizeHint() const -{ - // our width = width of combo + 20px for right-arrow and spacing - const int padding = hasChildren() ? 20 : 5; - return m_combo->sizeHint() + QSize( padding, 0 ); -} - -void SiblingCrumbButton::paintEvent( QPaintEvent *event ) -{ - Q_UNUSED( event ); - - QPainter p( this ); - QStyleOption opt; - opt.initFrom( this ); - QRect r = opt.rect; - - StyleHelper::horizontalHeader( &p, r ); // draw the background - - if( !hasChildren() ) - return; - - bool reverse = opt.direction == Qt::RightToLeft; - int menuButtonWidth = 12; - int rightSpacing = 10; - int left = !reverse ? r.right()-rightSpacing - menuButtonWidth : r.left(); - int right = !reverse ? r.right()-rightSpacing : r.left() + menuButtonWidth; - int height = sizeHint().height(); - QRect arrowRect( ( left + right ) / 2 + ( reverse ? 6 : -6 ), 0, height, height ); - - QStyleOption arrowOpt = opt; - arrowOpt.rect = arrowRect; - - - QLine l1( left, 0, right, height/2 ); - QLine l2( left, height, right, height/2 ); - - - p.setRenderHint( QPainter::Antialiasing, true ); - - // Draw the shadow - QColor shadow( 0, 0, 0, 100 ); - p.translate( 0, -1 ); - p.setPen( shadow ); - p.drawLine( l1 ); - p.drawLine( l2 ); - - // Draw the main arrow - QColor foreGround( "#747474" ); - p.translate( 0, 1 ); - p.setPen( foreGround ); - p.drawLine( l1 ); - p.drawLine( l2 ); -} - -void SiblingCrumbButton::fillCombo() -{ - QStringList list; - int count = breadcrumbBar()->model()->rowCount(m_index.parent()); - int defaultIndex = -1; - for ( int i = 0; i < count; ++i ) - { - QModelIndex sibling = m_index.sibling(i,0); - if ( sibling.isValid() ) - { - list << sibling.data().toString(); - if ( sibling.data( WhatsHotWidget::DefaultRole ).toBool() ) - defaultIndex = i; - } - } - - if ( m_combo->count() && list.count() ) - { - // Check if it's the same, Don't change if it is, as it'll cause flickering - QStringList old; - for ( int i = 0; i < m_combo->count(); i++ ) - old << m_combo->itemText( i ); - - if ( list == old ) - return; - } - - m_combo->clear(); - m_combo->addItems(list); - if ( defaultIndex == -1 ) - m_combo->setCurrentIndex( m_combo->findText(text())); - else - { - m_combo->setCurrentIndex( defaultIndex ); - comboboxActivated( defaultIndex ); - } - m_combo->adjustSize(); -} - -void SiblingCrumbButton::comboboxActivated(int i) -{ - QModelIndex activated = m_index.sibling(i,0); - int count = breadcrumbBar()->model()->rowCount(activated); - if( count > 0 ) { -// qDebug() << "activated crumb with children:" << activated.child(0,0).data().toString(); - breadcrumbBar()->currentChangedTriggered(activated.child(0,0)); - } else { - // if it has no children, then emit itself - breadcrumbBar()->currentChangedTriggered(activated); - } -} - -void SiblingCrumbButton::activateSelf() -{ - comboboxActivated(m_index.row()); -} - -bool SiblingCrumbButton::hasChildren() const -{ - return m_index.model()->hasChildren(m_index); -} diff --git a/src/libtomahawk/widgets/siblingcrumbbutton.h b/src/libtomahawk/widgets/siblingcrumbbutton.h deleted file mode 100644 index 7525f1c21..000000000 --- a/src/libtomahawk/widgets/siblingcrumbbutton.h +++ /dev/null @@ -1,80 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2011, Casey Link - * - * 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 SIBLINGCRUMBBUTTON_H -#define SIBLINGCRUMBBUTTON_H - -#include "breadcrumbbuttonbase.h" -#include "breadcrumbbar.h" - -#include "combobox.h" - -#include -#include -#include -#include -#include - -/** - * \brief A factory for sibling crumb buttons - */ -class SiblingCrumbButtonFactory : public BreadcrumbButtonFactory -{ - - public: - SiblingCrumbButtonFactory(){} - - virtual ~SiblingCrumbButtonFactory(){} - virtual BreadcrumbButtonBase* newButton(QModelIndex index, BreadcrumbBar *parent); -}; - -/** - * \brief A crumb button implementation where the dropdowns show sibling items - * Unlike most crumb buttons, this one shows a list of sibling items (as - * opposed to child items). This is desireable in certain circumstances. - */ -class SiblingCrumbButton : public BreadcrumbButtonBase -{ - Q_OBJECT - -public: - SiblingCrumbButton(QModelIndex index, BreadcrumbBar *parent); - - void setIndex(QModelIndex index); - QModelIndex index() const; - void setActive(bool active); - bool isActive() const; - virtual QSize sizeHint() const; - -protected: - virtual void paintEvent(QPaintEvent *event); - -private slots: - void fillCombo(); - void comboboxActivated(int i); - void activateSelf(); - -private: - bool hasChildren() const; - - QModelIndex m_index; /*!< our current index */ - ComboBox *m_combo; /*!< our combobox! */ - -}; - -#endif // SIBLINGCRUMBBUTTON_H diff --git a/src/libtomahawk/widgets/whatshotwidget.cpp b/src/libtomahawk/widgets/whatshotwidget.cpp index 6ed5fa63e..0f8dbd00d 100644 --- a/src/libtomahawk/widgets/whatshotwidget.cpp +++ b/src/libtomahawk/widgets/whatshotwidget.cpp @@ -34,8 +34,6 @@ #include "playlist/playlistmodel.h" #include "playlist/treeproxymodel.h" #include "widgets/overlaywidget.h" -#include "widgets/siblingcrumbbutton.h" -#include "widgets/kbreadcrumbselectionmodel.h" #include "utils/tomahawkutils.h" #include "utils/logger.h" #include @@ -185,7 +183,8 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat QStandardItem* source = rootItem->child( i, 0 ); if ( defaultSource.toLower() == source->text().toLower() ) { - source->setData( true, DefaultRole ); + qDebug() << "Setting DEFAULT SOURCE:" << source->text(); + source->setData( true, Breadcrumb::DefaultRole ); } if ( defaults.contains( source->text().toLower() ) ) @@ -200,9 +199,9 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat { if ( cur->child( k, 0 )->text() == index ) { -// tDebug() << "Found DEFAULT ITEM:" << index; + tDebug() << "Found DEFAULT ITEM:" << index; cur = cur->child( k, 0 ); // this is the default, drill down into the default to pick the next default - cur->setData( true, DefaultRole ); + cur->setData( true, Breadcrumb::DefaultRole ); break; } } @@ -397,7 +396,7 @@ WhatsHotWidget::parseNode( QStandardItem* parentItem, const QString &label, cons QStandardItem *childItem= new QStandardItem( chart[ "label" ] ); childItem->setData( chart[ "id" ] ); if ( chart.value( "default", "" ) == "true") - sourceItem->setData( WhatsHotWidget::DefaultRole, true ); + sourceItem->setData( Breadcrumb::DefaultRole, true ); sourceItem->appendRow( childItem ); } } diff --git a/src/libtomahawk/widgets/whatshotwidget.h b/src/libtomahawk/widgets/whatshotwidget.h index 332014d31..e13c0421e 100644 --- a/src/libtomahawk/widgets/whatshotwidget.h +++ b/src/libtomahawk/widgets/whatshotwidget.h @@ -53,10 +53,6 @@ class DLLEXPORT WhatsHotWidget : public QWidget, public Tomahawk::ViewPage Q_OBJECT public: - enum ExtraRoles { - DefaultRole = Qt::UserRole + 10 - }; - WhatsHotWidget( QWidget* parent = 0 ); ~WhatsHotWidget(); From 2b682a14ae7ce275ded47739e034762d001791f3 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 28 Oct 2011 03:10:59 +0200 Subject: [PATCH 021/109] * Unbreak compiling. --- src/GetNewStuffDelegate.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/GetNewStuffDelegate.cpp b/src/GetNewStuffDelegate.cpp index a6275feee..d2d2d98e1 100644 --- a/src/GetNewStuffDelegate.cpp +++ b/src/GetNewStuffDelegate.cpp @@ -39,7 +39,6 @@ GetNewStuffDelegate::GetNewStuffDelegate( QObject* parent ) : QStyledItemDelegate ( parent ) - , m_hoveringOver( -1 ) , m_widestTextWidth( 0 ) , m_hoveringOver( -1 ) { From 253d65d5a324238d9bb94a151919212e873973eb Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 27 Oct 2011 21:55:29 -0400 Subject: [PATCH 022/109] fix chart defaults --- src/libtomahawk/widgets/Breadcrumb.h | 3 ++- src/libtomahawk/widgets/BreadcrumbButton.cpp | 2 +- src/libtomahawk/widgets/whatshotwidget.cpp | 22 +++++++++++--------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/libtomahawk/widgets/Breadcrumb.h b/src/libtomahawk/widgets/Breadcrumb.h index ecc2279b2..74adabf1a 100644 --- a/src/libtomahawk/widgets/Breadcrumb.h +++ b/src/libtomahawk/widgets/Breadcrumb.h @@ -45,7 +45,8 @@ class Breadcrumb : public QWidget public: enum ExtraRoles { DefaultRole = Qt::UserRole + 1, - UserSelectedRole = Qt::UserRole + 2 + UserSelectedRole = Qt::UserRole + 2, + ChartIdRole = Qt::UserRole + 3 }; explicit Breadcrumb( QWidget* parent = 0, Qt::WindowFlags f = 0 ); diff --git a/src/libtomahawk/widgets/BreadcrumbButton.cpp b/src/libtomahawk/widgets/BreadcrumbButton.cpp index 17b591c51..3c005732d 100644 --- a/src/libtomahawk/widgets/BreadcrumbButton.cpp +++ b/src/libtomahawk/widgets/BreadcrumbButton.cpp @@ -92,7 +92,7 @@ QSize BreadcrumbButton::sizeHint() const { // our width = width of combo + 20px for right-arrow and spacing - const int padding = hasChildren() ? 20 : 5; + const int padding = hasChildren() ? 20 : 8; return m_combo->sizeHint() + QSize( padding, 0 ); } diff --git a/src/libtomahawk/widgets/whatshotwidget.cpp b/src/libtomahawk/widgets/whatshotwidget.cpp index 0f8dbd00d..f46d488b7 100644 --- a/src/libtomahawk/widgets/whatshotwidget.cpp +++ b/src/libtomahawk/widgets/whatshotwidget.cpp @@ -308,19 +308,19 @@ WhatsHotWidget::leftCrumbIndexChanged( QModelIndex index ) QStandardItem* item = m_crumbModelLeft->itemFromIndex( index ); if( !item ) return; - if( !item->data().isValid() ) + if( !item->data( Breadcrumb::ChartIdRole ).isValid() ) return; - QList indexes; - while ( index.parent().isValid() ) - { - indexes.prepend(index); - index = index.parent(); - } + QList indexes; + while ( index.parent().isValid() ) + { + indexes.prepend(index); + index = index.parent(); + } - const QString chartId = item->data().toString(); + const QString chartId = item->data( Breadcrumb::ChartIdRole ).toString(); if ( m_artistModels.contains( chartId ) ) { @@ -394,9 +394,11 @@ WhatsHotWidget::parseNode( QStandardItem* parentItem, const QString &label, cons foreach ( Tomahawk::InfoSystem::InfoStringHash chart, charts ) { QStandardItem *childItem= new QStandardItem( chart[ "label" ] ); - childItem->setData( chart[ "id" ] ); + childItem->setData( chart[ "id" ], Breadcrumb::ChartIdRole ); if ( chart.value( "default", "" ) == "true") - sourceItem->setData( Breadcrumb::DefaultRole, true ); + { + childItem->setData( true, Breadcrumb::DefaultRole ); + } sourceItem->appendRow( childItem ); } } From 058808aa6194e78a2c134ba8a676a2119e066b36 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 27 Oct 2011 22:28:51 -0400 Subject: [PATCH 023/109] unused signal --- src/libtomahawk/widgets/whatshotwidget.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libtomahawk/widgets/whatshotwidget.cpp b/src/libtomahawk/widgets/whatshotwidget.cpp index f46d488b7..517604fd5 100644 --- a/src/libtomahawk/widgets/whatshotwidget.cpp +++ b/src/libtomahawk/widgets/whatshotwidget.cpp @@ -92,7 +92,6 @@ WhatsHotWidget::WhatsHotWidget( QWidget* parent ) connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) ); - connect( AudioEngine::instance(), SIGNAL( playlistChanged( Tomahawk::PlaylistInterface* ) ), this, SLOT( playlistChanged( Tomahawk::PlaylistInterface* ) ) ); QTimer::singleShot( 0, this, SLOT( fetchData() ) ); } From 4de60911cd944234bcefbd8184953337517fab02 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 27 Oct 2011 22:44:38 -0400 Subject: [PATCH 024/109] Fix sizing and flicker --- src/libtomahawk/widgets/BreadcrumbButton.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/libtomahawk/widgets/BreadcrumbButton.cpp b/src/libtomahawk/widgets/BreadcrumbButton.cpp index 3c005732d..4a94b469a 100644 --- a/src/libtomahawk/widgets/BreadcrumbButton.cpp +++ b/src/libtomahawk/widgets/BreadcrumbButton.cpp @@ -26,6 +26,7 @@ #include #include +#include using namespace Tomahawk; @@ -35,6 +36,11 @@ BreadcrumbButton::BreadcrumbButton( Breadcrumb* parent, QAbstractItemModel* mode , m_model( model ) , m_combo( new ComboBox( this ) ) { + setLayout( new QHBoxLayout ); + layout()->setContentsMargins( 0, 0, 0, 0 ); + layout()->setSpacing( 0 ); + layout()->addWidget( m_combo ); + setFixedHeight( TomahawkUtils::headerHeight() ); m_combo->setSizeAdjustPolicy( QComboBox::AdjustToContents ); @@ -131,6 +137,7 @@ BreadcrumbButton::setParentIndex( const QModelIndex& idx ) return; } + m_combo->hide(); m_combo->clear(); m_combo->addItems( list ); @@ -140,6 +147,8 @@ BreadcrumbButton::setParentIndex( const QModelIndex& idx ) m_combo->setCurrentIndex( defaultIndex ); m_curIndex = m_model->index( m_combo->currentIndex(), 0, m_parentIndex ); + + m_combo->show(); m_combo->adjustSize(); } From b040870cf12f21a524216dad42abf5d1a39ccb3b Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 27 Oct 2011 23:49:56 -0400 Subject: [PATCH 025/109] Try fixing chart loading race condition --- src/libtomahawk/widgets/whatshotwidget.cpp | 12 +++++++++--- src/libtomahawk/widgets/whatshotwidget.h | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/widgets/whatshotwidget.cpp b/src/libtomahawk/widgets/whatshotwidget.cpp index 517604fd5..dfbce3310 100644 --- a/src/libtomahawk/widgets/whatshotwidget.cpp +++ b/src/libtomahawk/widgets/whatshotwidget.cpp @@ -238,7 +238,8 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat m_artistModels[ chartId ] = artistsModel; - setLeftViewArtists( artistsModel ); + if ( m_queueItemToShow == chartId ) + setLeftViewArtists( artistsModel ); } else if( type == "albums" ) { @@ -259,7 +260,9 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat albumModel->addAlbums( al ); m_albumModels[ chartId ] = albumModel; - setLeftViewAlbums( albumModel ); + + if ( m_queueItemToShow == chartId ) + setLeftViewAlbums( albumModel ); } else if( type == "tracks" ) { @@ -278,7 +281,9 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat trackModel->append( tracklist ); m_trackModels[ chartId ] = trackModel; - setLeftViewTracks( trackModel ); + + if ( m_queueItemToShow == chartId ) + setLeftViewTracks( trackModel ); } else { @@ -360,6 +365,7 @@ WhatsHotWidget::leftCrumbIndexChanged( QModelIndex index ) Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData, 20000, true ); m_queuedFetches.insert( chartId ); + m_queueItemToShow = chartId; } diff --git a/src/libtomahawk/widgets/whatshotwidget.h b/src/libtomahawk/widgets/whatshotwidget.h index e13c0421e..dcfa34f3a 100644 --- a/src/libtomahawk/widgets/whatshotwidget.h +++ b/src/libtomahawk/widgets/whatshotwidget.h @@ -96,6 +96,7 @@ private: QHash< QString, AlbumModel* > m_albumModels; QHash< QString, TreeModel* > m_artistModels; QHash< QString, PlaylistModel* > m_trackModels; + QString m_queueItemToShow; QSet< QString > m_queuedFetches; QTimer* m_timer; }; From e2bb4d7340282b260b1839d722b955883a1d56ab Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 28 Oct 2011 11:23:00 +0200 Subject: [PATCH 026/109] * Properly center album icons. --- .../playlist/albumitemdelegate.cpp | 2 +- src/libtomahawk/playlist/albumview.cpp | 24 ++++++++++++++++++- src/libtomahawk/playlist/albumview.h | 1 + 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/playlist/albumitemdelegate.cpp b/src/libtomahawk/playlist/albumitemdelegate.cpp index 4758e3c1f..15a6844cd 100644 --- a/src/libtomahawk/playlist/albumitemdelegate.cpp +++ b/src/libtomahawk/playlist/albumitemdelegate.cpp @@ -117,7 +117,7 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, oneLiner = true; else oneLiner = ( textRect.height() / 2 < painter->fontMetrics().boundingRect( item->album()->name() ).height() || - textRect.height() / 2 < painter->fontMetrics().boundingRect( item->album()->artist()->name() ).height() ); + textRect.height() / 2 < painter->fontMetrics().boundingRect( item->album()->artist()->name() ).height() ); if ( oneLiner ) { diff --git a/src/libtomahawk/playlist/albumview.cpp b/src/libtomahawk/playlist/albumview.cpp index 45099d6e8..a3aa5ad9c 100644 --- a/src/libtomahawk/playlist/albumview.cpp +++ b/src/libtomahawk/playlist/albumview.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "audio/audioengine.h" #include "tomahawksettings.h" @@ -47,7 +48,8 @@ AlbumView::AlbumView( QWidget* parent ) setDropIndicatorShown( false ); setDragDropOverwriteMode( false ); setUniformItemSizes( true ); - setSpacing( 20 ); + setSpacing( 16 ); + setContentsMargins( 0, 0, 0, 0 ); setResizeMode( Adjust ); setViewMode( IconMode ); @@ -179,6 +181,26 @@ AlbumView::paintEvent( QPaintEvent* event ) } +void +AlbumView::resizeEvent( QResizeEvent* event ) +{ + QListView::resizeEvent( event ); + + int scrollbar = !verticalScrollBar()->isVisible() ? verticalScrollBar()->rect().width() : 0; + int rectWidth = contentsRect().width() - scrollbar - 16 - 3; + QSize itemSize = m_proxyModel->data( QModelIndex(), Qt::SizeHintRole ).toSize(); + + int itemsPerRow = qFloor( rectWidth / ( itemSize.width() + 16 ) ); + int rightSpacing = rectWidth - ( itemsPerRow * ( itemSize.width() + 16 ) ); + int newSpacing = 16 + floor( rightSpacing / ( itemsPerRow + 1 ) ); + + if ( itemsPerRow < 1 ) + setSpacing( 16 ); + else + setSpacing( newSpacing ); +} + + void AlbumView::onFilterChanged( const QString& ) { diff --git a/src/libtomahawk/playlist/albumview.h b/src/libtomahawk/playlist/albumview.h index 52dfecee3..68736c55e 100644 --- a/src/libtomahawk/playlist/albumview.h +++ b/src/libtomahawk/playlist/albumview.h @@ -63,6 +63,7 @@ protected: virtual void startDrag( Qt::DropActions supportedActions ); void paintEvent( QPaintEvent* event ); + void resizeEvent( QResizeEvent* event ); private slots: void onFilterChanged( const QString& filter ); From 205bde833f2f4d8032b9536fbe982179bcc9cbda Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 28 Oct 2011 12:00:17 +0200 Subject: [PATCH 027/109] * Fixed album spacing on non-X11. --- src/libtomahawk/playlist/albumview.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libtomahawk/playlist/albumview.cpp b/src/libtomahawk/playlist/albumview.cpp index a3aa5ad9c..16c1fc132 100644 --- a/src/libtomahawk/playlist/albumview.cpp +++ b/src/libtomahawk/playlist/albumview.cpp @@ -186,7 +186,11 @@ AlbumView::resizeEvent( QResizeEvent* event ) { QListView::resizeEvent( event ); +#ifdef Q_WS_X11 int scrollbar = !verticalScrollBar()->isVisible() ? verticalScrollBar()->rect().width() : 0; +#else + int scrollbar = verticalScrollBar()->rect().width(); +#endif int rectWidth = contentsRect().width() - scrollbar - 16 - 3; QSize itemSize = m_proxyModel->data( QModelIndex(), Qt::SizeHintRole ).toSize(); From f8def37879030374759de1bf4de4f60a6eb3ac89 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 28 Oct 2011 12:31:15 +0200 Subject: [PATCH 028/109] * Support clicking info button for unknown albums in TrackView. --- src/libtomahawk/playlist/trackview.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/playlist/trackview.cpp b/src/libtomahawk/playlist/trackview.cpp index ca7e89e25..4d1ceabc4 100644 --- a/src/libtomahawk/playlist/trackview.cpp +++ b/src/libtomahawk/playlist/trackview.cpp @@ -556,7 +556,8 @@ TrackView::mousePressEvent( QMouseEvent* event ) } else { - //TODO + artist_ptr artist = Artist::get( item->query()->artist() ); + ViewManager::instance()->show( Album::get( artist, item->query()->album() ) ); } break; } From 4ffbd839ebd8f78c7abbd9fc777f2faa83dd4621 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Fri, 28 Oct 2011 12:08:02 -0400 Subject: [PATCH 029/109] Actually use cache in musicbrainz plugin. We were caching the data but not fetching from the cache --- .../infoplugins/generic/musicbrainzPlugin.cpp | 40 +++++++++++++++++-- .../infoplugins/generic/musicbrainzPlugin.h | 6 +-- .../infoplugins/generic/spotifyPlugin.cpp | 3 ++ .../infosystem/infosystemworker.cpp | 2 +- 4 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.cpp index c5659f27f..8c0b3c2f4 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.cpp @@ -50,7 +50,6 @@ MusicBrainzPlugin::namChangedSlot( QNetworkAccessManager *nam ) m_nam = QWeakPointer< QNetworkAccessManager >( nam ); } - void MusicBrainzPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { @@ -66,13 +65,48 @@ MusicBrainzPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) return; } + switch ( requestData.type ) + { + case InfoArtistReleases: + { + + Tomahawk::InfoSystem::InfoStringHash criteria; + criteria["artist"] = hash["artist"]; + + emit getCachedInfo( criteria, 2419200000, requestData ); + break; + } + + case InfoAlbumSongs: + { + + Tomahawk::InfoSystem::InfoStringHash criteria; + criteria["artist"] = hash["artist"]; + criteria["album"] = hash["album"]; + + emit getCachedInfo( criteria, 2419200000, requestData ); + + break; + } + + default: + { + Q_ASSERT( false ); + break; + } + } +} + +void +MusicBrainzPlugin::notInCacheSlot( InfoStringHash criteria, InfoRequestData requestData ) +{ switch ( requestData.type ) { case InfoArtistReleases: { QString requestString( "http://musicbrainz.org/ws/2/artist" ); QUrl url( requestString ); - url.addQueryItem( "query", hash["artist"] ); + url.addQueryItem( "query", criteria["artist"] ); QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); @@ -84,7 +118,7 @@ MusicBrainzPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { QString requestString( "http://musicbrainz.org/ws/2/artist" ); QUrl url( requestString ); - url.addQueryItem( "query", hash["artist"] ); + url.addQueryItem( "query", criteria["artist"] ); QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); diff --git a/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.h b/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.h index 77637e37b..444b30809 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.h +++ b/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.h @@ -43,6 +43,7 @@ public slots: protected slots: virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void notInCacheSlot( InfoStringHash criteria, InfoRequestData requestData ); virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data ) { @@ -51,11 +52,6 @@ protected slots: Q_UNUSED( data ); } -virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) - { - Q_UNUSED( criteria ); - Q_UNUSED( requestData ); - } private slots: void artistSearchSlot(); diff --git a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp index e05d739b6..1fba1d266 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp @@ -267,6 +267,9 @@ SpotifyPlugin::chartTypes() } + QVariantMap defaultMap; + defaultMap[ "Spotify" ] = QStringList() << "United States" << "Top Albums"; + m_allChartsMap[ "defaults" ] = defaultMap; m_allChartsMap.insert( "Spotify", QVariant::fromValue( charts ) ); } diff --git a/src/libtomahawk/infosystem/infosystemworker.cpp b/src/libtomahawk/infosystem/infosystemworker.cpp index 95cf8e63b..406fa1f01 100644 --- a/src/libtomahawk/infosystem/infosystemworker.cpp +++ b/src/libtomahawk/infosystem/infosystemworker.cpp @@ -198,7 +198,7 @@ InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, ui requestData.internalId = TomahawkUtils::infosystemRequestId(); else requestData.internalId = requestData.requestId; - + quint64 requestId = requestData.internalId; m_requestSatisfiedMap[ requestId ] = false; if ( timeoutMillis != 0 ) From 58b625ef0335cb96ecba7483fc9588fac5659839 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Fri, 28 Oct 2011 12:30:26 -0400 Subject: [PATCH 030/109] Only cache spotify charts for a week, and send defaults --- .../infosystem/infoplugins/generic/spotifyPlugin.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp index 1fba1d266..8965d6a94 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp @@ -140,7 +140,7 @@ SpotifyPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData ) criteria["chart_id"] = hash["chart_id"]; } - emit getCachedInfo( criteria, 0, requestData ); + emit getCachedInfo( criteria, 604800000 /* Expire chart cache in 1 week */, requestData ); } void SpotifyPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData ) @@ -268,7 +268,7 @@ SpotifyPlugin::chartTypes() } QVariantMap defaultMap; - defaultMap[ "Spotify" ] = QStringList() << "United States" << "Top Albums"; + defaultMap[ "spotify" ] = QStringList() << "United States" << "Top Albums"; m_allChartsMap[ "defaults" ] = defaultMap; m_allChartsMap.insert( "Spotify", QVariant::fromValue( charts ) ); From af5211aada3392c237e07180cf5e834dc829d38e Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Fri, 28 Oct 2011 13:19:33 -0400 Subject: [PATCH 031/109] Switch to using Rovi search api instead of album api This means we can search for albums with the artist name too, to avoid confusion --- .../infoplugins/generic/RoviPlugin.cpp | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp index f4fe5fb32..6c100daa8 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp @@ -79,9 +79,10 @@ RoviPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) } Tomahawk::InfoSystem::InfoStringHash criteria; + criteria["artist"] = hash["artist"]; criteria["album"] = hash["album"]; - emit getCachedInfo( criteria, 2419200000, requestData ); + emit getCachedInfo( criteria, 0, requestData ); } void @@ -91,8 +92,10 @@ RoviPlugin::notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomah { case InfoAlbumSongs: { - QUrl baseUrl = QUrl( "http://api.rovicorp.com/data/v1/album/tracks" ); - baseUrl.addQueryItem( "album", criteria[ "album" ] ); + QUrl baseUrl = QUrl( "http://api.rovicorp.com/search/v2/music/search" ); + baseUrl.addQueryItem( "query", QString( "%1 %2" ).arg( criteria[ "artist" ] ).arg( criteria[ "album" ] ) ); + baseUrl.addQueryItem( "entitytype", "album" ); + baseUrl.addQueryItem( "include", "album:tracks" ); QNetworkReply* reply = makeRequest( baseUrl ); @@ -137,15 +140,25 @@ RoviPlugin::albumLookupFinished() QJson::Parser p; bool ok; - QVariantMap result = p.parse( reply, &ok ).toMap(); + QVariantMap response = p.parse( reply, &ok ).toMap(); - if ( !ok || result.isEmpty() || !result.contains( "tracks" ) ) + if ( !ok || response.isEmpty() || !response.contains( "searchResponse" ) ) { - tLog() << "Error parsing JSON from Rovi!" << p.errorString() << result; + tLog() << "Error parsing JSON from Rovi!" << p.errorString() << response; + emit info( requestData, QVariant() ); + return; + } + + QVariantMap results = response[ "searchResponse" ].toMap().value( "results" ).toList().first().toMap(); + QVariantList tracks = results[ "album" ].toMap()[ "tracks" ].toList(); + + if ( tracks.isEmpty() ) + { + tLog() << "Error parsing JSON from Rovi!" << p.errorString() << response; emit info( requestData, QVariant() ); } - QVariantList tracks = result[ "tracks" ].toList(); + QStringList trackNameList; foreach ( const QVariant& track, tracks ) { @@ -161,6 +174,7 @@ RoviPlugin::albumLookupFinished() Tomahawk::InfoSystem::InfoStringHash criteria; criteria["artist"] = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>()["artist"]; + criteria["album"] = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>()["album"]; emit updateCache( criteria, 0, requestData.type, returnedData ); } From 4ad6b2bebfc0fed1f4edeb59af013955a4752519 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Fri, 28 Oct 2011 15:25:54 -0400 Subject: [PATCH 032/109] Allow artist pages to have a random/repeat mode --- src/libtomahawk/CMakeLists.txt | 1 + .../widgets/infowidgets/AlbumInfoWidget.cpp | 6 + .../widgets/infowidgets/AlbumInfoWidget.h | 2 +- .../widgets/infowidgets/ArtistInfoWidget.cpp | 11 ++ .../widgets/infowidgets/ArtistInfoWidget.h | 7 +- .../widgets/infowidgets/ArtistInfoWidget_p.h | 106 ++++++++++++++++++ 6 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 src/libtomahawk/widgets/infowidgets/ArtistInfoWidget_p.h diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 7cb24f02f..92e0bd1e8 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -447,6 +447,7 @@ set( libHeaders widgets/SocialPlaylistWidget.h widgets/infowidgets/sourceinfowidget.h widgets/infowidgets/ArtistInfoWidget.h + widgets/infowidgets/ArtistInfoWidget_p.h widgets/infowidgets/AlbumInfoWidget.h widgets/Breadcrumb.h widgets/BreadcrumbButton.h diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp index 975e2f605..0105e6b92 100644 --- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp @@ -88,6 +88,12 @@ AlbumInfoWidget::~AlbumInfoWidget() delete ui; } +PlaylistInterface* +AlbumInfoWidget::playlistInterface() const +{ + return ui->tracksView->playlistInterface(); +} + void AlbumInfoWidget::onModeToggle() diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h index bb5c521d0..17e47f0d5 100644 --- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h +++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h @@ -64,7 +64,7 @@ public: void load( const Tomahawk::album_ptr& album ); virtual QWidget* widget() { return this; } - virtual Tomahawk::PlaylistInterface* playlistInterface() const { return 0; } + virtual Tomahawk::PlaylistInterface* playlistInterface() const; virtual QString title() const { return m_title; } virtual QString description() const { return m_description; } diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index 32a32b083..f57866878 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -17,12 +17,14 @@ */ #include "ArtistInfoWidget.h" +#include "ArtistInfoWidget_p.h" #include "ui_ArtistInfoWidget.h" #include "audio/audioengine.h" #include "viewmanager.h" #include "playlist/treemodel.h" #include "playlist/playlistmodel.h" +#include "playlist/treeproxymodel.h" #include "database/databasecommand_alltracks.h" #include "database/databasecommand_allalbums.h" @@ -45,6 +47,8 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* { ui->setupUi( this ); + m_plInterface = new MetaPlaylistInterface( this ); + ui->albums->setFrameShape( QFrame::NoFrame ); ui->albums->setAttribute( Qt::WA_MacShowFocusRect, 0 ); ui->relatedArtists->setFrameShape( QFrame::NoFrame ); @@ -93,9 +97,16 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* ArtistInfoWidget::~ArtistInfoWidget() { + delete m_plInterface; delete ui; } +PlaylistInterface* +ArtistInfoWidget::playlistInterface() const +{ + return m_plInterface; +} + void ArtistInfoWidget::onModeToggle() diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h index 65ef84f29..754578bef 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h @@ -47,6 +47,8 @@ namespace Ui class ArtistInfoWidget; } +class MetaPlaylistInterface; + class DLLEXPORT ArtistInfoWidget : public QWidget, public Tomahawk::ViewPage { Q_OBJECT @@ -66,7 +68,7 @@ public: void load( const Tomahawk::artist_ptr& artist ); virtual QWidget* widget() { return this; } - virtual Tomahawk::PlaylistInterface* playlistInterface() const { return 0; } + virtual Tomahawk::PlaylistInterface* playlistInterface() const; virtual QString title() const { return m_title; } virtual QString description() const { return m_description; } @@ -103,6 +105,7 @@ private: TreeModel* m_relatedModel; TreeModel* m_albumsModel; PlaylistModel* m_topHitsModel; + MetaPlaylistInterface* m_plInterface; OverlayButton* m_button; @@ -111,6 +114,8 @@ private: QString m_longDescription; QString m_infoId; QPixmap m_pixmap; + + friend class MetaPlaylistInterface; }; #endif // ARTISTINFOWIDGET_H diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget_p.h b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget_p.h new file mode 100644 index 000000000..4a3b6b50a --- /dev/null +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget_p.h @@ -0,0 +1,106 @@ +/* === 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 ARTISTINFOWIDGET_P_H +#define ARTISTINFOWIDGET_P_H + +#include "ArtistInfoWidget.h" +#include "ui_ArtistInfoWidget.h" +#include "playlistinterface.h" +#include "treeproxymodel.h" +#include "result.h" + +#include + +class MetaPlaylistInterface : public QObject, public Tomahawk::PlaylistInterface +{ + Q_OBJECT +public: + explicit MetaPlaylistInterface( ArtistInfoWidget* w ) + : PlaylistInterface( this ) + , m_w( w ) + { + connect( m_w->ui->albums->proxyModel(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ), + SLOT( anyRepeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ) ); + connect( m_w->ui->relatedArtists->proxyModel(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ), + SLOT( anyRepeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ) ); + connect( m_w->ui->topHits->proxyModel(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ), + SLOT( anyRepeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ) ); + + connect( m_w->ui->albums->proxyModel(), SIGNAL( shuffleModeChanged( bool ) ), + SLOT( anyShuffleChanged( bool ) ) ); + connect( m_w->ui->relatedArtists->proxyModel(), SIGNAL( shuffleModeChanged( bool ) ), + SLOT( anyShuffleChanged( bool ) ) ); + connect( m_w->ui->topHits->proxyModel(), SIGNAL( shuffleModeChanged( bool ) ), + SLOT( anyShuffleChanged( bool ) ) ); + } + virtual ~MetaPlaylistInterface() {} + + + // Any one is fine, we keep them all synched + virtual RepeatMode repeatMode() const { return m_w->ui->albums->proxyModel()->repeatMode(); } + + virtual bool shuffled() const { return m_w->ui->albums->proxyModel()->shuffled(); } + + // Do nothing + virtual Tomahawk::result_ptr currentItem() const { return Tomahawk::result_ptr(); } + virtual Tomahawk::result_ptr siblingItem( int ) { return Tomahawk::result_ptr(); } + virtual int trackCount() const { return 0; } + virtual QList< Tomahawk::query_ptr > tracks() { return QList< Tomahawk::query_ptr >(); } + virtual int unfilteredTrackCount() const { return 0; } + +public slots: + virtual void setRepeatMode( RepeatMode mode ) + { + m_w->ui->albums->proxyModel()->setRepeatMode( mode ); + m_w->ui->relatedArtists->proxyModel()->setRepeatMode( mode ); + m_w->ui->topHits->proxyModel()->setRepeatMode( mode ); + } + + virtual void setShuffled( bool enabled ) + { + m_w->ui->albums->proxyModel()->setShuffled( enabled ); + m_w->ui->relatedArtists->proxyModel()->setShuffled( enabled ); + m_w->ui->topHits->proxyModel()->setShuffled( enabled ); + } + +signals: + void repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode mode ); + void shuffleModeChanged( bool enabled ); + + void trackCountChanged( unsigned int tracks ); + void sourceTrackCountChanged( unsigned int tracks ); + void nextTrackReady(); + +private slots: + void anyRepeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode mode ) + { + emit repeatModeChanged( mode ); + } + + void anyShuffleChanged( bool enabled ) + { + emit shuffleModeChanged( enabled ); + } + +private: + ArtistInfoWidget* m_w; + +}; + +#endif From 5eef57f3b80fb8b3e5862319373402e9b51a2d9c Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Fri, 28 Oct 2011 15:45:05 -0400 Subject: [PATCH 033/109] Fix regression in infosystem by working around it. Can't reuse requestData objects without changing their ids. --- src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index f57866878..f8743a61a 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -189,12 +189,15 @@ ArtistInfoWidget::load( const artist_ptr& artist ) requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo ); requestData.type = Tomahawk::InfoSystem::InfoArtistImages; + requestData.requestId = TomahawkUtils::infosystemRequestId(); Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); requestData.type = Tomahawk::InfoSystem::InfoArtistSimilars; + requestData.requestId = TomahawkUtils::infosystemRequestId(); Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); requestData.type = Tomahawk::InfoSystem::InfoArtistSongs; + requestData.requestId = TomahawkUtils::infosystemRequestId(); Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); } From 7549d7311518c6272a03d2be6407cb5b9b2c5fdf Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Fri, 28 Oct 2011 15:45:52 -0400 Subject: [PATCH 034/109] Don't crash if Rovi returns no results --- .../infosystem/infoplugins/generic/RoviPlugin.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp index 6c100daa8..cb4a57d87 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp @@ -149,7 +149,13 @@ RoviPlugin::albumLookupFinished() return; } - QVariantMap results = response[ "searchResponse" ].toMap().value( "results" ).toList().first().toMap(); + QVariantList resultList = response[ "searchResponse" ].toMap().value( "results" ).toList(); + if ( resultList.size() == 0 ) + { + emit info( requestData, QVariant() ); + } + + QVariantMap results = resultList.first().toMap(); QVariantList tracks = results[ "album" ].toMap()[ "tracks" ].toList(); if ( tracks.isEmpty() ) From 5f7bd6087f33e88ac19f2088621c3db36e26d548 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Fri, 28 Oct 2011 16:49:22 -0400 Subject: [PATCH 035/109] Helps to return when i want to return... --- src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp index cb4a57d87..c5810440e 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp @@ -153,6 +153,7 @@ RoviPlugin::albumLookupFinished() if ( resultList.size() == 0 ) { emit info( requestData, QVariant() ); + return; } QVariantMap results = resultList.first().toMap(); From f8d48cc9fcb4396a131178a2c287f32058f14419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Fri, 28 Oct 2011 22:57:28 +0200 Subject: [PATCH 036/109] Adding Hypem and fix race in spotify(?) --- src/libtomahawk/CMakeLists.txt | 2 + .../infoplugins/generic/chartsplugin.cpp | 2 +- .../infoplugins/generic/hypemPlugin.cpp | 421 ++++++++++++++++++ .../infoplugins/generic/hypemPlugin.h | 93 ++++ .../infoplugins/generic/spotifyPlugin.cpp | 20 +- .../infoplugins/generic/spotifyPlugin.h | 4 +- .../infosystem/infosystemworker.cpp | 5 + 7 files changed, 543 insertions(+), 4 deletions(-) create mode 100644 src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp create mode 100644 src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.h diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 92e0bd1e8..a85ad861b 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -112,6 +112,7 @@ set( libSources infosystem/infoplugins/generic/lastfmplugin.cpp infosystem/infoplugins/generic/chartsplugin.cpp infosystem/infoplugins/generic/spotifyPlugin.cpp + infosystem/infoplugins/generic/hypemPlugin.cpp infosystem/infoplugins/generic/musixmatchplugin.cpp infosystem/infoplugins/generic/musicbrainzPlugin.cpp infosystem/infoplugins/generic/RoviPlugin.cpp @@ -340,6 +341,7 @@ set( libHeaders infosystem/infoplugins/generic/lastfmplugin.h infosystem/infoplugins/generic/chartsplugin.h infosystem/infoplugins/generic/spotifyPlugin.h + infosystem/infoplugins/generic/hypemPlugin.h infosystem/infoplugins/generic/musixmatchplugin.h infosystem/infoplugins/generic/musicbrainzPlugin.h infosystem/infoplugins/generic/RoviPlugin.h diff --git a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp index a0fe25503..9185a1321 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp @@ -107,7 +107,7 @@ ChartsPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) qDebug() << Q_FUNC_INFO << requestData.customData; InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); - bool foundSource; + bool foundSource = false; switch ( requestData.type ) { diff --git a/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp new file mode 100644 index 000000000..b6b9a987b --- /dev/null +++ b/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp @@ -0,0 +1,421 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Hugo Lindström + * 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 "hypemPlugin.h" + +#include +#include +#include +#include +#include +#include + +#include "album.h" +#include "typedefs.h" +#include "audio/audioengine.h" +#include "tomahawksettings.h" +#include "utils/tomahawkutils.h" +#include "utils/logger.h" + +#define HYPEM_URL "http://hypem.com/playlist/" +#define HYPEM_END_URL "json/1/data.js" +#include +#include + +using namespace Tomahawk::InfoSystem; + + +hypemPlugin::hypemPlugin() + : InfoPlugin() + , m_chartsFetchJobs( 0 ) +{ + + m_supportedGetTypes << InfoChart << InfoChartCapabilities; + m_types << "Artists" << "Tracks" << "Recent by Tag"; + + m_trackTypes << "Last 3 Days" + << "Last Week" + << "No Remixes" + << "On Twitter"; + + m_byTagTypes << "Dance" + << "Experimental" + << "Electronic" + << "Funk" + << "Hip-hop" + << "Indie" + << "Instrumental" + << "Post-punk" + << "Rock" + << "Singer-songwriter" + << "Alternative" + << "Pop" + << "Female" + << "Vocalist" + << "Folk" + << "Electro" + << "Lo-fi" + << "Psychedelic" + << "Rap" + << "British" + << "Ambient" + << "Dubstep" + << "House" + << "Chillwave" + << "Dreampop" + << "Shoegaze" + << "Chillout" + << "Soul" + << "French" + << "Acoustic" + << "Canadian" + << "60s" + << "80s" + << "Techno" + << "Punk" + << "New wave"; + chartTypes(); + +} + + + + +hypemPlugin::~hypemPlugin() +{ + qDebug() << Q_FUNC_INFO; +} + + +void +hypemPlugin::namChangedSlot( QNetworkAccessManager *nam ) +{ + tDebug() << "hypemPlugin: namChangedSLot"; + qDebug() << Q_FUNC_INFO; + if( !nam ) + return; + + m_nam = QWeakPointer< QNetworkAccessManager >( nam ); + + +} + + +void +hypemPlugin::dataError( Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + emit info( requestData, QVariant() ); + return; +} + + +void +hypemPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + qDebug() << Q_FUNC_INFO << requestData.caller; + qDebug() << Q_FUNC_INFO << requestData.customData; + + InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); + + + switch ( requestData.type ) + { + + case InfoChart: + if ( !hash.contains( "chart_source" ) || hash["chart_source"] != "hypem" ) + { + dataError( requestData ); + break; + } + qDebug() << Q_FUNC_INFO << "InfoCHart req for" << hash["chart_source"]; + fetchChart( requestData ); + break; + + case InfoChartCapabilities: + fetchChartCapabilities( requestData ); + break; + default: + dataError( requestData ); + } +} + + +void +hypemPlugin::pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input ) +{ + Q_UNUSED( caller ) + Q_UNUSED( type) + Q_UNUSED( input ) +} + + +void +hypemPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + + if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) + { + dataError( requestData ); + return; + } + + InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); + Tomahawk::InfoSystem::InfoStringHash criteria; + + /// Each request needs to contain both a id and source + if ( !hash.contains( "chart_id" ) && !hash.contains( "chart_source" ) ) + { + dataError( requestData ); + return; + + } + /// Set the criterias for current chart + criteria["chart_id"] = hash["chart_id"]; + criteria["chart_source"] = hash["chart_source"]; + + emit getCachedInfo( criteria, 0, requestData ); +} + +void +hypemPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) + { + dataError( requestData ); + return; + } + + Tomahawk::InfoSystem::InfoStringHash criteria; + emit getCachedInfo( criteria, 0, requestData ); +} + +void +hypemPlugin::notInCacheSlot( QHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + if ( !m_nam.data() ) + { + tLog() << "Have a null QNAM, uh oh"; + emit info( requestData, QVariant() ); + return; + } + + + switch ( requestData.type ) + { + case InfoChart: + { + /// Fetch the chart, we need source and id + + QUrl url = QUrl( QString( HYPEM_URL "%1/%2" ).arg( criteria["chart_id"].toLower() ).arg(HYPEM_END_URL) ); + qDebug() << Q_FUNC_INFO << "Getting chart url" << url; + + QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) ); + reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); + connect( reply, SIGNAL( finished() ), SLOT( chartReturned() ) ); + return; + + + } + + case InfoChartCapabilities: + { + if ( m_chartsFetchJobs > 0 ) + { + qDebug() << Q_FUNC_INFO << "InfoChartCapabilities still fetching!"; + m_cachedRequests.append( requestData ); + return; + } + + emit info( requestData, m_allChartsMap ); + return; + } + + default: + { + tLog() << Q_FUNC_INFO << "Couldn't figure out what to do with this type of request after cache miss"; + emit info( requestData, QVariant() ); + return; + } + } +} + + +void +hypemPlugin::chartTypes() +{ + /// Get possible chart type for specifichypemPlugin: InfoChart types returned chart source + tDebug() << Q_FUNC_INFO << "Got hypem types"; + + QVariantMap charts; + + foreach(QVariant types, m_types ) + { + QList< InfoStringHash > chart_types; + QList< InfoStringHash > pop_charts; + InfoStringHash c; + + if(types.toString() != "Artists") + { + + if(types.toString() == "Tracks") + { + + foreach(QVariant trackType, m_trackTypes) + { + QString typeId; + if(trackType.toString() == "Last 3 Days") + typeId = "popular/3day"; + + if(trackType.toString() == "Last Week") + typeId = "popular/lastweek"; + + if(trackType.toString() == "No Remixes") + typeId = "popular/noremix"; + + if(trackType.toString() == "On Twitter") + typeId = "popular/twitter"; + + c[ "id" ] = typeId; + c[ "label" ] = trackType.toString(); + c[ "type" ] = "tracks"; + pop_charts.append( c ); + } + + chart_types.append( pop_charts ); + + } + else if(types.toString() == "Recent by Tag") + { + foreach(QVariant tagTypes, m_byTagTypes) + { + + c[ "id" ] = "tags/" + tagTypes.toString().toLower(); + c[ "label" ] = tagTypes.toString(); + c[ "type" ] = tagTypes.toString(); + chart_types.append( c ); + } + + } + + }else + { + InfoStringHash c; + c[ "id" ] = "popular/artists"; + c[ "label" ] = "Most Recent"; + c[ "type" ] = "artists"; + chart_types.append( c ); + } + + charts.insert( types.toString(), QVariant::fromValue >( chart_types ) ); + } + + + m_allChartsMap.insert( "Hypem", QVariant::fromValue( charts ) ); + qDebug() << "hypemPlugin:Chartstype: " << m_allChartsMap; + + +} + +void +hypemPlugin::chartReturned() +{ + + /// Chart request returned something! Woho + QNetworkReply* reply = qobject_cast( sender() ); + QString url = reply->url().toString(); + QVariantMap returnedData; + if ( reply->error() == QNetworkReply::NoError ) + { + QJson::Parser p; + bool ok; + QVariantMap res = p.parse( reply, &ok ).toMap(); + + if ( !ok ) + { + tLog() << "Failed to parse json from chart lookup:" << p.errorString() << "On line" << p.errorLine(); + return; + } + + /// SO we have a result, parse it! + QList< InfoStringHash > top_tracks; + QStringList top_artists; + + if( url.contains( "artists" ) ) + setChartType( Artist ); + else + setChartType( Track ); + + foreach(QVariant result, res ) + { + QString title, artist; + QVariantMap chartMap = result.toMap(); + + if ( !chartMap.isEmpty() ) + { + + title = chartMap.value( "title" ).toString(); + artist = chartMap.value( "artist" ).toString(); + + if( chartType() == Track ) + { + InfoStringHash pair; + pair["artist"] = artist; + pair["track"] = title; + top_tracks << pair; + + qDebug() << "HypemChart type is track"; + } + + + if( chartType() == Artist ) + { + + top_artists << artist; + qDebug() << "HypemChart type is artist"; + + } + } + } + + if( chartType() == Track ) + { + tDebug() << "HypemPlugin:" << "\tgot " << top_tracks.size() << " tracks"; + returnedData["tracks"] = QVariant::fromValue( top_tracks ); + returnedData["type"] = "tracks"; + } + + + + if( chartType() == Artist ) + { + tDebug() << "HypemPlugin:" << "\tgot " << top_artists.size() << " artists"; + returnedData["artists"] = top_artists; + returnedData["type"] = "artists"; + } + + Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); + + + emit info( requestData, returnedData ); + // TODO update cache + } + else + qDebug() << "Network error in fetching chart:" << reply->url().toString(); + +} diff --git a/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.h b/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.h new file mode 100644 index 000000000..a13c0299d --- /dev/null +++ b/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.h @@ -0,0 +1,93 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Hugo Lindström + * + * 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 hypemPlugin_H +#define hypemPlugin_H + +#include "infosystem/infosystem.h" +#include "infosystem/infosystemworker.h" +#include +#include + +class QNetworkReply; + +namespace Tomahawk +{ + +namespace InfoSystem +{ + +class hypemPlugin : public InfoPlugin +{ + Q_OBJECT + +public: + hypemPlugin(); + virtual ~hypemPlugin(); + + enum ChartType { + None = 0x00, + Track = 0x01, + Album = 0x02, + Artist = 0x04 + + }; + void setChartType( ChartType type ) { m_chartType = type; } + ChartType chartType() const { return m_chartType; } + +public slots: + void chartReturned(); + void chartTypes(); + void namChangedSlot( QNetworkAccessManager *nam ); + +protected slots: + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); + + virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data ); + +private: + void fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData ); + void dataError( Tomahawk::InfoSystem::InfoRequestData requestData ); + + QVariantList m_chartResources; + QList m_charts; + + + ChartType m_chartType; + QVariantMap m_allChartsMap; + QVariantList m_types; + QVariantList m_popularTypes; + QVariantList m_trackTypes; + QVariantList m_byTagTypes; + + + uint m_chartsFetchJobs; + QList< InfoRequestData > m_cachedRequests; + + QHash< QString, QString > m_cachedCountries; + + QWeakPointer< QNetworkAccessManager > m_nam; +}; + +} + +} + +#endif // hypemPlugin_H diff --git a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp index 8965d6a94..c9549c81e 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp @@ -42,6 +42,7 @@ using namespace Tomahawk::InfoSystem; SpotifyPlugin::SpotifyPlugin() : InfoPlugin() + , m_chartsFetchJobs( 0 ) { m_supportedGetTypes << InfoChart << InfoChartCapabilities; @@ -72,6 +73,7 @@ SpotifyPlugin::namChangedSlot( QNetworkAccessManager *nam ) QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) ); tDebug() << Q_FUNC_INFO << "fetching:" << url; connect( reply, SIGNAL( finished() ), SLOT( chartTypes() ) ); + m_chartsFetchJobs++; } @@ -184,7 +186,13 @@ SpotifyPlugin::notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, To } case InfoChartCapabilities: { - qDebug() << Q_FUNC_INFO << "EMITTING CHART" << m_allChartsMap; + if ( m_chartsFetchJobs > 0 ) + { + qDebug() << Q_FUNC_INFO << "InfoChartCapabilities still fetching!"; + m_cachedRequests.append( requestData ); + return; + } + emit info( requestData, m_allChartsMap ); return; } @@ -278,6 +286,16 @@ SpotifyPlugin::chartTypes() tLog() << Q_FUNC_INFO << "Error fetching charts:" << reply->errorString(); } + m_chartsFetchJobs--; + if ( !m_cachedRequests.isEmpty() && m_chartsFetchJobs == 0 ) + { + foreach ( InfoRequestData request, m_cachedRequests ) + { + emit info( request, m_allChartsMap ); + } + m_cachedRequests.clear(); + } + } void diff --git a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.h b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.h index 084da24ee..4a27d826c 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.h +++ b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.h @@ -68,8 +68,8 @@ private: ChartType m_chartType; QVariantMap m_allChartsMap; - - + uint m_chartsFetchJobs; + QList< InfoRequestData > m_cachedRequests; QWeakPointer< QNetworkAccessManager > m_nam; }; diff --git a/src/libtomahawk/infosystem/infosystemworker.cpp b/src/libtomahawk/infosystem/infosystemworker.cpp index 406fa1f01..726523bd6 100644 --- a/src/libtomahawk/infosystem/infosystemworker.cpp +++ b/src/libtomahawk/infosystem/infosystemworker.cpp @@ -29,6 +29,7 @@ #include "infoplugins/generic/spotifyPlugin.h" #include "infoplugins/generic/lastfmplugin.h" #include "infoplugins/generic/musicbrainzPlugin.h" +#include "infoplugins/generic/hypemPlugin.h" #include "utils/tomahawkutils.h" #include "utils/logger.h" @@ -99,6 +100,10 @@ InfoSystemWorker::init( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache> cac InfoPluginPtr spotptr( new SpotifyPlugin() ); m_plugins.append( spotptr ); registerInfoTypes( spotptr, spotptr.data()->supportedGetTypes(), spotptr.data()->supportedPushTypes() ); + InfoPluginPtr hypeptr( new hypemPlugin() ); + m_plugins.append( hypeptr ); + registerInfoTypes( hypeptr, hypeptr.data()->supportedGetTypes(), hypeptr.data()->supportedPushTypes() ); + #ifdef Q_WS_MAC InfoPluginPtr admptr( new AdiumPlugin() ); From 51d89c72103dc7325c16b7555f7d96bb91480250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Fri, 28 Oct 2011 22:58:57 +0200 Subject: [PATCH 037/109] Adding hypem --- src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp index b6b9a987b..f93bee853 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp @@ -1,7 +1,6 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Hugo Lindström - * 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 From 5df328424d668a66f387db25b784d0eee1371e47 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Fri, 28 Oct 2011 17:11:58 -0400 Subject: [PATCH 038/109] Add shuffle/repeat support to treeview --- src/libtomahawk/playlist/treeproxymodel.cpp | 22 ++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/playlist/treeproxymodel.cpp b/src/libtomahawk/playlist/treeproxymodel.cpp index 5dc0e0cfa..c6c7f99cf 100644 --- a/src/libtomahawk/playlist/treeproxymodel.cpp +++ b/src/libtomahawk/playlist/treeproxymodel.cpp @@ -333,10 +333,30 @@ TreeProxyModel::siblingItem( int itemsAway, bool readOnly ) QModelIndex idx = currentIndex(); + if ( m_shuffled ) + idx = index( qrand() % rowCount( idx.parent() ), 0, idx.parent() ); + else if ( m_repeatMode == PlaylistInterface::RepeatOne ) + idx = index( idx.row(), 0, idx.parent() ); + else + idx = index( idx.row() + ( itemsAway > 0 ? 1 : -1 ), 0, idx.parent() ); + + if ( !idx.isValid() && m_repeatMode == PlaylistInterface::RepeatAll ) + { + if ( itemsAway > 0 ) + { + // reset to first item + idx = index( 0, 0, currentIndex().parent() ); + } + else + { + // reset to last item + idx = index( rowCount( currentIndex().parent() ) - 1, 0, currentIndex().parent() ); + } + } + // Try to find the next available PlaylistItem (with results) if ( idx.isValid() ) do { - idx = index( idx.row() + ( itemsAway > 0 ? 1 : -1 ), 0, idx.parent() ); if ( !idx.isValid() ) break; From 5d13694530b9f1b868cb9a96edf2aec3982721a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Fri, 28 Oct 2011 23:23:30 +0200 Subject: [PATCH 039/109] Call it Hype Machine instead --- src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp index f93bee853..2068c1fa7 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp @@ -325,7 +325,7 @@ hypemPlugin::chartTypes() } - m_allChartsMap.insert( "Hypem", QVariant::fromValue( charts ) ); + m_allChartsMap.insert( "Hype Machine", QVariant::fromValue( charts ) ); qDebug() << "hypemPlugin:Chartstype: " << m_allChartsMap; From 9476c3a3deaca9466ff8e3e40bdf8e11a8650885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Fri, 28 Oct 2011 23:33:00 +0200 Subject: [PATCH 040/109] And also dont break it, its so fragile --- src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp index 2068c1fa7..dd6a4b7b1 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp @@ -137,7 +137,7 @@ hypemPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { case InfoChart: - if ( !hash.contains( "chart_source" ) || hash["chart_source"] != "hypem" ) + if ( !hash.contains( "chart_source" ) || hash["chart_source"].toLower() != "hype machine" ) { dataError( requestData ); break; From 5f73c3fb0e92cc9ef6433e10bbc2f580ec50726c Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 28 Oct 2011 17:32:44 -0400 Subject: [PATCH 041/109] If people reuse the same requestId, reassign it for them and print a warning. Will cause problems if they are relying on the requestId to map to something, but in that case they'd have problems mapping multiple things to the same requestId in the first place... --- src/libtomahawk/infosystem/infosystemworker.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/infosystem/infosystemworker.cpp b/src/libtomahawk/infosystem/infosystemworker.cpp index 726523bd6..a744f76f3 100644 --- a/src/libtomahawk/infosystem/infosystemworker.cpp +++ b/src/libtomahawk/infosystem/infosystemworker.cpp @@ -199,8 +199,12 @@ InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, ui foundOne = true; - if ( allSources ) + if ( allSources || m_savedRequestMap.contains( requestData.requestId ) ) + { + if ( m_savedRequestMap.contains( requestData.requestId ) ) + tDebug() << Q_FUNC_INFO << "Warning: reassigning requestId because it already exists"; requestData.internalId = TomahawkUtils::infosystemRequestId(); + } else requestData.internalId = requestData.requestId; From 2b7623b6d5a65c5a1c6360375712e853f0c8d9bf Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 29 Oct 2011 04:16:11 +0200 Subject: [PATCH 042/109] * Added a drop shadow to cover images. At some point I'll make it perfect, good enough for now tho. From fce45b1d8d599412950235627f9b752bff5cb401 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 29 Oct 2011 04:26:02 +0200 Subject: [PATCH 043/109] * Commit dropshadow for reals. --- data/images/cover-shadow.png | Bin 3615 -> 0 bytes resources.qrc | 1 - .../playlist/albumitemdelegate.cpp | 29 +++++++++++++++--- 3 files changed, 25 insertions(+), 5 deletions(-) delete mode 100644 data/images/cover-shadow.png diff --git a/data/images/cover-shadow.png b/data/images/cover-shadow.png deleted file mode 100644 index e362118c62b06dbb8e6e25b63072de393d23e334..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3615 zcmeAS@N?(olHy`uVBq!ia0y~yV44EL9Be?5UFTQW04d25*NBqf{Irtt#G+IN$CUh} zR0Yr6#Prml)Wnp^!jq{$MZ5<+T^vIyZoRpCHt)8ZNW;a29{*KdEPBCx#c}hw#ZUf< zv)*FY=+e7&WSh#SC5FlI{cUAkWq~uFGl=&|b}~*m+b%ijRo$6;zdYdUxN+ZkxPQj0`vCEoIh!elz@d|H_R|W1BYR6n_7C_O$1^nrT2IwtRU| zS`}5X$N&8LMKyZL9UO^(3b}N&K!aCQ=rs;TtwvgbR#DuRU;7Z)HlF`n z{{8cvr-}{z=d&|3d@en+dH!>~^V4nqM+Vf*dfr~SZ~pV$HrGGLJS#5wC(poe?vly* z#Cg9f^Pc}SJ}>|IkKOtF^>YtQGiPA?P%!V+I_=|0%-f_2 zO*NtwBmLsOUE{Aj_1+U$AIvEYSXX*4;%KU|PW!R=BNNv6?>l4Xzf*F#IIuC5EyqTHteM^j?v>YSca{_u&R z+V`J-x9tI%6`r$yQ^$|+y#1FN3xEH9vJ2d}o~v_S`B%`66xx6n&uE@3{oXtzMy{JvH_9Z#$OM8oQs)B7UJE(<&Ok1SaMbjSS}Ij*UHpPHS0tsd=Yxo*mU)%Tyy4ZHW@Rv`n!Ho+Ob_1p#w-ifo9_IJ$e R1vau6JYD@<);T3K0RWW|XA}Sc diff --git a/resources.qrc b/resources.qrc index 02ee5f9ab..2260960e0 100644 --- a/resources.qrc +++ b/resources.qrc @@ -4,7 +4,6 @@ data/images/avatar-dude.png data/images/back-pressed.png data/images/back-rest.png - data/images/cover-shadow.png data/images/filter.png data/images/loved.png data/images/not-loved.png diff --git a/src/libtomahawk/playlist/albumitemdelegate.cpp b/src/libtomahawk/playlist/albumitemdelegate.cpp index 15a6844cd..c81722e0e 100644 --- a/src/libtomahawk/playlist/albumitemdelegate.cpp +++ b/src/libtomahawk/playlist/albumitemdelegate.cpp @@ -38,7 +38,6 @@ AlbumItemDelegate::AlbumItemDelegate( QAbstractItemView* parent, AlbumProxyModel , m_view( parent ) , m_model( proxy ) { - m_shadowPixmap = QPixmap( RESPATH "images/cover-shadow.png" ); m_defaultCover = QPixmap( RESPATH "images/no-album-art-placeholder.png" ); } @@ -63,9 +62,30 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, qApp->style()->drawControl( QStyle::CE_ItemViewItem, &opt, painter ); painter->save(); + painter->setRenderHint( QPainter::Antialiasing ); -// painter->setRenderHint( QPainter::Antialiasing ); -// painter->drawPixmap( option.rect.adjusted( 4, 4, -4, -38 ), m_shadowPixmap ); + if ( !( option.state & QStyle::State_Selected ) ) + { + QRect shadowRect = option.rect.adjusted( 5, 4, -5, -40 ); + painter->setPen( QColor( 90, 90, 90 ) ); + painter->drawRoundedRect( shadowRect, 0.5, 0.5 ); + + QPen shadowPen( QColor( 30, 30, 30 ) ); + shadowPen.setWidth( 0.4 ); + painter->drawLine( shadowRect.bottomLeft() + QPoint( -1, 2 ), shadowRect.bottomRight() + QPoint( 1, 2 ) ); + + shadowPen.setColor( QColor( 160, 160, 160 ) ); + painter->setPen( shadowPen ); + painter->drawLine( shadowRect.topLeft() + QPoint( -1, 2 ), shadowRect.bottomLeft() + QPoint( -1, 2 ) ); + painter->drawLine( shadowRect.topRight() + QPoint( 2, 2 ), shadowRect.bottomRight() + QPoint( 2, 2 ) ); + painter->drawLine( shadowRect.bottomLeft() + QPoint( 0, 3 ), shadowRect.bottomRight() + QPoint( 0, 3 ) ); + + shadowPen.setColor( QColor( 180, 180, 180 ) ); + painter->setPen( shadowPen ); + painter->drawLine( shadowRect.topLeft() + QPoint( -2, 3 ), shadowRect.bottomLeft() + QPoint( -2, 1 ) ); + painter->drawLine( shadowRect.topRight() + QPoint( 3, 3 ), shadowRect.bottomRight() + QPoint( 3, 1 ) ); + painter->drawLine( shadowRect.bottomLeft() + QPoint( 0, 4 ), shadowRect.bottomRight() + QPoint( 0, 4 ) ); + } QPixmap cover = item->cover.isNull() ? m_defaultCover : item->cover; QRect r = option.rect.adjusted( 6, 5, -6, -41 ); @@ -127,11 +147,12 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, } else { + painter->setFont( boldFont ); to.setAlignment( Qt::AlignHCenter | Qt::AlignTop ); text = painter->fontMetrics().elidedText( item->album()->name(), Qt::ElideRight, textRect.width() - 3 ); painter->drawText( textRect, text, to ); - painter->setFont( boldFont ); + painter->setPen( opt.palette.color( QPalette::Dark ) ); to.setAlignment( Qt::AlignHCenter | Qt::AlignBottom ); text = painter->fontMetrics().elidedText( item->album()->artist()->name(), Qt::ElideRight, textRect.width() - 3 ); painter->drawText( textRect, text, to ); From 22a502fd5982ec8593e5dc777a0efa80d6010cfc Mon Sep 17 00:00:00 2001 From: Jason Herskowitz Date: Fri, 28 Oct 2011 22:35:34 -0400 Subject: [PATCH 044/109] Put CD placeholder icon into jewel case --- data/images/no-album-art-placeholder.png | Bin 1054090 -> 1054090 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/data/images/no-album-art-placeholder.png b/data/images/no-album-art-placeholder.png index 3db3a8dd376bdac04a954f7eecec63d678fbc7ee..c45199b4698999c0dba43a0038771158b85eb0e0 100644 GIT binary patch delta 12638 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 s)tggWwbHlw*8RXXn^_&goqELi@pI Date: Fri, 28 Oct 2011 22:47:52 -0400 Subject: [PATCH 045/109] Downsize image in filesize and dimension size - don't let the cd touch the sides --- data/images/no-album-art-placeholder.png | Bin 1054090 -> 69076 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/data/images/no-album-art-placeholder.png b/data/images/no-album-art-placeholder.png index c45199b4698999c0dba43a0038771158b85eb0e0..a117306e28a0c91c6e2e27ab66543108aaa9f411 100644 GIT binary patch literal 69076 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 P_-)^}%{5yceA@p3Md!Hz literal 1054090 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 HfBydiC>oqh From 5ea8586c599cecead2e865446aa8e6c502072add Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 29 Oct 2011 08:49:42 +0200 Subject: [PATCH 046/109] * Should fix shutdown sequence. --- src/tomahawkapp.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp index c2a3c098b..874d25fb8 100644 --- a/src/tomahawkapp.cpp +++ b/src/tomahawkapp.cpp @@ -181,8 +181,6 @@ TomahawkApp::init() TomahawkUtils::setProxyFactory( proxyFactory ); - Echonest::Config::instance()->setAPIKey( "JRIHWEP6GPOER2QQ6" ); - m_audioEngine = QWeakPointer( new AudioEngine ); m_scanManager = QWeakPointer( new ScanManager( this ) ); new Pipeline( this ); @@ -225,7 +223,6 @@ TomahawkApp::init() Echonest::Config::instance()->setAPIKey( "JRIHWEP6GPOER2QQ6" ); Echonest::Config::instance()->setNetworkAccessManager( TomahawkUtils::nam() ); - EchonestGenerator::setupCatalogs(); #ifndef TOMAHAWK_HEADLESS @@ -305,10 +302,11 @@ TomahawkApp::~TomahawkApp() delete SipHandler::instance(); + Pipeline::instance()->stop(); + if ( !m_database.isNull() ) delete m_database.data(); - Pipeline::instance()->stop(); delete Pipeline::instance(); #ifndef TOMAHAWK_HEADLESS From ec8fd589e0dddc61507cbf3db1c9d63cae8cacb3 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 29 Oct 2011 09:21:55 +0200 Subject: [PATCH 047/109] * Removed obsolete png. --- data/images/album-shadow.png | Bin 209071 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 data/images/album-shadow.png diff --git a/data/images/album-shadow.png b/data/images/album-shadow.png deleted file mode 100644 index a053655e215ca04fca4528d8b4ddb0c2d7b5774a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 209071 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 V*tbMg`8*v}NKog%4Q&Qa{6GE-@Ld1^ From 455136a7225ae41e50c70ca1aafc0af84c946b9e Mon Sep 17 00:00:00 2001 From: Jason Herskowitz Date: Sat, 29 Oct 2011 09:39:05 -0400 Subject: [PATCH 048/109] Optizmize icon filesizes --- data/images/album-shadow.png | Bin 209071 -> 7123 bytes data/images/back.png | Bin 266130 -> 3644 bytes data/images/close.png | Bin 1054090 -> 3386 bytes data/images/collapse.png | Bin 1054090 -> 3536 bytes data/images/downloading.png | Bin 266130 -> 3750 bytes data/images/filter.png | Bin 1054090 -> 3847 bytes data/images/forward.png | Bin 266130 -> 3489 bytes data/images/open.png | Bin 1054090 -> 3279 bytes data/images/station.png | Bin 576133 -> 13174 bytes data/images/track-placeholder.png | Bin 1054090 -> 3905 bytes data/images/uploading.png | Bin 266130 -> 3731 bytes 11 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/images/album-shadow.png b/data/images/album-shadow.png index a053655e215ca04fca4528d8b4ddb0c2d7b5774a..aa609a3ba16199c2f90ce65efb1b73eb83f6b67f 100644 GIT binary patch literal 7123 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 JPDHLkV1mL@ox}hD literal 209071 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 V*tbMg`8*v}NKog%4Q&Qa{6GE-@Ld1^ diff --git a/data/images/back.png b/data/images/back.png index 699eed76ca55e962042ca50bda414e21247495fc..0c18ccf38faa1006bc686792ae44eb87d16460bd 100644 GIT binary patch delta 3634 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 Ef`2>s&;S4c literal 266130 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 diff --git a/data/images/close.png b/data/images/close.png index 36da18e7dce6747285c9e1bb3ae1b80c35813766..5e3eaffc27c701d698b77e2cb9ed189c4f939b85 100644 GIT binary patch delta 3374 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 Eg8EH+9smFU literal 1054090 zcmeI*2Yggz+V}A@lSUw+g)SvX@1S%um7djC=>7h@!Q-Te;G#7=$y2)!5L|3sne!U&CZ#WWf*fko_
  • 4D%n($mpGx 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 diff --git a/data/images/downloading.png b/data/images/downloading.png index dd983f655b87b640d54e882db10fd67ccc595ce4..1856dec220b33716dc9768ff1ee468efcf75b764 100644 GIT binary patch delta 3741 zcmV;O4r1|=pAe=Ue~C~|M-2)Z3IG5A4M|8uQUCw|KmY&$KnMl^0063Kaozv`3PWi_ zLr_UWLm+T+Z)Rz1WdHzp+MQE(Sd;e_KHv9c4^~3h@UfR{fdC>StO&>uS)ve<0AYj> z5;Xm069{HJUZAPk55R%$-RIA6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`D zLLvaF4FK%)e@Q+C0NAOM+!p}Yq8zCR0F40vnJ7mj0zkU}U{!%qECRs70HCZuA}$2L zt^t5qwlYTofV~9(c8*w(4?ti5fSE!p%m5%b0suoE6U_r4Oaq`W(!b!TUvP!ENC5!A z%azTSOVTqGxRuZvck=My;vwR~Y_URN7by^C3FIQ2f0vsh6G-O+OwJ4d02=wx!TvZu zkmu&);pS%NZ142NqW){}Zz4V+@!$Tui~3=fuAC~28EsPoqkpK{9 zG%|Vj005J}`Hw&=0RYXHq~ibpyyzHQsFW8>f5r#^07RgHDzHHZ48atvzz&?j9lXF7 z0$~P3Knx_nJP<+#?5=if4T@)34??&!Y3k=s86&ddJ_4>cw#!SkXS~n zChj2~A)X~(Ck_)|lSm{E$&%zw3LzzsGD!SVKGG0roJ=O`kZsA{w~!BzPm=q|!{oOVI>m_MObMbSQlyj;N;PFa(3)vyY4>O^>2$gY-Gd%Qm(Z8eYv>2*=jns= zcMJ`N4THx>VkjAF8G9M07`GWOnM|ey)0dgZR4~^v8<}UA514ONSSt1^d=-((5|uiY zR+WC0=c-gyb5%dpd8!Lkt5pxHe_mF7rbbn>P~)iy)E2ANsU20jsWz_8Qg>31P|s0c zqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uSYnV-9TeA7=Om+qP8+I>y zOjAR1s%ETak!GFdam@h^#)@rS0t$wXHf7*K5zS?5# za_tuF>)P*iaGgM%ES>c_Z94aL3A#4AQM!e?+jY>uuIoY)~6l zn+%&eo6EMSt(&dHcAIVA6yg+*DbgwRQ*PQZ?ELHs?3(Nb?K$>gfA%HzZT8O{%p4LO z);n}Nd~$Sk%yw*Wyz8XlG{dRHsl(}4XB%gsbDi@w7p6;)%MzD%mlsoQr;4X;pL)xc z%+^yMd)ZNTI#eJ*$O)i@o$z8)e??LqN_gLa_%;TM>o2SC_kmoO6 zc3xRt`@J4dvz#WLf7UQ<$lJtQ;JwRxU^+fMW%|zP13tz+0-t)HhrXu1BHul}BYxI? znSKZSp8Grc%l(h|zu|fE7V%C6U;)7a8@mESk|3$_SkmS{wQ>%qC18))9_|f6oZt5Pol_`Aq4|wg`ye zM{J0=A88qx7x{e@DJn9mF6vRVQ*?23_bk?|G6C?@kiR8rC#65}Qa{}jVnlqf_npBo_W3J`gqPZ95>CVfZ zcRX1&S&)1zB2~Schd65~Cxg+yURz%j`tk2nT*)2JgoRplSQVnUAv@6#zwxOiFd;3B_8yA~shQx|tGFoqt`+R{gE3x4zjX+Sb3_cYE^=gB=w+-tUy` zytONMS8KgRef4hA?t5^_C@dOYIJXG+^@gCvI%Wc zHjN%gI&kHifN$EH?V5MBa9S!3!a?Q1C*P)gd*e{(q0YnH!_D8Bf4B7r>qvPk(mKC& ztSzH$pgp0z@92!9ogH2sN4~fJe(y2kV|B+hk5`_cohUu=`Q(C=pRi!(|`JEz}0it_}4C7pLxCS#_SunZYJFvxFx#v_;&W~7k3Ko zOx#_1k9e>AzS{lj2l@}{f3*IwWx#FVfB0a)V9&!@5AO~Mho1hF|I>%z(nrik)gwkD zjgOrl9~%uCz4Bzvli{bbrxVZ0epdf^>vOB;-~HnIOV3#R*zgPai_gEVd8zYq@2jb= zI>#f&AH2?aJ@Kaetl$M000BzNklr9`JQ_b9x( z=eyXx^LlS@&o`fB$(|Ye+xPZ;-+caN0dL;C)oFiy7nGV?I`F;JeAZ8c-}|Zgt(OM2 zSd-RE!fLJFSb+RT&^bZbu-1CcTm|rA)JZn$yrfp0cR%VR=%wZ-N#}qJJpc(J0g@Ya ztAKTfLC+b1&{MuujJ*;Y_vg|nAOru_RN`5Tomt8d$S@8Mu|`WvKoiXq;0f>qcmg~D zo&bMOfG5Bc;0f>qcmg~Do&ZmPC%_X>*7~VAaE*ZND%MolHD)#7vzE6D)<_sZlR)eM zI1l0KRIj7H@J#@JO}aS*RsjAIb&`$TXCnvzfSxmNko;B9SjT^&C(ZD?J2&k&M&s|s z#%(k#2k;$9qy9d#iyeO*j&$yMxBrzNC4qkz8)8%02V=t=TI zc7;dL+ZP2;xE7n_->OcME4y2}iO_rl;KPZ>UkiW-KnQnYiOQy&~x{$@>6455sUPpPPSw zM+ja3^{^uF0zn9`#l}78y8w|%<^rGLdltYa0NMbOFbuyeLcm)4Skl>j-(Bx^ySL{h zVXP)t2lT^&p^)z>*JI;ul$HKqFnEsS-_jmI4gtK>ves?@I6EP6-2yPT0(L_oxdq^7 zf-IF3H3CoYep&Ei(%;O$t4OHiD#?HEIT#Eyp*YDuf6(oAzgsXl4kfLW(trSf&%`Df zmi2F~eOuDKX&75mc7y!$%ZqM}5}qfCITK>O`a*1y;euP<)+~$%)d4$Yy+)=8g~t-E z?RbIX3J|_KOlQB3bB7lUIjF} zR4(T9=6D#L_=X6@!<5R(9*(&d8@GAX(yx_(JRd}zLj-Tu%8BJB9lC+GwzR70R?{vnbf0B@+5zb_&Ds?&IQJ$UbpNUK|oW?6W|G$ZT~F+ z#_dJ6!AsU!NLo3e;CCdQFH3*`v?aBdKm}9#lCwg9OBCw#N$>yybPBO6C*aYN$$g%% z*lLk{pCnF^5=PB-5+vVig<<$3fJ-D#ixNn3Thhfa4Bh{%iMkAs2qq0000000NkvXX Hu0mjfz?dXU literal 266130 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 Apa1{> literal 1054090 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 NjoE$lEh8tL`u`H&RfPZm diff --git a/data/images/forward.png b/data/images/forward.png index 72ad320ef5259b679086eb564fc109f84684a7d2..9b49c6687bc5f3b071c19fbed0e7a4307c128a28 100644 GIT binary patch delta 3478 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 diff --git a/data/images/open.png b/data/images/open.png index 32542f05b4784e7783585bb9ae38f9bcf9d43161..96bd0f98265651746fbfc8aaee8500237555d96b 100644 GIT binary patch delta 3266 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 AhyVZp literal 1054090 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

    xGgEEd3*cVquAYf7 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 LTF#nN#?AYGJ^ZvmobJQL;8r8?0IGHbz<|^2 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 aAP)A_P@iCZl9w%wfP$>*`zmRZ;Qs-HD_9)> literal 576133 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

    OGih&Stdy!c&F=l5OoT^FgFEUnn-!a5sw62 z^tmazj0fz(?~|;A&(mi?{s}>Vp7uKeNDzNlldSx?i`2;OTp+C^thClmB1y8cnrj!> zSL%u@$MpLdDgY?l>%tYH4*dRLsoTQvcULO6>H@P%-4l1UlTx`A1?-K;tWx*Ho-i(z z@3om4TOpv1`sPCbO{MP1Ss)Xin@`(%VccThL=Xbm5YHOs0(Hor>9SNbS218O_nUuP z1l|?!?-Vya2jOzV>$U*?Go@_>z};k2el!lca}xx8o+26TG~IKl1~{`zmy=QXdqa(Z zINO=8G}r__*ts_DX$yRB3nKGu0|5g80|5g80|5g8XPp4&+@Xe}t}Uuf!{VGn?3{zO z7S6dFlHMcv8-QEZ+TC&6qJt0kQUHIS0ayd@t+n=ZYi&IjaL)ZD>HL9budlDKf72!j zK2Ki-@N2_d;I(8_-f5GBjg5^9Bp+)3GDQX8g~&PgJ%IB#yoB>LfQ44Tg!H!qculM; z#3a9pB)wDb(C?cWDlm6rk>wwx|3-*)lNhPmUXP|si^u<8T!I+5I}2k|8hn2|)Bytl z0|5g8tr_Q!@SZ!wKUg0OJ7VL6A_)-BEF}S9>HKdH+aU!VAVHMildSxu&$EKP5is7$ zlJrWwb;3XbHIbs<2GGiS|6IC2RTw0rorjVxg4hfs5X5~DFD0Y$cHbvO<6b`&PJP8$ zxeegu!)}uHI{K%Q24?|GTHJZ?80vt5fPsKk2zayv82w4Iz%}O_B&{qc_{Wl-e_8?r zU`^865~yJ6sg!8u)Ma^$LlA5rKxYuU#soZAGPxheEp{W4A8_wvo6gF}lLX24BWvvs n0IrcdD@q_qFX=67?RWnH`-rbwEAoby00000NkvXXu0mjfVqz9N literal 266130 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 Date: Sat, 29 Oct 2011 15:43:15 +0200 Subject: [PATCH 049/109] * Fixed setting echonest API key. --- src/tomahawkapp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp index 874d25fb8..dc9dba5da 100644 --- a/src/tomahawkapp.cpp +++ b/src/tomahawkapp.cpp @@ -191,6 +191,8 @@ TomahawkApp::init() tDebug() << "Init Database."; initDatabase(); + Echonest::Config::instance()->setAPIKey( "JRIHWEP6GPOER2QQ6" ); + tDebug() << "Init Echonest Factory."; GeneratorFactory::registerFactory( "echonest", new EchonestFactory ); tDebug() << "Init Database Factory."; @@ -221,7 +223,6 @@ TomahawkApp::init() tDebug() << "Init InfoSystem."; m_infoSystem = QWeakPointer( new Tomahawk::InfoSystem::InfoSystem( this ) ); - Echonest::Config::instance()->setAPIKey( "JRIHWEP6GPOER2QQ6" ); Echonest::Config::instance()->setNetworkAccessManager( TomahawkUtils::nam() ); EchonestGenerator::setupCatalogs(); From 247e8ffbf4189115c91b39dd6499ab9fb233d460 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sat, 29 Oct 2011 14:15:52 -0400 Subject: [PATCH 050/109] Treemodel: don't consider an infosystem request resolved if it has invalid data returned --- src/libtomahawk/playlist/treemodel.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/libtomahawk/playlist/treemodel.cpp b/src/libtomahawk/playlist/treemodel.cpp index 14cc9e06f..956b064f1 100644 --- a/src/libtomahawk/playlist/treemodel.cpp +++ b/src/libtomahawk/playlist/treemodel.cpp @@ -827,7 +827,7 @@ TreeModel::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QV QStringList albums = returnedData[ "albums" ].toStringList(); QList al; - InfoSystem::InfoStringHash inputInfo; + Tomahawk::InfoSystem::InfoStringHash inputInfo; inputInfo = requestData.input.value< InfoSystem::InfoStringHash >(); artist_ptr artist = Artist::get( inputInfo[ "artist" ], false ); @@ -848,14 +848,19 @@ TreeModel::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QV { if ( m_receivedInfoData.contains( requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >() ) ) break; + + QVariantMap returnedData = output.value< QVariantMap >(); + if ( returnedData.isEmpty() ) + break; + m_receivedInfoData.insert( requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >() ); - QVariantMap returnedData = output.value< QVariantMap >(); + QStringList tracks = returnedData[ "tracks" ].toStringList(); QList ql; - InfoSystem::InfoStringHash inputInfo; - inputInfo = requestData.input.value< InfoSystem::InfoStringHash >(); + Tomahawk::InfoSystem::InfoStringHash inputInfo; + inputInfo = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); foreach ( const QString& trackName, tracks ) { From 6c2ed4baed66d63a91713638cd6c524acc82d7c5 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sat, 29 Oct 2011 14:45:09 -0400 Subject: [PATCH 051/109] Move timeout and all sources from getInfo into request data struct. Add finished() signal that gives finished for a particular data type for a caller. --- src/libtomahawk/infosystem/infosystem.cpp | 12 +++++-- src/libtomahawk/infosystem/infosystem.h | 11 ++++-- .../infosystem/infosystemworker.cpp | 34 +++++++++---------- src/libtomahawk/infosystem/infosystemworker.h | 5 +-- src/libtomahawk/playlist/treemodel.cpp | 4 ++- src/libtomahawk/widgets/whatshotwidget.cpp | 10 +++--- 6 files changed, 47 insertions(+), 29 deletions(-) diff --git a/src/libtomahawk/infosystem/infosystem.cpp b/src/libtomahawk/infosystem/infosystem.cpp index c9b814968..505aa2119 100644 --- a/src/libtomahawk/infosystem/infosystem.cpp +++ b/src/libtomahawk/infosystem/infosystem.cpp @@ -79,7 +79,11 @@ InfoSystem::InfoSystem( QObject *parent ) connect( m_worker.data(), SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), this, SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), Qt::UniqueConnection ); + connect( m_worker.data(), SIGNAL( finished( QString ) ), this, SIGNAL( finished( QString ) ), Qt::UniqueConnection ); + + connect( m_worker.data(), SIGNAL( finished( QString, Tomahawk::InfoSystem::InfoType ) ), + this, SIGNAL( finished( QString, Tomahawk::InfoSystem::InfoType ) ), Qt::UniqueConnection ); } InfoSystem::~InfoSystem() @@ -120,10 +124,10 @@ InfoSystem::newNam() const void -InfoSystem::getInfo( const InfoRequestData &requestData, uint timeoutMillis, bool allSources ) +InfoSystem::getInfo( const InfoRequestData &requestData ) { qDebug() << Q_FUNC_INFO; - QMetaObject::invokeMethod( m_worker.data(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ), Q_ARG( uint, timeoutMillis ), Q_ARG( bool, allSources ) ); + QMetaObject::invokeMethod( m_worker.data(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) ); } @@ -133,11 +137,13 @@ InfoSystem::getInfo( const QString &caller, const QVariantMap &customData, const InfoRequestData requestData; requestData.caller = caller; requestData.customData = customData; + requestData.allSources = allSources; Q_FOREACH( InfoType type, inputMap.keys() ) { requestData.type = type; requestData.input = inputMap[ type ]; - QMetaObject::invokeMethod( m_worker.data(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ), Q_ARG( uint, ( timeoutMap.contains( type ) ? timeoutMap[ type ] : 0 ) ), Q_ARG( bool, allSources ) ); + requestData.timeoutMillis = timeoutMap.contains( type ) ? timeoutMap[ type ] : 10000; + QMetaObject::invokeMethod( m_worker.data(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) ); } } diff --git a/src/libtomahawk/infosystem/infosystem.h b/src/libtomahawk/infosystem/infosystem.h index 2e95aff1f..01be362e5 100644 --- a/src/libtomahawk/infosystem/infosystem.h +++ b/src/libtomahawk/infosystem/infosystem.h @@ -132,7 +132,9 @@ struct InfoRequestData { Tomahawk::InfoSystem::InfoType type; QVariant input; QVariantMap customData; - + uint timeoutMillis; + bool allSources; + InfoRequestData() : requestId( TomahawkUtils::infosystemRequestId() ) , internalId( TomahawkUtils::infosystemRequestId() ) @@ -140,6 +142,8 @@ struct InfoRequestData { , type( Tomahawk::InfoSystem::InfoNoInfo ) , input( QVariant() ) , customData( QVariantMap() ) + , timeoutMillis( 10000 ) + , allSources( false ) {} InfoRequestData( const quint64 rId, const QString &callr, const Tomahawk::InfoSystem::InfoType typ, const QVariant &inputvar, const QVariantMap &custom ) @@ -149,6 +153,8 @@ struct InfoRequestData { , type( typ ) , input( inputvar ) , customData( custom ) + , timeoutMillis( 10000 ) + , allSources( false ) {} }; @@ -234,7 +240,7 @@ public: InfoSystem( QObject *parent ); ~InfoSystem(); - void getInfo( const InfoRequestData &requestData, uint timeoutMillis = 0, bool allSources = false ); + void getInfo( const InfoRequestData &requestData ); //WARNING: if changing timeoutMillis above, also change in below function in .cpp file void getInfo( const QString &caller, const QVariantMap &customData, const InfoTypeMap &inputMap, const InfoTimeoutMap &timeoutMap = InfoTimeoutMap(), bool allSources = false ); void pushInfo( const QString &caller, const InfoType type, const QVariant &input ); @@ -243,6 +249,7 @@ public: signals: void info( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); void finished( QString target ); + void finished( QString target, Tomahawk::InfoSystem::InfoType type ); public slots: void newNam() const; diff --git a/src/libtomahawk/infosystem/infosystemworker.cpp b/src/libtomahawk/infosystem/infosystemworker.cpp index a744f76f3..324a9bdb5 100644 --- a/src/libtomahawk/infosystem/infosystemworker.cpp +++ b/src/libtomahawk/infosystem/infosystemworker.cpp @@ -176,7 +176,7 @@ InfoSystemWorker::determineOrderedMatches( const InfoType type ) const void -InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, uint timeoutMillis, bool allSources ) +InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { //qDebug() << Q_FUNC_INFO << "type is " << requestData.type << " and allSources = " << (allSources ? "true" : "false" ); @@ -184,11 +184,11 @@ InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, ui if ( providers.isEmpty() ) { emit info( requestData, QVariant() ); - checkFinished( requestData.caller ); + checkFinished( requestData ); return; } - if ( !allSources ) + if ( !requestData.allSources ) providers = QList< InfoPluginPtr >( providers.mid( 0, 1 ) ); bool foundOne = false; @@ -199,7 +199,7 @@ InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, ui foundOne = true; - if ( allSources || m_savedRequestMap.contains( requestData.requestId ) ) + if ( requestData.allSources || m_savedRequestMap.contains( requestData.requestId ) ) { if ( m_savedRequestMap.contains( requestData.requestId ) ) tDebug() << Q_FUNC_INFO << "Warning: reassigning requestId because it already exists"; @@ -210,10 +210,10 @@ InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, ui quint64 requestId = requestData.internalId; m_requestSatisfiedMap[ requestId ] = false; - if ( timeoutMillis != 0 ) + if ( requestData.timeoutMillis != 0 ) { qint64 currMs = QDateTime::currentMSecsSinceEpoch(); - m_timeRequestMapper.insert( currMs + timeoutMillis, requestId ); + m_timeRequestMapper.insert( currMs + requestData.timeoutMillis, requestId ); } // qDebug() << "Assigning request with requestId" << requestId << "and type" << requestData.type; m_dataTracker[ requestData.caller ][ requestData.type ] = m_dataTracker[ requestData.caller ][ requestData.type ] + 1; @@ -232,13 +232,13 @@ InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, ui if ( !foundOne ) { emit info( requestData, QVariant() ); - checkFinished( requestData.caller ); + checkFinished( requestData ); } } void -InfoSystemWorker::pushInfo( QString caller, InfoType type, QVariant input ) +InfoSystemWorker::pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input ) { // qDebug() << Q_FUNC_INFO; @@ -275,23 +275,23 @@ InfoSystemWorker::infoSlot( Tomahawk::InfoSystem::InfoRequestData requestData, Q // qDebug() << "Current count in dataTracker for target" << requestData.caller << "and type" << requestData.type << "is" << m_dataTracker[ requestData.caller ][ requestData.type ]; delete m_savedRequestMap[ requestId ]; m_savedRequestMap.remove( requestId ); - checkFinished( requestData.caller ); + checkFinished( requestData ); } void -InfoSystemWorker::checkFinished( const QString &target ) +InfoSystemWorker::checkFinished( const Tomahawk::InfoSystem::InfoRequestData &requestData ) { - Q_FOREACH( InfoType testtype, m_dataTracker[ target ].keys() ) + if ( m_dataTracker[ requestData.caller ][ requestData.type ] == 0 ) + emit finished( requestData.caller, requestData.type ); + + Q_FOREACH( InfoType testtype, m_dataTracker[ requestData.caller ].keys() ) { - if ( m_dataTracker[ target ][ testtype ] != 0) - { -// qDebug() << "Found outstanding request of type" << testtype; + if ( m_dataTracker[ requestData.caller ][ testtype ] != 0 ) return; - } } // qDebug() << "Emitting finished with target" << target; - emit finished( target ); + emit finished( requestData.caller ); } @@ -336,7 +336,7 @@ InfoSystemWorker::checkTimeoutsTimerFired() if ( !m_timeRequestMapper.count( time ) ) m_timeRequestMapper.remove( time ); - checkFinished( returnData.caller ); + checkFinished( returnData ); } else { diff --git a/src/libtomahawk/infosystem/infosystemworker.h b/src/libtomahawk/infosystem/infosystemworker.h index a190dd962..3bcdd1b62 100644 --- a/src/libtomahawk/infosystem/infosystemworker.h +++ b/src/libtomahawk/infosystem/infosystemworker.h @@ -52,12 +52,13 @@ public: signals: void info( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); void finished( QString target ); + void finished( QString target, Tomahawk::InfoSystem::InfoType type ); void namChanged( QNetworkAccessManager* ); public slots: void init( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache > cache ); - void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, uint timeoutMillis, bool allSources ); + void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input ); void infoSlot( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); @@ -69,7 +70,7 @@ private slots: private: - void checkFinished( const QString &target ); + void checkFinished( const Tomahawk::InfoSystem::InfoRequestData &target ); QList< InfoPluginPtr > determineOrderedMatches( const InfoType type ) const; QHash< QString, QHash< InfoType, int > > m_dataTracker; diff --git a/src/libtomahawk/playlist/treemodel.cpp b/src/libtomahawk/playlist/treemodel.cpp index 956b064f1..154548d7f 100644 --- a/src/libtomahawk/playlist/treemodel.cpp +++ b/src/libtomahawk/playlist/treemodel.cpp @@ -635,7 +635,9 @@ TreeModel::addTracks( const album_ptr& album, const QModelIndex& parent ) requestData.customData["rows"] = QVariant( rows ); requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo ); requestData.type = Tomahawk::InfoSystem::InfoAlbumSongs; - Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData, 0, true ); + requestData.timeoutMillis = 0; + requestData.allSources = true; + Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); } else Q_ASSERT( false ); diff --git a/src/libtomahawk/widgets/whatshotwidget.cpp b/src/libtomahawk/widgets/whatshotwidget.cpp index dfbce3310..f96ca18ba 100644 --- a/src/libtomahawk/widgets/whatshotwidget.cpp +++ b/src/libtomahawk/widgets/whatshotwidget.cpp @@ -136,9 +136,10 @@ WhatsHotWidget::fetchData() requestData.caller = s_whatsHotIdentifier; requestData.customData = QVariantMap(); requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo ); - requestData.type = Tomahawk::InfoSystem::InfoChartCapabilities; - Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData, 20000, true ); + requestData.timeoutMillis = 20000; + requestData.allSources = true; + Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); tDebug( LOGVERBOSE ) << "WhatsHot: requested InfoChartCapabilities"; } @@ -358,11 +359,12 @@ WhatsHotWidget::leftCrumbIndexChanged( QModelIndex index ) requestData.caller = s_whatsHotIdentifier; requestData.customData = customData; requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( criteria ); - requestData.type = Tomahawk::InfoSystem::InfoChart; + requestData.timeoutMillis = 20000; + requestData.allSources = true; qDebug() << "Making infosystem request for chart of type:" <getInfo( requestData, 20000, true ); + Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); m_queuedFetches.insert( chartId ); m_queueItemToShow = chartId; From b46af3405e37e0eff5065348efb83f163a3411a0 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 30 Oct 2011 06:41:25 +0100 Subject: [PATCH 052/109] * Fixed TWK-537: Adding social actions for remote sources was broken. --- .../databasecommand_loadsocialactions.cpp | 4 +-- .../database/databasecommand_socialaction.cpp | 28 ++++++++----------- .../playlist/customplaylistview.cpp | 15 ++++++++-- src/sourcetree/sourcetreeview.cpp | 10 +++++-- 4 files changed, 34 insertions(+), 23 deletions(-) diff --git a/src/libtomahawk/database/databasecommand_loadsocialactions.cpp b/src/libtomahawk/database/databasecommand_loadsocialactions.cpp index cf0dfa7e7..8f2b51e2f 100644 --- a/src/libtomahawk/database/databasecommand_loadsocialactions.cpp +++ b/src/libtomahawk/database/databasecommand_loadsocialactions.cpp @@ -50,7 +50,7 @@ DatabaseCommand_LoadSocialActions::exec( DatabaseImpl* dbi ) return; QString whereToken; - whereToken = QString( "WHERE id IS %1" ).arg( trkid ); + whereToken = QString( "WHERE id IS %1" ).arg( trkid ); QString sql = QString( "SELECT k, v, timestamp, source " @@ -66,7 +66,7 @@ DatabaseCommand_LoadSocialActions::exec( DatabaseImpl* dbi ) Tomahawk::SocialAction action; action.action = query.value( 0 ); // action action.value = query.value( 1 ); // comment - action.timestamp = query.value( 2 ); // timestamp + action.timestamp = query.value( 2 ); // timestamp action.source = query.value( 3 ); // source allSocialActions.append( action ); diff --git a/src/libtomahawk/database/databasecommand_socialaction.cpp b/src/libtomahawk/database/databasecommand_socialaction.cpp index 0f30c3ca4..2b137b9da 100644 --- a/src/libtomahawk/database/databasecommand_socialaction.cpp +++ b/src/libtomahawk/database/databasecommand_socialaction.cpp @@ -49,22 +49,17 @@ DatabaseCommand_SocialAction::exec( DatabaseImpl* dbi ) TomahawkSqlQuery query = dbi->newquery(); - QVariant srcid = source()->isLocal() ? QVariant( QVariant::Int ) : source()->id(); - int trkid = -2; - if ( !m_result.isNull() && !m_artist.isNull() && !m_track.isEmpty() ) - { - bool autoCreate = true; - int artid = dbi->artistId( m_artist, autoCreate ); - if ( artid < 1 ) - return; + if ( m_artist.isNull() || m_track.isEmpty() ) + return; - autoCreate = true; // artistId overwrites autoCreate (reference) - trkid = dbi->trackId( artid, m_track, autoCreate ); - if ( trkid < 1 ) - return; - } + int artid = dbi->artistId( m_artist, true ); + if ( artid < 1 ) + return; + int trkid = dbi->trackId( artid, m_track, true ); + if ( trkid < 1 ) + return; // update if it already exists TomahawkSqlQuery find = dbi->newquery(); @@ -80,12 +75,13 @@ DatabaseCommand_SocialAction::exec( DatabaseImpl* dbi ) .arg( trkid ) .arg( source()->isLocal() ? "IS NULL" : QString( "=%1" ).arg( source()->id() ) ) .arg( m_action ) ); - } else + } + else { - query.prepare( "INSERT INTO social_attributes(id, source, k, v, timestamp) " + query.prepare( "INSERT INTO social_attributes(id, source, k, v, timestamp) " "VALUES (?, ?, ?, ?, ?)" ); - query.bindValue( 0, trkid >= -1 ? trkid : QVariant() ); + query.bindValue( 0, trkid ); query.bindValue( 1, srcid ); query.bindValue( 2, m_action ); query.bindValue( 3, m_comment ); diff --git a/src/libtomahawk/playlist/customplaylistview.cpp b/src/libtomahawk/playlist/customplaylistview.cpp index 218b6ffb4..7273dcee3 100644 --- a/src/libtomahawk/playlist/customplaylistview.cpp +++ b/src/libtomahawk/playlist/customplaylistview.cpp @@ -52,8 +52,11 @@ CustomPlaylistView::CustomPlaylistView( CustomPlaylistView::PlaylistType type, c } } + CustomPlaylistView::~CustomPlaylistView() -{} +{ +} + bool CustomPlaylistView::isBeingPlayed() const @@ -61,6 +64,7 @@ CustomPlaylistView::isBeingPlayed() const return AudioEngine::instance()->currentTrackPlaylist() == playlistInterface(); } + bool CustomPlaylistView::jumpToCurrentTrack() { @@ -80,7 +84,7 @@ CustomPlaylistView::generateTracks() "FROM social_attributes, track, artist " "WHERE social_attributes.id = track.id AND artist.id = track.artist AND social_attributes.k = 'Love' AND social_attributes.v = 'true' AND social_attributes.source %1 " "GROUP BY track.id " - "ORDER BY counter DESC, social_attributes.timestamp DESC " ).arg( m_source->isLocal() ? "IS NULL" : QString( "=%1" ).arg( m_source->id() ) ); + "ORDER BY counter DESC, social_attributes.timestamp DESC " ).arg( m_source->isLocal() ? "IS NULL" : QString( "= %1" ).arg( m_source->id() ) ); break; case AllLovedTracks: sql = QString( "SELECT track.name, artist.name, source, COUNT(*) as counter " @@ -96,6 +100,7 @@ CustomPlaylistView::generateTracks() Database::instance()->enqueue( QSharedPointer( cmd ) ); } + void CustomPlaylistView::tracksGenerated( QList< query_ptr > tracks ) { @@ -103,6 +108,7 @@ CustomPlaylistView::tracksGenerated( QList< query_ptr > tracks ) m_model->append( q ); } + QString CustomPlaylistView::title() const { @@ -132,18 +138,21 @@ CustomPlaylistView::description() const } } + QString CustomPlaylistView::longDescription() const { return QString(); } + QPixmap CustomPlaylistView::pixmap() const { return QPixmap( RESPATH "images/loved_playlist.png" ); } + void CustomPlaylistView::reload() { @@ -153,7 +162,7 @@ CustomPlaylistView::reload() void -CustomPlaylistView::sourceAdded( const source_ptr& s) +CustomPlaylistView::sourceAdded( const source_ptr& s ) { connect( s.data(), SIGNAL( socialAttributesChanged() ), this, SLOT( reload() ) ); } diff --git a/src/sourcetree/sourcetreeview.cpp b/src/sourcetree/sourcetreeview.cpp index 59e9a4d01..8e0cbec8d 100644 --- a/src/sourcetree/sourcetreeview.cpp +++ b/src/sourcetree/sourcetreeview.cpp @@ -115,8 +115,11 @@ SourceTreeView::SourceTreeView( QWidget* parent ) connect( this, SIGNAL( catchUpRequest() ), m_latchManager, SLOT( catchUpRequest() ) ); } + SourceTreeView::~SourceTreeView() -{} +{ +} + void SourceTreeView::setupMenus() @@ -151,7 +154,7 @@ SourceTreeView::setupMenus() m_latchOnAction->setText( tr( "&Catch Up" ) ); m_latchMenu.addSeparator(); m_latchOffAction = m_latchMenu.addAction( tr( "&Stop Listening Along" ) ); - connect( m_latchOffAction, SIGNAL( triggered() ), SLOT( latchOff() ) ); + connect( m_latchOffAction, SIGNAL( triggered() ), SLOT( latchOff() ) ); } } } @@ -231,6 +234,7 @@ SourceTreeView::selectRequest( const QPersistentModelIndex& idx ) } } + void SourceTreeView::expandRequest( const QPersistentModelIndex &idx ) { @@ -245,6 +249,7 @@ SourceTreeView::loadPlaylist() onItemActivated( m_contextMenuIndex ); } + void SourceTreeView::deletePlaylist( const QModelIndex& idxIn ) { @@ -353,6 +358,7 @@ SourceTreeView::latchOnOrCatchUp() emit latchRequest( source ); } + void SourceTreeView::latchOff() { From be1827e55499e1bd13861d18807e977b0c51a68f Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Sun, 30 Oct 2011 14:58:18 +0100 Subject: [PATCH 053/109] Install tomahawk.svg to correct destination --- src/CMakeLists.linux.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.linux.txt b/src/CMakeLists.linux.txt index 355144407..ae108e4d5 100644 --- a/src/CMakeLists.linux.txt +++ b/src/CMakeLists.linux.txt @@ -5,6 +5,6 @@ FOREACH( _file ${_icons} ) INSTALL( FILES ${_file} RENAME tomahawk.png DESTINATION share/icons/hicolor/${_res}/apps ) ENDFOREACH( _file ) -INSTALL( FILES ${CMAKE_SOURCE_DIR}/data/icons/tomahawk-icon.svg RENAME tomahawk.svg DESTINATION share/icons/hicolor/scalable ) +INSTALL( FILES ${CMAKE_SOURCE_DIR}/data/icons/tomahawk-icon.svg RENAME tomahawk.svg DESTINATION share/icons/hicolor/scalable/apps ) INSTALL( FILES ${CMAKE_SOURCE_DIR}/admin/unix/tomahawk.desktop DESTINATION share/applications ) From f1200ac74d6cbd6d19fe76fa6cfadaa5246fdf47 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Sun, 30 Oct 2011 16:56:10 +0100 Subject: [PATCH 054/109] Find patched CLucene from Packman --- CMakeModules/FindCLucene.cmake | 9 +++++++ CMakeModules/FindCLuceneUnstable.cmake | 37 ++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 CMakeModules/FindCLuceneUnstable.cmake diff --git a/CMakeModules/FindCLucene.cmake b/CMakeModules/FindCLucene.cmake index eb0eafc3e..83c569a38 100644 --- a/CMakeModules/FindCLucene.cmake +++ b/CMakeModules/FindCLucene.cmake @@ -12,7 +12,15 @@ INCLUDE(CheckSymbolExists) INCLUDE(FindLibraryWithDebug) +# try to locate a patched unstable version (for comp's sake *sigh*) first +FIND_PACKAGE(CLuceneUnstable) +IF(CLUCENEUNSTABLE_FOUND) + SET(CLucene_FOUND TRUE) + SET(CLUCENE_INCLUDE_DIR ${CLUCENE_UNSTABLE_INCLUDE_DIRS}) + SET(CLUCENE_LIBRARIES ${CLUCENE_UNSTABLE_LIBS}) + #MESSAGE(FATAL_ERROR NARF) +ELSE(CLUCENEUNSTABLE_FOUND) IF(CLucene_FIND_VERSION) SET(CLUCENE_MIN_VERSION ${CLucene_FIND_VERSION}) ELSEIF() @@ -99,6 +107,7 @@ ENDIF (CLUCENE_LIBRARY_DIR) IF(CLUCENE_INCLUDE_DIR AND CLUCENE_LIBRARIES AND CLUCENE_LIBRARY_DIR AND CLUCENE_GOOD_VERSION) SET(CLucene_FOUND TRUE) ENDIF(CLUCENE_INCLUDE_DIR AND CLUCENE_LIBRARIES AND CLUCENE_LIBRARY_DIR AND CLUCENE_GOOD_VERSION) +ENDIF(CLUCENEUNSTABLE_FOUND) IF(CLucene_FOUND) IF(NOT CLucene_FIND_QUIETLY) diff --git a/CMakeModules/FindCLuceneUnstable.cmake b/CMakeModules/FindCLuceneUnstable.cmake new file mode 100644 index 000000000..62643aa86 --- /dev/null +++ b/CMakeModules/FindCLuceneUnstable.cmake @@ -0,0 +1,37 @@ +# - Try to find clucene-unstable +# This is a workaround for distros, that want to ship a recent enough clucene but don't want to replace the old version +# +# CLUCENEUNSTABLE_FOUND - system has clucene-unstable +# CLUCENE_UNSTABLE_INCLUDE_DIR - the clucene-unstable include directories +# CLUCENE_UNSTABLE_LIBS - link these to use clucene-unstable +# +# (c) Dominik Schmidt +# + +# Include dir +find_path(CLUCENE_UNSTABLE_INCLUDE_DIR + NAMES CLucene.h + PATH_SUFFIXES clucene-unstable + PATHS ${KDE4_INCLUDE_DIR} +) + +# Finally the library itself +find_library(CLUCENE_UNSTABLE_SHARED_LIB + NAMES clucene-unstable-shared + PATHS ${KDE4_LIB_DIR} +) + +find_library(CLUCENE_UNSTABLE_CORE_LIB + NAMES clucene-unstable-core + PATHS ${KDE4_LIB_DIR} +) + + +SET( CLUCENE_UNSTABLE_LIBS ${CLUCENE_UNSTABLE_SHARED_LIB} ${CLUCENE_UNSTABLE_CORE_LIB} ) +SET( CLUCENE_UNSTABLE_INCLUDE_DIRS ${CLUCENE_UNSTABLE_INCLUDE_DIR}) +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(CLuceneUnstable DEFAULT_MSG CLUCENE_UNSTABLE_LIBS CLUCENE_UNSTABLE_INCLUDE_DIRS) + + +MARK_AS_ADVANCED(CLUCENE_UNSTABLE_LIBS CLUCENE_UNSTABLE_INCLUDE_DIRS) + From ec66cf29f30251a43d904d02023fd78d96ef09ab Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Fri, 28 Oct 2011 17:22:00 -0400 Subject: [PATCH 055/109] Remove the iTunes Store: Top Songs in part of the itunes charts --- .../infosystem/infoplugins/generic/chartsplugin.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp index 9185a1321..235e26740 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp @@ -286,7 +286,7 @@ ChartsPlugin::chartTypes() const QVariantMap chart = chartObj.toMap(); const QString id = chart.value( "id" ).toString(); const QString geo = chart.value( "geo" ).toString(); - QString name = chart.value( "name" ).toString(); + QString name = chart.value( "genre" ).toString(); const QString type = chart.value( "type" ).toString(); const bool isDefault = ( chart.contains( "default" ) && chart[ "default" ].toInt() == 1 ); @@ -311,8 +311,8 @@ ChartsPlugin::chartTypes() country = m_cachedCountries[ geo ]; } - if ( name.startsWith( "iTunes Store:" ) ) // truncate - name = name.mid( 13 ); + if ( name.isEmpty() ) // not a specific chart, an all chart + name = tr( "Top Overall" ); InfoStringHash c; c[ "id" ] = id; From c1ee97fc40503ed95a0556ceb51e72ffd370a348 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sat, 29 Oct 2011 18:27:08 -0400 Subject: [PATCH 056/109] Show a querylabel in the infobar if there's artist we can link it to --- src/libtomahawk/infobar/infobar.cpp | 49 ++++++++++ src/libtomahawk/infobar/infobar.h | 9 ++ src/libtomahawk/infobar/infobar.ui | 5 +- src/libtomahawk/viewmanager.cpp | 20 +++- src/libtomahawk/viewpage.h | 15 +++ src/libtomahawk/widgets/Breadcrumb.cpp | 18 +--- src/libtomahawk/widgets/BreadcrumbButton.cpp | 1 + .../widgets/infowidgets/AlbumInfoWidget.cpp | 18 ++++ .../widgets/infowidgets/AlbumInfoWidget.h | 34 ++++--- src/libtomahawk/widgets/querylabel.cpp | 96 ++++++++++++++----- src/libtomahawk/widgets/querylabel.h | 23 ++++- src/tomahawkwindow.cpp | 2 +- 12 files changed, 230 insertions(+), 60 deletions(-) diff --git a/src/libtomahawk/infobar/infobar.cpp b/src/libtomahawk/infobar/infobar.cpp index c441292cf..72da4836b 100644 --- a/src/libtomahawk/infobar/infobar.cpp +++ b/src/libtomahawk/infobar/infobar.cpp @@ -27,6 +27,7 @@ #include "utils/tomahawkutils.h" #include "utils/logger.h" #include +#include #define ANIMATION_TIME 400 #define IMAGE_HEIGHT 64 @@ -37,10 +38,12 @@ using namespace Tomahawk; InfoBar::InfoBar( QWidget* parent ) : QWidget( parent ) , ui( new Ui::InfoBar ) + , m_queryLabel( 0 ) { ui->setupUi( this ); TomahawkUtils::unmarginLayout( layout() ); layout()->setContentsMargins( 8, 4, 8, 4 ); + ui->verticalLayout->setContentsMargins( 0, 0, 0, 15 ); QFont boldFont = ui->captionLabel->font(); boldFont.setPixelSize( 18 ); @@ -71,6 +74,14 @@ InfoBar::InfoBar( QWidget* parent ) ui->longDescriptionLabel->setText( QString() ); ui->imageLabel->setText( QString() ); + m_queryLabel = new QueryLabel( this ); + m_queryLabel->setType( QueryLabel::Artist ); + m_queryLabel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); + m_queryLabel->setTextPen( palette().brightText().color() ); + m_queryLabel->setFont( boldFont ); + m_queryLabel->hide(); + connect( m_queryLabel, SIGNAL( clickedArtist() ), this, SLOT( artistClicked() ) ); + m_autoUpdate = new QCheckBox( this ); m_autoUpdate->setText( tr( "Automatically update" ) ); m_autoUpdate->setLayoutDirection( Qt::RightToLeft ); @@ -116,9 +127,47 @@ InfoBar::setCaption( const QString& s ) void InfoBar::setDescription( const QString& s ) { + if ( m_queryLabel->isVisible() ) + { + ui->verticalLayout->removeWidget( m_queryLabel ); + m_queryLabel->hide(); + + ui->verticalLayout->addWidget( ui->descriptionLabel ); + ui->descriptionLabel->show(); + } ui->descriptionLabel->setText( s ); } +void +InfoBar::setDescription( const artist_ptr& artist ) +{ + m_queryLabel->setQuery( Query::get( artist->name(), QString(), QString() ) ); + m_queryLabel->setExtraContentsMargins( 4, 0, 0, 0 ); + + if ( !m_queryLabel->isVisible() ) + { + ui->verticalLayout->removeWidget( ui->descriptionLabel ); + ui->descriptionLabel->hide(); + + m_queryLabel->show(); + ui->verticalLayout->addWidget( m_queryLabel ); + } + +} + +void +InfoBar::setDescription( const album_ptr& album_ptr ) +{ + // TODO +} + +void +InfoBar::artistClicked() +{ + if ( m_queryLabel && !m_queryLabel->query().isNull() ) + ViewManager::instance()->show( Artist::get( m_queryLabel->artist() ) ); +} + void InfoBar::setLongDescription( const QString& s ) diff --git a/src/libtomahawk/infobar/infobar.h b/src/libtomahawk/infobar/infobar.h index a718a32a3..f16339d52 100644 --- a/src/libtomahawk/infobar/infobar.h +++ b/src/libtomahawk/infobar/infobar.h @@ -22,7 +22,9 @@ #include #include "dllmacro.h" +#include "artist.h" +class QueryLabel; class QCheckBox; class QTimeLine; class QSearchField; @@ -43,7 +45,12 @@ public: public slots: void setCaption( const QString& s ); + void setDescription( const QString& s ); + // If you want a querylabel instead of an ElidedLabel + void setDescription( const Tomahawk::artist_ptr& artist ); + void setDescription( const Tomahawk::album_ptr& album_ptr ); + void setLongDescription( const QString& s ); void setPixmap( const QPixmap& p ); @@ -60,12 +67,14 @@ protected: private slots: void onFilterEdited(); + void artistClicked(); private: Ui::InfoBar* ui; QSearchField* m_searchWidget; QCheckBox* m_autoUpdate; + QueryLabel* m_queryLabel; }; #endif // INFOBAR_H diff --git a/src/libtomahawk/infobar/infobar.ui b/src/libtomahawk/infobar/infobar.ui index 7379ef909..a0f3a9fe9 100644 --- a/src/libtomahawk/infobar/infobar.ui +++ b/src/libtomahawk/infobar/infobar.ui @@ -65,10 +65,13 @@ QLayout::SetDefaultConstraint + + 0 + - + 0 0 diff --git a/src/libtomahawk/viewmanager.cpp b/src/libtomahawk/viewmanager.cpp index 594abc40f..8f022890c 100644 --- a/src/libtomahawk/viewmanager.cpp +++ b/src/libtomahawk/viewmanager.cpp @@ -555,6 +555,12 @@ ViewManager::setPage( ViewPage* page, bool trackHistory ) if( obj->metaObject()->indexOfSignal( "descriptionChanged(QString)" ) > -1 ) connect( obj, SIGNAL( descriptionChanged( QString ) ), m_infobar, SLOT( setDescription( QString ) ), Qt::UniqueConnection ); + if( obj->metaObject()->indexOfSignal( "descriptionChanged(Tomahawk::artist_ptr)" ) > -1 ) + connect( obj, SIGNAL( descriptionChanged( Tomahawk::artist_ptr ) ), m_infobar, SLOT( setDescription( Tomahawk::artist_ptr ) ), Qt::UniqueConnection ); + + if( obj->metaObject()->indexOfSignal( "descriptionChanged(Tomahawk::album_ptr)" ) > -1 ) + connect( obj, SIGNAL( descriptionChanged( Tomahawk::album_ptr ) ), m_infobar, SLOT( setDescription( Tomahawk::album_ptr ) ), Qt::UniqueConnection ); + if( obj->metaObject()->indexOfSignal( "longDescriptionChanged(QString)" ) > -1 ) connect( obj, SIGNAL( longDescriptionChanged( QString ) ), m_infobar, SLOT( setLongDescription( QString ) ), Qt::UniqueConnection ); @@ -672,7 +678,19 @@ ViewManager::updateView() m_infobar->setVisible( currentPage()->showInfoBar() ); m_infobar->setCaption( currentPage()->title() ); - m_infobar->setDescription( currentPage()->description() ); + switch( currentPage()->descriptionType() ) + { + case ViewPage::TextType: + m_infobar->setDescription( currentPage()->description() ); + break; + case ViewPage::ArtistType: + m_infobar->setDescription( currentPage()->descriptionArtist() ); + break; + case ViewPage::AlbumType: + m_infobar->setDescription( currentPage()->descriptionAlbum() ); + break; + + } m_infobar->setLongDescription( currentPage()->longDescription() ); m_infobar->setPixmap( currentPage()->pixmap() ); diff --git a/src/libtomahawk/viewpage.h b/src/libtomahawk/viewpage.h index 6e32bcef9..2810830d1 100644 --- a/src/libtomahawk/viewpage.h +++ b/src/libtomahawk/viewpage.h @@ -23,6 +23,8 @@ #include "typedefs.h" #include "playlistinterface.h" +#include "artist.h" +#include "album.h" #include "utils/tomahawkutils.h" #include "dllmacro.h" @@ -33,6 +35,12 @@ namespace Tomahawk class DLLEXPORT ViewPage { public: + enum DescriptionType { + TextType = 0, + ArtistType = 1, + AlbumType = 2 + }; + ViewPage() {} virtual ~ViewPage() {} @@ -40,7 +48,12 @@ public: virtual Tomahawk::PlaylistInterface* playlistInterface() const = 0; virtual QString title() const = 0; + + virtual DescriptionType descriptionType() { return TextType; } virtual QString description() const = 0; + virtual Tomahawk::artist_ptr descriptionArtist() const { return Tomahawk::artist_ptr(); } + virtual Tomahawk::album_ptr descriptionAlbum() const { return Tomahawk::album_ptr(); } + virtual QString longDescription() const { return QString(); } virtual QPixmap pixmap() const { return QPixmap( RESPATH "icons/tomahawk-icon-128x128.png" ); } @@ -62,6 +75,8 @@ public: /** subclasses implementing ViewPage can emit the following signals: * nameChanged( const QString& ) * descriptionChanged( const QString& ) + * descriptionChanged( const Tomahawk::artist_ptr& artist ) + * descriptionChanged( const Tomahawk::album_ptr& album ) * longDescriptionChanged( const QString& ) * pixmapChanged( const QPixmap& ) * destroyed( QWidget* widget ); diff --git a/src/libtomahawk/widgets/Breadcrumb.cpp b/src/libtomahawk/widgets/Breadcrumb.cpp index c61dc6ca3..e93d3b7ab 100644 --- a/src/libtomahawk/widgets/Breadcrumb.cpp +++ b/src/libtomahawk/widgets/Breadcrumb.cpp @@ -121,10 +121,10 @@ Breadcrumb::updateButtons( const QModelIndex& updateFrom ) // Animate all buttons except the first if ( m_buttons.count() > 0 ) { - QPropertyAnimation* animation = new QPropertyAnimation( btn, "pos" ); + QPropertyAnimation* animation = new QPropertyAnimation( btn, "x" ); animation->setDuration( 300 ); - animation->setStartValue( m_buttons.last()->pos() ); - animation->setEndValue( btn->pos() ); + animation->setStartValue( m_buttons.last()->pos().x() ); + animation->setEndValue( btn->pos().x() ); animation->start( QAbstractAnimation::DeleteWhenStopped ); } @@ -150,19 +150,7 @@ Breadcrumb::updateButtons( const QModelIndex& updateFrom ) while ( m_buttons.size() > cur ) { BreadcrumbButton* b = m_buttons.takeLast(); - m_buttonlayout->removeWidget( b ); - b->show(); - - if ( m_buttons.size() ) - { - QPropertyAnimation* animation = new QPropertyAnimation( b, "pos" ); - animation->setDuration( 300 ); - animation->setStartValue( b->pos() ); - animation->setEndValue( m_buttons.last()->pos() ); - animation->start( QAbstractAnimation::DeleteWhenStopped ); - } - b->deleteLater(); } diff --git a/src/libtomahawk/widgets/BreadcrumbButton.cpp b/src/libtomahawk/widgets/BreadcrumbButton.cpp index 4a94b469a..1e7f5f4ca 100644 --- a/src/libtomahawk/widgets/BreadcrumbButton.cpp +++ b/src/libtomahawk/widgets/BreadcrumbButton.cpp @@ -59,6 +59,7 @@ BreadcrumbButton::paintEvent( QPaintEvent* ) StyleHelper::horizontalHeader( &p, r ); // draw the background + qDebug() << "BREADCRUMBBUTTON PAINTING IN:" << r << mapToParent( r.topLeft() ) << m_curIndex.data(); if( !hasChildren() ) return; diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp index 0105e6b92..3b5877a96 100644 --- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp @@ -136,6 +136,23 @@ AlbumInfoWidget::isBeingPlayed() const return false; } +artist_ptr AlbumInfoWidget::descriptionArtist() const +{ + if ( !m_album.isNull() && !m_album->artist().isNull() ) + return m_album->artist(); + + return artist_ptr(); +} + +ViewPage::DescriptionType +AlbumInfoWidget::descriptionType() +{ + if ( !m_album.isNull() && !m_album->artist().isNull() ) + return ViewPage::ArtistType; + + return ViewPage::TextType; +} + void AlbumInfoWidget::load( const album_ptr& album ) @@ -143,6 +160,7 @@ AlbumInfoWidget::load( const album_ptr& album ) m_album = album; m_title = album->name(); m_description = album->artist()->name(); + ui->albumsLabel->setText( tr( "Other Albums by %1" ).arg( album->artist()->name() ) ); m_tracksModel->addTracks( album, QModelIndex() ); diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h index 17e47f0d5..8fe7c286c 100644 --- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h +++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h @@ -53,6 +53,24 @@ public: AlbumInfoWidget( const Tomahawk::album_ptr& album, QWidget* parent = 0 ); ~AlbumInfoWidget(); + virtual QWidget* widget() { return this; } + virtual Tomahawk::PlaylistInterface* playlistInterface() const; + + virtual QString title() const { return m_title; } + virtual DescriptionType descriptionType(); + virtual QString description() const { return m_description; } + virtual Tomahawk::artist_ptr descriptionArtist() const; + virtual QString longDescription() const { return m_longDescription; } + virtual QPixmap pixmap() const { if ( m_pixmap.isNull() ) return Tomahawk::ViewPage::pixmap(); else return m_pixmap; } + + virtual bool isTemporaryPage() const { return true; } + virtual bool showStatsBar() const { return false; } + + virtual bool jumpToCurrentTrack() { return false; } + virtual bool isBeingPlayed() const; + +public slots: + /** \brief Loads information for a given album. * \param album The album that you want to load information for. * @@ -63,23 +81,9 @@ public: */ void load( const Tomahawk::album_ptr& album ); - virtual QWidget* widget() { return this; } - virtual Tomahawk::PlaylistInterface* playlistInterface() const; - - virtual QString title() const { return m_title; } - virtual QString description() const { return m_description; } - virtual QString longDescription() const { return m_longDescription; } - virtual QPixmap pixmap() const { if ( m_pixmap.isNull() ) return Tomahawk::ViewPage::pixmap(); else return m_pixmap; } - - virtual bool isTemporaryPage() const { return true; } - virtual bool showStatsBar() const { return false; } - - virtual bool jumpToCurrentTrack() { return false; } - virtual bool isBeingPlayed() const; - signals: void longDescriptionChanged( const QString& description ); - void descriptionChanged( const QString& description ); + void descriptionChanged( const Tomahawk::artist_ptr& artist ); void pixmapChanged( const QPixmap& pixmap ); protected: diff --git a/src/libtomahawk/widgets/querylabel.cpp b/src/libtomahawk/widgets/querylabel.cpp index eee17a469..eed695ce0 100644 --- a/src/libtomahawk/widgets/querylabel.cpp +++ b/src/libtomahawk/widgets/querylabel.cpp @@ -80,8 +80,10 @@ QueryLabel::init() setContentsMargins( 0, 0, 0, 0 ); setMouseTracking( true ); - align = Qt::AlignLeft; - mode = Qt::ElideMiddle; + m_useCustomPen = false; + m_useCustomFont = false; + m_align = Qt::AlignLeft; + m_mode = Qt::ElideMiddle; } @@ -234,38 +236,63 @@ QueryLabel::setQuery( const Tomahawk::query_ptr& query ) Qt::Alignment QueryLabel::alignment() const { - return align; + return m_align; } void QueryLabel::setAlignment( Qt::Alignment alignment ) { - if ( this->align != alignment ) + if ( m_align != alignment ) { - this->align = alignment; + m_align = alignment; update(); // no geometry change, repaint is sufficient } } +void +QueryLabel::setTextPen( const QPen & pen ) +{ + m_useCustomPen = true; + m_textPen = pen; +} + +QPen +QueryLabel::textPen() const +{ + return m_textPen; +} Qt::TextElideMode QueryLabel::elideMode() const { - return mode; + return m_mode; } void QueryLabel::setElideMode( Qt::TextElideMode mode ) { - if ( this->mode != mode ) + if ( m_mode != mode ) { - this->mode = mode; + m_mode = mode; updateLabel(); } } +QFont +QueryLabel::font() const +{ + return m_font; +} + +void +QueryLabel::setFont( const QFont& font ) +{ + m_useCustomFont = true; + m_font = font; +} + void QueryLabel::updateLabel() @@ -277,6 +304,17 @@ QueryLabel::updateLabel() update(); } +void +QueryLabel::setExtraContentsMargins( int left, int top, int right, int bottom ) +{ + QMargins margins = contentsMargins(); + margins.setLeft( margins.left() + left ); + margins.setTop( margins.top() + top ); + margins.setRight( margins.right() + right ); + margins.setBottom( margins.bottom() + bottom ); + setContentsMargins( margins ); +} + QSize QueryLabel::sizeHint() const @@ -290,7 +328,7 @@ QueryLabel::sizeHint() const QSize QueryLabel::minimumSizeHint() const { - switch ( mode ) + switch ( m_mode ) { case Qt::ElideNone: return sizeHint(); @@ -312,11 +350,14 @@ QueryLabel::paintEvent( QPaintEvent* event ) QPainter p( this ); QRect r = contentsRect(); QString s = text(); - const QString elidedText = fontMetrics().elidedText( s, mode, r.width() ); + const QString elidedText = fontMetrics().elidedText( s, m_mode, r.width() ); p.save(); p.setRenderHint( QPainter::Antialiasing ); + if ( m_useCustomFont ) + p.setFont( m_font ); + if ( m_hoverArea.width() ) { if ( elidedText != s ) @@ -343,7 +384,7 @@ QueryLabel::paintEvent( QPaintEvent* event ) p.setBrush( palette().window() ); p.setPen( palette().color( foregroundRole() ) ); } - p.drawText( r, align, elidedText ); + p.drawText( r, m_align, elidedText ); } else { @@ -353,10 +394,14 @@ QueryLabel::paintEvent( QPaintEvent* event ) int albumX = m_type & Album ? fm.width( album() ) : 0; int trackX = m_type & Track ? fm.width( track() ) : 0; + if ( m_useCustomPen ) + p.setPen( m_textPen ); + if ( m_type & Artist ) { p.setBrush( palette().window() ); - p.setPen( palette().color( foregroundRole() ) ); + if ( !m_useCustomPen ) + p.setPen( palette().color( foregroundRole() ) ); if ( m_hoverType == Artist ) { @@ -364,17 +409,18 @@ QueryLabel::paintEvent( QPaintEvent* event ) p.setBrush( palette().highlight() ); } - p.drawText( r, align, artist() ); + p.drawText( r, m_align, artist() ); r.adjust( artistX, 0, 0, 0 ); } if ( m_type & Album ) { p.setBrush( palette().window() ); - p.setPen( palette().color( foregroundRole() ) ); + if ( !m_useCustomPen ) + p.setPen( palette().color( foregroundRole() ) ); if ( m_type & Artist ) { - p.drawText( r, align, DASH ); + p.drawText( r, m_align, DASH ); r.adjust( dashX, 0, 0, 0 ); } if ( m_hoverType == Album ) @@ -383,17 +429,18 @@ QueryLabel::paintEvent( QPaintEvent* event ) p.setBrush( palette().highlight() ); } - p.drawText( r, align, album() ); + p.drawText( r, m_align, album() ); r.adjust( albumX, 0, 0, 0 ); } if ( m_type & Track ) { p.setBrush( palette().window() ); - p.setPen( palette().color( foregroundRole() ) ); + if ( !m_useCustomPen ) + p.setPen( palette().color( foregroundRole() ) ); if ( m_type & Artist || m_type & Album ) { - p.drawText( r, align, DASH ); + p.drawText( r, m_align, DASH ); r.adjust( dashX, 0, 0, 0 ); } if ( m_hoverType == Track ) @@ -402,7 +449,7 @@ QueryLabel::paintEvent( QPaintEvent* event ) p.setBrush( palette().highlight() ); } - p.drawText( r, align, track() ); + p.drawText( r, m_align, track() ); r.adjust( trackX, 0, 0, 0 ); } } @@ -432,7 +479,8 @@ void QueryLabel::mousePressEvent( QMouseEvent* event ) { QFrame::mousePressEvent( event ); - time.start(); + m_time.restart(); + m_dragPos = event->pos(); } @@ -442,7 +490,8 @@ QueryLabel::mouseReleaseEvent( QMouseEvent* event ) QFrame::mouseReleaseEvent( event ); m_dragPos = QPoint(); - if ( time.elapsed() < qApp->doubleClickInterval() ) + qDebug() << "ELAPSED TIME" << m_time.elapsed() << "limit:" << qApp->doubleClickInterval(); + if ( m_time.elapsed() < qApp->doubleClickInterval() ) { switch( m_hoverType ) { @@ -484,7 +533,10 @@ QueryLabel::mouseMoveEvent( QMouseEvent* event ) return; } - const QFontMetrics& fm = fontMetrics(); + QFontMetrics fm = fontMetrics(); + if ( m_useCustomFont ) + fm = QFontMetrics( m_font ); + int dashX = fm.width( DASH ); int artistX = m_type & Artist ? fm.width( artist() ) : 0; int albumX = m_type & Album ? fm.width( album() ) : 0; diff --git a/src/libtomahawk/widgets/querylabel.h b/src/libtomahawk/widgets/querylabel.h index 1fed3d9c2..ef3b758f0 100644 --- a/src/libtomahawk/widgets/querylabel.h +++ b/src/libtomahawk/widgets/querylabel.h @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -21,6 +21,7 @@ #include #include +#include #include "result.h" #include "query.h" @@ -67,6 +68,14 @@ public: Qt::TextElideMode elideMode() const; void setElideMode( Qt::TextElideMode mode ); + void setTextPen( const QPen& ); + QPen textPen() const; + + void setFont( const QFont& ); + QFont font() const; + + void setExtraContentsMargins( int left, int top, int right, int bottom ); + virtual QSize sizeHint() const; virtual QSize minimumSizeHint() const; @@ -98,18 +107,22 @@ protected: virtual void paintEvent( QPaintEvent* event ); virtual void startDrag(); - + private: QString smartAppend( QString& text, const QString& appendage ) const; - QTime time; + QTime m_time; DisplayType m_type; QString m_text; Tomahawk::result_ptr m_result; Tomahawk::query_ptr m_query; - Qt::Alignment align; - Qt::TextElideMode mode; + Qt::Alignment m_align; + Qt::TextElideMode m_mode; + + bool m_useCustomPen, m_useCustomFont; + QPen m_textPen; + QFont m_font; DisplayType m_hoverType; QRect m_hoverArea; diff --git a/src/tomahawkwindow.cpp b/src/tomahawkwindow.cpp index 584204717..9aa03157f 100644 --- a/src/tomahawkwindow.cpp +++ b/src/tomahawkwindow.cpp @@ -710,7 +710,7 @@ TomahawkWindow::showAboutTomahawk() { QMessageBox::about( this, tr( "About Tomahawk" ), tr( "

    Tomahawk %1
    (%2)

    Copyright 2010, 2011
    Christian Muehlhaeuser <muesli@tomahawk-player.org>

    " - "Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Michael Zanetti, Harald Sitter and Steve Robertson" ) + "Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindström, Michael Zanetti, Harald Sitter and Steve Robertson" ) .arg( TomahawkUtils::appFriendlyVersion() ) .arg( qApp->applicationVersion() ) ); } From 0e83e47f1ae6208d6bbca6a437332049490c95b8 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 30 Oct 2011 14:32:46 -0400 Subject: [PATCH 057/109] Don't make infobar bigger than needed --- src/libtomahawk/infobar/infobar.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/infobar/infobar.cpp b/src/libtomahawk/infobar/infobar.cpp index 72da4836b..d4f39d680 100644 --- a/src/libtomahawk/infobar/infobar.cpp +++ b/src/libtomahawk/infobar/infobar.cpp @@ -43,7 +43,6 @@ InfoBar::InfoBar( QWidget* parent ) ui->setupUi( this ); TomahawkUtils::unmarginLayout( layout() ); layout()->setContentsMargins( 8, 4, 8, 4 ); - ui->verticalLayout->setContentsMargins( 0, 0, 0, 15 ); QFont boldFont = ui->captionLabel->font(); boldFont.setPixelSize( 18 ); @@ -133,6 +132,7 @@ InfoBar::setDescription( const QString& s ) m_queryLabel->hide(); ui->verticalLayout->addWidget( ui->descriptionLabel ); + ui->verticalLayout->setContentsMargins( 0, 0, 0, 0 ); ui->descriptionLabel->show(); } ui->descriptionLabel->setText( s ); @@ -151,12 +151,13 @@ InfoBar::setDescription( const artist_ptr& artist ) m_queryLabel->show(); ui->verticalLayout->addWidget( m_queryLabel ); + ui->verticalLayout->setContentsMargins( 0, 0, 0, 15 ); } } void -InfoBar::setDescription( const album_ptr& album_ptr ) +InfoBar::setDescription( const album_ptr& ) { // TODO } From fa577aa018a347832bd73a8356b86b097e69e2d9 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 30 Oct 2011 15:02:48 -0400 Subject: [PATCH 058/109] Don't start playing if clicking a tomahawk link when paused --- src/libtomahawk/globalactionmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/globalactionmanager.cpp b/src/libtomahawk/globalactionmanager.cpp index 1e969d2a1..e14bb3e2a 100644 --- a/src/libtomahawk/globalactionmanager.cpp +++ b/src/libtomahawk/globalactionmanager.cpp @@ -357,7 +357,7 @@ GlobalActionManager::handleOpenTrack ( const query_ptr& q ) ViewManager::instance()->queue()->model()->append( q ); ViewManager::instance()->showQueue(); - if( !AudioEngine::instance()->isPlaying() ) { + if( !AudioEngine::instance()->isPlaying() && !AudioEngine::instance()->isPaused() ) { connect( q.data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( waitingForResolved( bool ) ) ); m_waitingToPlay = q; } From 2dfa25fbee0a67384ad851a41e8c61dc3f5a1a0a Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 30 Oct 2011 15:23:50 -0400 Subject: [PATCH 059/109] Add support for tomahawk://view/album and tomahawk://view/artist urls --- src/libtomahawk/globalactionmanager.cpp | 44 +++++++++++++++++++++++++ src/libtomahawk/globalactionmanager.h | 1 + 2 files changed, 45 insertions(+) diff --git a/src/libtomahawk/globalactionmanager.cpp b/src/libtomahawk/globalactionmanager.cpp index e14bb3e2a..e01bf423e 100644 --- a/src/libtomahawk/globalactionmanager.cpp +++ b/src/libtomahawk/globalactionmanager.cpp @@ -269,6 +269,8 @@ GlobalActionManager::parseTomahawkLink( const QString& urlIn ) return handlePlayCommand( u ); } else if( cmdType == "open" ) { return handleOpenCommand( u ); + } else if( cmdType == "view" ) { + return handleViewCommand( u ); } else { tLog() << "Tomahawk link not supported, command not known!" << cmdType << u.path(); return false; @@ -515,6 +517,48 @@ GlobalActionManager::handleSearchCommand( const QUrl& url ) return true; } +bool +GlobalActionManager::handleViewCommand( const QUrl& url ) +{ + QStringList parts = url.path().split( "/" ).mid( 1 ); // get the rest of the command + if( parts.isEmpty() ) { + tLog() << "No specific view command:" << url.toString(); + return false; + } + + if ( parts[ 0 ] == "artist" ) + { + const QString artist = url.queryItemValue( "name" ); + if ( artist.isEmpty() ) + { + tLog() << "Not artist supplied for view/artist command."; + return false; + } + artist_ptr artistPtr = Artist::get( artist ); + if ( !artistPtr.isNull() ) + ViewManager::instance()->show( artistPtr ); + + return true; + } + else if ( parts[ 0 ] == "album" ) + { + const QString artist = url.queryItemValue( "artist" ); + const QString album = url.queryItemValue( "name" ); + if ( artist.isEmpty() || album.isEmpty() ) + { + tLog() << "Not artist or album supplied for view/artist command:" << url; + return false; + } + album_ptr albumPtr = Album::get( Artist::get( artist, false ), album, false ); + if ( !albumPtr.isNull() ) + ViewManager::instance()->show( albumPtr ); + + return true; + } + + return false; +} + bool GlobalActionManager::handleAutoPlaylistCommand( const QUrl& url ) diff --git a/src/libtomahawk/globalactionmanager.h b/src/libtomahawk/globalactionmanager.h index e3d85eb14..3a0ad3685 100644 --- a/src/libtomahawk/globalactionmanager.h +++ b/src/libtomahawk/globalactionmanager.h @@ -95,6 +95,7 @@ private: bool handlePlayCommand(const QUrl& url ); bool handleBookmarkCommand(const QUrl& url ); bool handleOpenCommand(const QUrl& url ); + bool handleViewCommand(const QUrl& url ); bool doQueueAdd( const QStringList& parts, const QList< QPair< QString, QString > >& queryItems ); bool playSpotify( const QUrl& url ); From 1c50db9ce91fe21d6ba501f669310ccbc0b1dc8c Mon Sep 17 00:00:00 2001 From: Jason Herskowitz Date: Sun, 30 Oct 2011 17:37:22 -0400 Subject: [PATCH 060/109] Re-add CD artwork with no jewel case so we can use if if we need it in certain places --- data/images/no-album-no-case.png | Bin 0 -> 69076 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 data/images/no-album-no-case.png diff --git a/data/images/no-album-no-case.png b/data/images/no-album-no-case.png new file mode 100644 index 0000000000000000000000000000000000000000..1fbbbb8bf251037b1336cd9ea0e3d801a183d147 GIT binary patch literal 69076 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 Dl`-J$ literal 0 HcmV?d00001 From 106740abcad6ec1f8902c7a5f3a1cf1ad1710547 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 30 Oct 2011 16:43:00 -0400 Subject: [PATCH 061/109] Fix bug where all stars could actr as if they were hovered in all the items --- src/GetNewStuffDelegate.cpp | 4 +++- src/GetNewStuffDelegate.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/GetNewStuffDelegate.cpp b/src/GetNewStuffDelegate.cpp index d2d2d98e1..8a5e5d25f 100644 --- a/src/GetNewStuffDelegate.cpp +++ b/src/GetNewStuffDelegate.cpp @@ -192,7 +192,7 @@ GetNewStuffDelegate::paint( QPainter* painter, const QStyleOptionViewItem& optio m_cachedStarRects[ QPair(index.row(), index.column()) ] = r; QPixmap pm; - if ( m_hoveringOver > -1 ) + if ( m_hoveringOver > -1 && ( m_hoveringItem.first == index.row() && m_hoveringItem.second == index.column() ) ) { if ( i <= m_hoveringOver ) // positive star painter->drawPixmap( r, m_onHoverStar ); @@ -303,6 +303,8 @@ GetNewStuffDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, cons { // 0-indexed m_hoveringOver = whichStar; + m_hoveringItem.first = index.row(); + m_hoveringItem.second = index.column(); } return true; diff --git a/src/GetNewStuffDelegate.h b/src/GetNewStuffDelegate.h index e2b71afaa..853e3c74d 100644 --- a/src/GetNewStuffDelegate.h +++ b/src/GetNewStuffDelegate.h @@ -39,6 +39,7 @@ private: int m_widestTextWidth; int m_hoveringOver; + QPair m_hoveringItem; mutable QHash< QPair, QRect > m_cachedButtonRects; mutable QHash< QPair, QRect > m_cachedStarRects; }; From 70608158fe9f49714288cbaed0eb20524ebccee8 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 30 Oct 2011 16:43:16 -0400 Subject: [PATCH 062/109] less verbose debug --- src/libtomahawk/widgets/BreadcrumbButton.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libtomahawk/widgets/BreadcrumbButton.cpp b/src/libtomahawk/widgets/BreadcrumbButton.cpp index 1e7f5f4ca..4a94b469a 100644 --- a/src/libtomahawk/widgets/BreadcrumbButton.cpp +++ b/src/libtomahawk/widgets/BreadcrumbButton.cpp @@ -59,7 +59,6 @@ BreadcrumbButton::paintEvent( QPaintEvent* ) StyleHelper::horizontalHeader( &p, r ); // draw the background - qDebug() << "BREADCRUMBBUTTON PAINTING IN:" << r << mapToParent( r.topLeft() ) << m_curIndex.data(); if( !hasChildren() ) return; From 29fa5c4cd99c231e117bcccd643037b0770b5195 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 30 Oct 2011 17:49:09 -0400 Subject: [PATCH 063/109] Make artist names in album cover view clickable --- .../playlist/albumitemdelegate.cpp | 66 +++++++++++++++++++ src/libtomahawk/playlist/albumitemdelegate.h | 8 +++ src/libtomahawk/playlist/albumview.cpp | 5 +- src/libtomahawk/utils/tomahawkutils.cpp | 8 +++ src/libtomahawk/utils/tomahawkutils.h | 2 + src/libtomahawk/widgets/querylabel.cpp | 4 +- 6 files changed, 89 insertions(+), 4 deletions(-) diff --git a/src/libtomahawk/playlist/albumitemdelegate.cpp b/src/libtomahawk/playlist/albumitemdelegate.cpp index c81722e0e..e1d025c3b 100644 --- a/src/libtomahawk/playlist/albumitemdelegate.cpp +++ b/src/libtomahawk/playlist/albumitemdelegate.cpp @@ -31,6 +31,8 @@ #include "playlist/albumitem.h" #include "playlist/albumproxymodel.h" +#include +#include AlbumItemDelegate::AlbumItemDelegate( QAbstractItemView* parent, AlbumProxyModel* proxy ) @@ -152,11 +154,75 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, text = painter->fontMetrics().elidedText( item->album()->name(), Qt::ElideRight, textRect.width() - 3 ); painter->drawText( textRect, text, to ); + // If the user is hovering over an artist rect, draw a background so she knows it's clickable + QRect r = textRect; + r.setTop( r.bottom() - painter->fontMetrics().height() ); + if ( m_hoveringOver == index ) + TomahawkUtils::drawQueryBackground( painter, opt.palette, r, 1.5 ); + painter->setPen( opt.palette.color( QPalette::Dark ) ); to.setAlignment( Qt::AlignHCenter | Qt::AlignBottom ); text = painter->fontMetrics().elidedText( item->album()->artist()->name(), Qt::ElideRight, textRect.width() - 3 ); painter->drawText( textRect, text, to ); + // Calculate rect of artist on-hover button click area + + m_artistNameRects[ index ] = r; } painter->restore(); } + +bool +AlbumItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ) +{ + Q_UNUSED( option ); + + if ( event->type() != QEvent::MouseButtonRelease && + event->type() != QEvent::MouseMove && + event->type() != QEvent::MouseButtonPress ) + return false; + + if ( m_artistNameRects.contains( index ) ) + { + QMouseEvent* ev = static_cast< QMouseEvent* >( event ); + QRect artistNameRect = m_artistNameRects[ index ]; + if ( artistNameRect.contains( ev->pos() ) ) + { + if ( event->type() == QEvent::MouseMove ) + { + if ( m_hoveringOver != index ) + { + QModelIndex old = m_hoveringOver; + m_hoveringOver = index; + emit updateIndex( old ); + emit updateIndex( index ); + } + + return true; + } + else if ( event->type() == QEvent::MouseButtonRelease ) + { + AlbumItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( index ) ); + if ( !item || item->album().isNull() || item->album()->artist().isNull() ) + return false; + + ViewManager::instance()->show( item->album()->artist() ); + + return true; + } else if ( event->type() == QEvent::MouseButtonPress ) + { + // Stop the whole album from having a down click action as we just want the artist name to be clicked + return true; + } + } + } + + if ( m_hoveringOver.isValid() ) + { + QModelIndex old = m_hoveringOver; + m_hoveringOver = QPersistentModelIndex(); + emit updateIndex( old ); + } + + return false; +} diff --git a/src/libtomahawk/playlist/albumitemdelegate.h b/src/libtomahawk/playlist/albumitemdelegate.h index a5bb3a27a..37a7226da 100644 --- a/src/libtomahawk/playlist/albumitemdelegate.h +++ b/src/libtomahawk/playlist/albumitemdelegate.h @@ -23,6 +23,7 @@ #include "dllmacro.h" +class QEvent; class AlbumProxyModel; class DLLEXPORT AlbumItemDelegate : public QStyledItemDelegate @@ -36,13 +37,20 @@ protected: void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const; + bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ); // QWidget* createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const; +signals: + void updateIndex( const QModelIndex& idx ); + private: QAbstractItemView* m_view; AlbumProxyModel* m_model; mutable QHash< qint64, QPixmap > m_cache; + mutable QHash< QPersistentModelIndex, QRect > m_artistNameRects; + QPersistentModelIndex m_hoveringOver; + QPixmap m_shadowPixmap; QPixmap m_defaultCover; }; diff --git a/src/libtomahawk/playlist/albumview.cpp b/src/libtomahawk/playlist/albumview.cpp index 16c1fc132..a077ffa4a 100644 --- a/src/libtomahawk/playlist/albumview.cpp +++ b/src/libtomahawk/playlist/albumview.cpp @@ -50,6 +50,7 @@ AlbumView::AlbumView( QWidget* parent ) setUniformItemSizes( true ); setSpacing( 16 ); setContentsMargins( 0, 0, 0, 0 ); + setMouseTracking( true ); setResizeMode( Adjust ); setViewMode( IconMode ); @@ -77,7 +78,9 @@ void AlbumView::setProxyModel( AlbumProxyModel* model ) { m_proxyModel = model; - setItemDelegate( new AlbumItemDelegate( this, m_proxyModel ) ); + AlbumItemDelegate* del = new AlbumItemDelegate( this, m_proxyModel ); + connect( del, SIGNAL( updateIndex( QModelIndex ) ), this, SLOT( update( QModelIndex ) ) ); + setItemDelegate( del ); QListView::setModel( m_proxyModel ); } diff --git a/src/libtomahawk/utils/tomahawkutils.cpp b/src/libtomahawk/utils/tomahawkutils.cpp index 31cd625f8..b4e64fedc 100644 --- a/src/libtomahawk/utils/tomahawkutils.cpp +++ b/src/libtomahawk/utils/tomahawkutils.cpp @@ -434,6 +434,14 @@ drawBackgroundAndNumbers( QPainter* painter, const QString& text, const QRect& f painter->drawText( figRect.adjusted( -5, 0, 6, 0 ), text, to ); } +void +drawQueryBackground( QPainter* p, const QPalette& palette, const QRect& r, qreal lightnessFactor ) +{ + p->setPen( palette.mid().color().lighter( lightnessFactor * 100 ) ); + p->setBrush( palette.highlight().color().lighter( lightnessFactor * 100 ) ); + p->drawRoundedRect( r, 4.0, 4.0 ); +} + void unmarginLayout( QLayout* layout ) diff --git a/src/libtomahawk/utils/tomahawkutils.h b/src/libtomahawk/utils/tomahawkutils.h index 543a0907e..e8bcd23ce 100644 --- a/src/libtomahawk/utils/tomahawkutils.h +++ b/src/libtomahawk/utils/tomahawkutils.h @@ -26,6 +26,7 @@ #include #include #include +#include #define RESPATH ":/data/" @@ -85,6 +86,7 @@ namespace TomahawkUtils DLLEXPORT QPixmap createDragPixmap( MediaType type, int itemCount = 1 ); DLLEXPORT void drawBackgroundAndNumbers( QPainter* p, const QString& text, const QRect& rect ); + DLLEXPORT void drawQueryBackground( QPainter* p, const QPalette& palette, const QRect& r, qreal lightnessFactor = 1 ); DLLEXPORT void unmarginLayout( QLayout* layout ); diff --git a/src/libtomahawk/widgets/querylabel.cpp b/src/libtomahawk/widgets/querylabel.cpp index eed695ce0..78a95c3f2 100644 --- a/src/libtomahawk/widgets/querylabel.cpp +++ b/src/libtomahawk/widgets/querylabel.cpp @@ -367,9 +367,7 @@ QueryLabel::paintEvent( QPaintEvent* event ) m_hoverType = Track; } - p.setPen( palette().mid().color() ); - p.setBrush( palette().highlight() ); - p.drawRoundedRect( m_hoverArea, 4.0, 4.0 ); + TomahawkUtils::drawQueryBackground( &p, palette(), m_hoverArea ); } if ( elidedText != s || ( m_result.isNull() && m_query.isNull() ) ) From b98d591077d5d2f859f8b821bf97f3a574593868 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Sun, 30 Oct 2011 23:49:34 +0100 Subject: [PATCH 064/109] Remove unneeded include directory --- src/libtomahawk/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index a85ad861b..ed0e7c4c9 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -500,7 +500,6 @@ include_directories( . ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/. ${LIBECHONEST_INCLUDE_DIR} ${LIBECHONEST_INCLUDE_DIR}/.. ${CLUCENE_INCLUDE_DIR} - ${CLUCENE_LIBRARY_DIR} ${PHONON_INCLUDES} ${CMAKE_BINARY_DIR}/thirdparty/liblastfm2/src From 7829be093678a78b163acbfddfa51f1a52ffff87 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 30 Oct 2011 20:51:42 -0400 Subject: [PATCH 065/109] darker background for highlight on osx --- src/libtomahawk/playlist/albumitemdelegate.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libtomahawk/playlist/albumitemdelegate.cpp b/src/libtomahawk/playlist/albumitemdelegate.cpp index e1d025c3b..ee461f225 100644 --- a/src/libtomahawk/playlist/albumitemdelegate.cpp +++ b/src/libtomahawk/playlist/albumitemdelegate.cpp @@ -160,7 +160,11 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, if ( m_hoveringOver == index ) TomahawkUtils::drawQueryBackground( painter, opt.palette, r, 1.5 ); +#ifdef Q_WS_MAC + painter->setPen( opt.palette.color( QPalette::Dark ).darker( 200 ) ); +#else painter->setPen( opt.palette.color( QPalette::Dark ) ); +#endif to.setAlignment( Qt::AlignHCenter | Qt::AlignBottom ); text = painter->fontMetrics().elidedText( item->album()->artist()->name(), Qt::ElideRight, textRect.width() - 3 ); painter->drawText( textRect, text, to ); From cd04d027a0cd47e61c4d9d801e66665370ca28bd Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 30 Oct 2011 18:19:32 -0400 Subject: [PATCH 066/109] support and fix search tomahawk link --- src/libtomahawk/globalactionmanager.cpp | 29 +++++++++++++++---------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/libtomahawk/globalactionmanager.cpp b/src/libtomahawk/globalactionmanager.cpp index e01bf423e..b0bbe57fd 100644 --- a/src/libtomahawk/globalactionmanager.cpp +++ b/src/libtomahawk/globalactionmanager.cpp @@ -50,6 +50,7 @@ #include "utils/spotifyparser.h" #include "utils/shortenedlinkparser.h" #include "utils/rdioparser.h" +#include "widgets/searchwidget.h" GlobalActionManager* GlobalActionManager::s_instance = 0; @@ -500,20 +501,26 @@ bool GlobalActionManager::handleSearchCommand( const QUrl& url ) { // open the super collection and set this as the search filter - QStringList query; - if( url.hasQueryItem( "artist" ) ) - query << url.queryItemValue( "artist" ); - if( url.hasQueryItem( "album" ) ) - query << url.queryItemValue( "album" ); - if( url.hasQueryItem( "title" ) ) - query << url.queryItemValue( "title" ); - QString queryStr = query.join( " " ); + QString queryStr; + if ( url.hasQueryItem( "query" ) ) + queryStr = url.queryItemValue( "query" ); + else + { + QStringList query; + if( url.hasQueryItem( "artist" ) ) + query << url.queryItemValue( "artist" ); + if( url.hasQueryItem( "album" ) ) + query << url.queryItemValue( "album" ); + if( url.hasQueryItem( "title" ) ) + query << url.queryItemValue( "title" ); + queryStr = query.join( " " ); + } - if( queryStr.isEmpty() ) + if( queryStr.trimmed().isEmpty() ) return false; - ViewManager::instance()->showSuperCollection(); -// ViewManager::instance()->topbar()->setFilter( queryStr ); + ViewManager::instance()->show( new SearchWidget( queryStr.trimmed() ) ); + return true; } From 44fa85af6b4fa6c6b5d73140cbb0c3b17e2c8700 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 30 Oct 2011 18:28:17 -0400 Subject: [PATCH 067/109] sort chart dropdowns --- src/libtomahawk/widgets/BreadcrumbButton.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/widgets/BreadcrumbButton.cpp b/src/libtomahawk/widgets/BreadcrumbButton.cpp index 4a94b469a..e298d4b5b 100644 --- a/src/libtomahawk/widgets/BreadcrumbButton.cpp +++ b/src/libtomahawk/widgets/BreadcrumbButton.cpp @@ -102,6 +102,10 @@ BreadcrumbButton::sizeHint() const return m_combo->sizeHint() + QSize( padding, 0 ); } +bool caseInsensitiveLessThan( const QString &s1, const QString &s2 ) +{ + return s1.toLower() < s2.toLower(); +} void BreadcrumbButton::setParentIndex( const QModelIndex& idx ) @@ -112,12 +116,19 @@ BreadcrumbButton::setParentIndex( const QModelIndex& idx ) QStringList list; int count = m_model->rowCount( m_parentIndex ); int defaultIndex = -1, userSelected = -1; + + // Two-pass so we can sort the list first + for ( int i = 0; i < count; ++i ) + { + list << m_model->index( i, 0, m_parentIndex ).data().toString(); + } + qSort( list.begin(), list.end(), caseInsensitiveLessThan ); + for ( int i = 0; i < count; ++i ) { QModelIndex idx = m_model->index( i, 0, m_parentIndex ); if ( idx.isValid() ) { - list << idx.data().toString(); if ( idx.data( Breadcrumb::DefaultRole ).toBool() ) defaultIndex = i; if ( idx.data( Breadcrumb::UserSelectedRole ).toBool() ) From f3dfc9631c7711685adb3a0c32a65950c02c4464 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 30 Oct 2011 18:41:19 -0400 Subject: [PATCH 068/109] fix some playlist drops on sidebar stuff --- src/libtomahawk/dropjob.cpp | 7 +++++++ src/sourcetree/sourcetreeview.cpp | 3 +++ 2 files changed, 10 insertions(+) diff --git a/src/libtomahawk/dropjob.cpp b/src/libtomahawk/dropjob.cpp index 48f9d212b..5b7955f97 100644 --- a/src/libtomahawk/dropjob.cpp +++ b/src/libtomahawk/dropjob.cpp @@ -152,6 +152,13 @@ DropJob::isDropType( DropJob::DropType desired, const QMimeData* data ) // Not the most elegant if ( url.contains( "spotify" ) && url.contains( "playlist" ) && s_canParseSpotifyPlaylists ) return true; + + // we don't know about these.. gotta say yes for now + if ( url.contains( "bit.ly" ) || + url.contains( "j.mp" ) || + url.contains( "t.co" ) || + url.contains( "rd.io" ) ) + return true; } return false; diff --git a/src/sourcetree/sourcetreeview.cpp b/src/sourcetree/sourcetreeview.cpp index 8e0cbec8d..c14119a7c 100644 --- a/src/sourcetree/sourcetreeview.cpp +++ b/src/sourcetree/sourcetreeview.cpp @@ -539,6 +539,9 @@ SourceTreeView::dropEvent( QDropEvent* event ) dropThis->setDropTypes( DropJob::Playlist ); dropThis->setDropAction( DropJob::Create ); dropThis->parseMimeData( event->mimeData() ); + + // Don't add it to the playlist under drop, it's a new playlist now + return; } QTreeView::dropEvent( event ); From f94fd218a0417d74d8fcd0e75887ecce6b97bdd0 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 30 Oct 2011 19:33:45 -0400 Subject: [PATCH 069/109] Fix some event loop reentrancy madness by creating the playlist on the event loop --- src/sourcetree/items/categoryitems.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sourcetree/items/categoryitems.cpp b/src/sourcetree/items/categoryitems.cpp index c1237cd97..896ffdd21 100644 --- a/src/sourcetree/items/categoryitems.cpp +++ b/src/sourcetree/items/categoryitems.cpp @@ -256,7 +256,7 @@ CategoryAddItem::dropMimeData( const QMimeData* data, Qt::DropAction ) if ( data->hasFormat( "application/tomahawk.dragsource.type" ) ) dj->setProperty( "dragsource", QString::fromUtf8( data->data( "application/tomahawk.dragsource.type" ) ) ); - connect( dj, SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) ); + connect( dj, SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ), Qt::QueuedConnection ); if ( dropType() == DropTypeAllFromArtist ) dj->setGetWholeArtists( true ); if ( dropType() == DropTypeThisAlbum ) From 5f62c1abb247fcc926824913ffb3a5eed03bd669 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 30 Oct 2011 20:48:52 -0400 Subject: [PATCH 070/109] Use the no-background cover image when there's no shadow --- resources.qrc | 1 + src/audiocontrols.cpp | 2 +- src/libtomahawk/playlist/treeitemdelegate.cpp | 2 +- src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/resources.qrc b/resources.qrc index 2260960e0..d6dc20bdb 100644 --- a/resources.qrc +++ b/resources.qrc @@ -127,5 +127,6 @@ data/images/headphones-off.png data/images/headphones-sidebar.png data/images/headphones-bigger.png + data/images/no-album-no-case.png diff --git a/src/audiocontrols.cpp b/src/audiocontrols.cpp index 0139b3145..42f8d7ab0 100644 --- a/src/audiocontrols.cpp +++ b/src/audiocontrols.cpp @@ -128,7 +128,7 @@ AudioControls::AudioControls( QWidget* parent ) connect( AudioEngine::instance(), SIGNAL( timerMilliSeconds( qint64 ) ), SLOT( onPlaybackTimer( qint64 ) ) ); connect( AudioEngine::instance(), SIGNAL( volumeChanged( int ) ), SLOT( onVolumeChanged( int ) ) ); - m_defaultCover = QPixmap( RESPATH "images/no-album-art-placeholder.png" ) + m_defaultCover = QPixmap( RESPATH "images/no-album-no-case.png" ) .scaled( ui->coverImage->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); connect( Tomahawk::InfoSystem::InfoSystem::instance(), diff --git a/src/libtomahawk/playlist/treeitemdelegate.cpp b/src/libtomahawk/playlist/treeitemdelegate.cpp index 1328b15cd..dee8934c9 100644 --- a/src/libtomahawk/playlist/treeitemdelegate.cpp +++ b/src/libtomahawk/playlist/treeitemdelegate.cpp @@ -40,7 +40,7 @@ TreeItemDelegate::TreeItemDelegate( ArtistView* parent, TreeProxyModel* proxy ) , m_model( proxy ) { m_nowPlayingIcon = QPixmap( RESPATH "images/now-playing-speaker.png" ); - m_defaultAlbumCover = QPixmap( RESPATH "images/no-album-art-placeholder.png" ); + m_defaultAlbumCover = QPixmap( RESPATH "images/no-album-no-case.png" ); m_defaultArtistImage = QPixmap( RESPATH "images/no-artist-image-placeholder.png" ); } diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index f8743a61a..8f24ae62d 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -74,7 +74,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* m_topHitsModel->setStyle( TrackModel::Short ); ui->topHits->setTrackModel( m_topHitsModel ); - m_pixmap = QPixmap( RESPATH "images/no-album-art-placeholder.png" ).scaledToWidth( 48, Qt::SmoothTransformation ); + m_pixmap = QPixmap( RESPATH "images/no-album-no-case.png" ).scaledToWidth( 48, Qt::SmoothTransformation ); m_button = new OverlayButton( ui->albums ); m_button->setText( tr( "Click to show All Releases" ) ); From ca0166d8fda00cad07d481d5fbe75a015742cf6c Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 30 Oct 2011 20:49:26 -0400 Subject: [PATCH 071/109] Set the playlist in audioengine to null when setting the playlist to null --- src/libtomahawk/audio/audioengine.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libtomahawk/audio/audioengine.cpp b/src/libtomahawk/audio/audioengine.cpp index 2398749f2..2831c74a1 100644 --- a/src/libtomahawk/audio/audioengine.cpp +++ b/src/libtomahawk/audio/audioengine.cpp @@ -682,7 +682,10 @@ AudioEngine::setPlaylist( PlaylistInterface* playlist ) } if ( !playlist ) + { + m_playlist.clear(); return; + } m_playlist = playlist->getSharedPointer(); From 65779b18eb2e683fe94fd67af3b734f67e7fd852 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 30 Oct 2011 20:58:58 -0400 Subject: [PATCH 072/109] Properly sort --- src/libtomahawk/widgets/BreadcrumbButton.cpp | 13 +------------ src/libtomahawk/widgets/whatshotwidget.cpp | 10 ++++++++-- src/libtomahawk/widgets/whatshotwidget.h | 2 ++ 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/libtomahawk/widgets/BreadcrumbButton.cpp b/src/libtomahawk/widgets/BreadcrumbButton.cpp index e298d4b5b..4a94b469a 100644 --- a/src/libtomahawk/widgets/BreadcrumbButton.cpp +++ b/src/libtomahawk/widgets/BreadcrumbButton.cpp @@ -102,10 +102,6 @@ BreadcrumbButton::sizeHint() const return m_combo->sizeHint() + QSize( padding, 0 ); } -bool caseInsensitiveLessThan( const QString &s1, const QString &s2 ) -{ - return s1.toLower() < s2.toLower(); -} void BreadcrumbButton::setParentIndex( const QModelIndex& idx ) @@ -116,19 +112,12 @@ BreadcrumbButton::setParentIndex( const QModelIndex& idx ) QStringList list; int count = m_model->rowCount( m_parentIndex ); int defaultIndex = -1, userSelected = -1; - - // Two-pass so we can sort the list first - for ( int i = 0; i < count; ++i ) - { - list << m_model->index( i, 0, m_parentIndex ).data().toString(); - } - qSort( list.begin(), list.end(), caseInsensitiveLessThan ); - for ( int i = 0; i < count; ++i ) { QModelIndex idx = m_model->index( i, 0, m_parentIndex ); if ( idx.isValid() ) { + list << idx.data().toString(); if ( idx.data( Breadcrumb::DefaultRole ).toBool() ) defaultIndex = i; if ( idx.data( Breadcrumb::UserSelectedRole ).toBool() ) diff --git a/src/libtomahawk/widgets/whatshotwidget.cpp b/src/libtomahawk/widgets/whatshotwidget.cpp index f96ca18ba..a0ac24639 100644 --- a/src/libtomahawk/widgets/whatshotwidget.cpp +++ b/src/libtomahawk/widgets/whatshotwidget.cpp @@ -50,6 +50,7 @@ static QString s_whatsHotIdentifier = QString( "WhatsHotWidget" ); WhatsHotWidget::WhatsHotWidget( QWidget* parent ) : QWidget( parent ) , ui( new Ui::WhatsHotWidget ) + , m_sortedProxy( 0 ) { ui->setupUi( this ); @@ -64,6 +65,9 @@ WhatsHotWidget::WhatsHotWidget( QWidget* parent ) TomahawkUtils::unmarginLayout( ui->verticalLayout->layout() ); m_crumbModelLeft = new QStandardItemModel( this ); + m_sortedProxy = new QSortFilterProxyModel( this ); + m_sortedProxy->setDynamicSortFilter( true ); + m_sortedProxy->setFilterCaseSensitivity( Qt::CaseInsensitive ); ui->breadCrumbLeft->setRootIcon( QPixmap( RESPATH "images/charts.png" ) ); @@ -209,7 +213,9 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat } } - ui->breadCrumbLeft->setModel( m_crumbModelLeft ); + m_sortedProxy->setSourceModel( m_crumbModelLeft ); + m_sortedProxy->sort( 0, Qt::AscendingOrder ); + ui->breadCrumbLeft->setModel( m_sortedProxy ); break; } case InfoSystem::InfoChart: @@ -310,7 +316,7 @@ void WhatsHotWidget::leftCrumbIndexChanged( QModelIndex index ) { tDebug( LOGVERBOSE ) << "WhatsHot:: left crumb changed" << index.data(); - QStandardItem* item = m_crumbModelLeft->itemFromIndex( index ); + QStandardItem* item = m_crumbModelLeft->itemFromIndex( m_sortedProxy->mapToSource( index ) ); if( !item ) return; if( !item->data( Breadcrumb::ChartIdRole ).isValid() ) diff --git a/src/libtomahawk/widgets/whatshotwidget.h b/src/libtomahawk/widgets/whatshotwidget.h index dcfa34f3a..ecd0be8ef 100644 --- a/src/libtomahawk/widgets/whatshotwidget.h +++ b/src/libtomahawk/widgets/whatshotwidget.h @@ -31,6 +31,7 @@ #include "dllmacro.h" +class QSortFilterProxyModel; class QStandardItemModel; class QStandardItem; class TreeModel; @@ -91,6 +92,7 @@ private: Ui::WhatsHotWidget *ui; QStandardItemModel* m_crumbModelLeft; + QSortFilterProxyModel* m_sortedProxy; // Cache our model data QHash< QString, AlbumModel* > m_albumModels; From e9ab459b3067a15f13988cb12e76314315462b65 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 30 Oct 2011 21:32:49 -0400 Subject: [PATCH 073/109] Always accept playlist drops on the sidebar --- src/sourcetree/sourcetreeview.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sourcetree/sourcetreeview.cpp b/src/sourcetree/sourcetreeview.cpp index c14119a7c..39771bdc7 100644 --- a/src/sourcetree/sourcetreeview.cpp +++ b/src/sourcetree/sourcetreeview.cpp @@ -485,8 +485,9 @@ SourceTreeView::dragMoveEvent( QDragMoveEvent* event ) m_dropRect = QRect(); } - if ( accept ) + if ( accept || DropJob::isDropType( DropJob::Playlist, event->mimeData() ) ) { + // Playlists are accepted always since they can be dropped anywhere //tDebug() << Q_FUNC_INFO << "Accepting"; event->setDropAction( Qt::CopyAction ); event->accept(); From aa8898a720f708ca7bd26a9968f51f43b996943f Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 30 Oct 2011 21:57:55 -0400 Subject: [PATCH 074/109] Do better th an QSizePolicy::Fixed by setting min/max heights too --- src/libtomahawk/infobar/infobar.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libtomahawk/infobar/infobar.cpp b/src/libtomahawk/infobar/infobar.cpp index d4f39d680..4296b75ae 100644 --- a/src/libtomahawk/infobar/infobar.cpp +++ b/src/libtomahawk/infobar/infobar.cpp @@ -105,6 +105,8 @@ InfoBar::InfoBar( QWidget* parent ) setPalette( p ); setAutoFillBackground( true ); + setMinimumHeight( geometry().height() ); + setMaximumHeight( geometry().height() ); connect( ViewManager::instance(), SIGNAL( filterAvailable( bool ) ), SLOT( setFilterAvailable( bool ) ) ); connect( ViewManager::instance(), SIGNAL( autoUpdateAvailable( bool ) ), SLOT( setAutoUpdateAvailable( bool ) ) ); } From ae56d5107697a842ef2bfed4d2d6f602a8dd9c8c Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 30 Oct 2011 22:13:59 -0400 Subject: [PATCH 075/109] =?UTF-8?q?Properly=20escape=20the=20=C3=B6=20in?= =?UTF-8?q?=20Hugo's=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/tomahawkwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tomahawkwindow.cpp b/src/tomahawkwindow.cpp index 9aa03157f..44931115d 100644 --- a/src/tomahawkwindow.cpp +++ b/src/tomahawkwindow.cpp @@ -710,7 +710,7 @@ TomahawkWindow::showAboutTomahawk() { QMessageBox::about( this, tr( "About Tomahawk" ), tr( "

    Tomahawk %1
    (%2)

    Copyright 2010, 2011
    Christian Muehlhaeuser <muesli@tomahawk-player.org>

    " - "Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindström, Michael Zanetti, Harald Sitter and Steve Robertson" ) + "Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindström, Michael Zanetti, Harald Sitter and Steve Robertson" ) .arg( TomahawkUtils::appFriendlyVersion() ) .arg( qApp->applicationVersion() ) ); } From 7ab68fb41885d4c1eeea88b6743eb96e10fa7dc4 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sun, 30 Oct 2011 22:36:14 -0400 Subject: [PATCH 076/109] Show the same mode as the source when clicking into an album from the collection --- src/libtomahawk/playlist/artistview.cpp | 2 +- src/libtomahawk/playlist/treemodel.cpp | 16 +++++++-------- src/libtomahawk/playlist/treemodel.h | 10 ++++------ src/libtomahawk/playlist/treeproxymodel.cpp | 2 +- src/libtomahawk/typedefs.h | 6 ++++++ src/libtomahawk/viewmanager.cpp | 4 ++-- src/libtomahawk/viewmanager.h | 2 +- .../widgets/infowidgets/AlbumInfoWidget.cpp | 20 ++++++++++++++----- .../widgets/infowidgets/AlbumInfoWidget.h | 5 ++++- .../widgets/infowidgets/ArtistInfoWidget.cpp | 5 ++--- 10 files changed, 44 insertions(+), 28 deletions(-) diff --git a/src/libtomahawk/playlist/artistview.cpp b/src/libtomahawk/playlist/artistview.cpp index 61851b692..59ce86097 100644 --- a/src/libtomahawk/playlist/artistview.cpp +++ b/src/libtomahawk/playlist/artistview.cpp @@ -158,7 +158,7 @@ ArtistView::onItemActivated( const QModelIndex& index ) if ( !item->artist().isNull() ) ViewManager::instance()->show( item->artist() ); else if ( !item->album().isNull() ) - ViewManager::instance()->show( item->album() ); + ViewManager::instance()->show( item->album(), m_model->mode() ); else if ( !item->result().isNull() && item->result()->isOnline() ) { m_model->setCurrentItem( item->index ); diff --git a/src/libtomahawk/playlist/treemodel.cpp b/src/libtomahawk/playlist/treemodel.cpp index 154548d7f..56afbc2e2 100644 --- a/src/libtomahawk/playlist/treemodel.cpp +++ b/src/libtomahawk/playlist/treemodel.cpp @@ -38,7 +38,7 @@ TreeModel::TreeModel( QObject* parent ) , m_rootItem( new TreeModelItem( 0, this ) ) , m_infoId( uuid() ) , m_columnStyle( AllColumns ) - , m_mode( Database ) + , m_mode( DatabaseMode ) { setIcon( QPixmap( RESPATH "images/music-icon.png" ) ); @@ -576,7 +576,7 @@ TreeModel::addAlbums( const artist_ptr& artist, const QModelIndex& parent ) { emit loadingStarted(); - if ( m_mode == Database ) + if ( m_mode == DatabaseMode ) { DatabaseCommand_AllAlbums* cmd = new DatabaseCommand_AllAlbums( m_collection, artist ); cmd->setData( parent.row() ); @@ -586,7 +586,7 @@ TreeModel::addAlbums( const artist_ptr& artist, const QModelIndex& parent ) Database::instance()->enqueue( QSharedPointer( cmd ) ); } - else if ( m_mode == InfoSystem ) + else if ( m_mode == InfoSystemMode ) { Tomahawk::InfoSystem::InfoStringHash artistInfo; artistInfo["artist"] = artist->name(); @@ -612,7 +612,7 @@ TreeModel::addTracks( const album_ptr& album, const QModelIndex& parent ) rows << parent.row(); rows << parent.parent().row(); - if ( m_mode == Database ) + if ( m_mode == DatabaseMode ) { DatabaseCommand_AllTracks* cmd = new DatabaseCommand_AllTracks( m_collection ); cmd->setAlbum( album.data() ); @@ -623,7 +623,7 @@ TreeModel::addTracks( const album_ptr& album, const QModelIndex& parent ) Database::instance()->enqueue( QSharedPointer( cmd ) ); } - else if ( m_mode == InfoSystem ) + else if ( m_mode == InfoSystemMode ) { Tomahawk::InfoSystem::InfoStringHash artistInfo; artistInfo["artist"] = album->artist()->name(); @@ -850,14 +850,14 @@ TreeModel::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QV { if ( m_receivedInfoData.contains( requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >() ) ) break; - + QVariantMap returnedData = output.value< QVariantMap >(); if ( returnedData.isEmpty() ) break; - + m_receivedInfoData.insert( requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >() ); - + QStringList tracks = returnedData[ "tracks" ].toStringList(); QList ql; diff --git a/src/libtomahawk/playlist/treemodel.h b/src/libtomahawk/playlist/treemodel.h index 0c22511cc..3973b396f 100644 --- a/src/libtomahawk/playlist/treemodel.h +++ b/src/libtomahawk/playlist/treemodel.h @@ -33,6 +33,7 @@ #include "infosystem/infosystem.h" #include "dllmacro.h" +#include "typedefs.h" class QMetaData; @@ -55,9 +56,6 @@ public: enum ColumnStyle { AllColumns = 0, TrackOnly }; - enum ModelMode - { Database = 0, InfoSystem }; - explicit TreeModel( QObject* parent = 0 ); virtual ~TreeModel(); @@ -73,8 +71,8 @@ public: virtual int rowCount( const QModelIndex& parent = QModelIndex() ) const; virtual int columnCount( const QModelIndex& parent = QModelIndex() ) const; - virtual ModelMode mode() const { return m_mode; } - virtual void setMode( ModelMode mode ) { m_mode = mode; } + virtual Tomahawk::ModelMode mode() const { return m_mode; } + virtual void setMode( Tomahawk::ModelMode mode ) { m_mode = mode; } virtual QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const; virtual QVariant headerData( int section, Qt::Orientation orientation, int role ) const; @@ -165,7 +163,7 @@ private: QString m_description; QPixmap m_icon; ColumnStyle m_columnStyle; - ModelMode m_mode; + Tomahawk::ModelMode m_mode; QList m_artistsFilter; diff --git a/src/libtomahawk/playlist/treeproxymodel.cpp b/src/libtomahawk/playlist/treeproxymodel.cpp index c6c7f99cf..5530d9aae 100644 --- a/src/libtomahawk/playlist/treeproxymodel.cpp +++ b/src/libtomahawk/playlist/treeproxymodel.cpp @@ -195,7 +195,7 @@ TreeProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent TreeModelItem* pi = sourceModel()->itemFromIndex( sourceModel()->index( sourceRow, 0, sourceParent ) ); Q_ASSERT( pi ); - if ( m_model->mode() == TreeModel::Database && !pi->result().isNull() ) + if ( m_model->mode() == Tomahawk::DatabaseMode && !pi->result().isNull() ) { QList< Tomahawk::result_ptr > rl = m_cache.values( sourceParent ); foreach ( const Tomahawk::result_ptr& result, rl ) diff --git a/src/libtomahawk/typedefs.h b/src/libtomahawk/typedefs.h index b3b98232e..b972009ba 100644 --- a/src/libtomahawk/typedefs.h +++ b/src/libtomahawk/typedefs.h @@ -63,6 +63,12 @@ namespace Tomahawk Static }; + enum ModelMode + { + DatabaseMode = 0, + InfoSystemMode + }; + }; // ns typedef int AudioErrorCode; diff --git a/src/libtomahawk/viewmanager.cpp b/src/libtomahawk/viewmanager.cpp index 8f022890c..b7cd72b08 100644 --- a/src/libtomahawk/viewmanager.cpp +++ b/src/libtomahawk/viewmanager.cpp @@ -217,12 +217,12 @@ ViewManager::show( const Tomahawk::artist_ptr& artist ) Tomahawk::ViewPage* -ViewManager::show( const Tomahawk::album_ptr& album ) +ViewManager::show( const Tomahawk::album_ptr& album, Tomahawk::ModelMode initialMode ) { AlbumInfoWidget* swidget; if ( !m_albumViews.contains( album ) || m_albumViews.value( album ).isNull() ) { - swidget = new AlbumInfoWidget( album ); + swidget = new AlbumInfoWidget( album, initialMode ); m_albumViews.insert( album, swidget ); } else diff --git a/src/libtomahawk/viewmanager.h b/src/libtomahawk/viewmanager.h index 38820b63b..3c2bed9a6 100644 --- a/src/libtomahawk/viewmanager.h +++ b/src/libtomahawk/viewmanager.h @@ -139,7 +139,7 @@ public slots: Tomahawk::ViewPage* show( const Tomahawk::playlist_ptr& playlist ); Tomahawk::ViewPage* show( const Tomahawk::dynplaylist_ptr& playlist ); Tomahawk::ViewPage* show( const Tomahawk::artist_ptr& artist ); - Tomahawk::ViewPage* show( const Tomahawk::album_ptr& album ); + Tomahawk::ViewPage* show( const Tomahawk::album_ptr& album, Tomahawk::ModelMode withInitialMode = Tomahawk::InfoSystemMode ); Tomahawk::ViewPage* show( const Tomahawk::collection_ptr& collection ); Tomahawk::ViewPage* show( const Tomahawk::source_ptr& source ); diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp index 3b5877a96..2f0f8415a 100644 --- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp @@ -39,7 +39,7 @@ static QString s_aiInfoIdentifier = QString( "AlbumInfoWidget" ); using namespace Tomahawk; -AlbumInfoWidget::AlbumInfoWidget( const Tomahawk::album_ptr& album, QWidget* parent ) +AlbumInfoWidget::AlbumInfoWidget( const Tomahawk::album_ptr& album, ModelMode startingMode, QWidget* parent ) : QWidget( parent ) , ui( new Ui::AlbumInfoWidget ) { @@ -58,16 +58,19 @@ AlbumInfoWidget::AlbumInfoWidget( const Tomahawk::album_ptr& album, QWidget* par ui->albumsView->setAlbumModel( m_albumsModel ); m_tracksModel = new TreeModel( ui->tracksView ); - m_tracksModel->setMode( TreeModel::InfoSystem ); + m_tracksModel->setMode( startingMode ); ui->tracksView->setTreeModel( m_tracksModel ); ui->tracksView->setRootIsDecorated( false ); m_pixmap = QPixmap( RESPATH "images/no-album-art-placeholder.png" ).scaledToWidth( 48, Qt::SmoothTransformation ); m_button = new OverlayButton( ui->tracksView ); - m_button->setText( tr( "Click to show Super Collection Tracks" ) ); m_button->setCheckable( true ); - m_button->setChecked( true ); + m_button->setChecked( m_tracksModel->mode() == InfoSystemMode ); + if ( m_button->isChecked() ) + m_button->setText( tr( "Click to show Super Collection Tracks" ) ); + else + m_button->setText( tr( "Click to show Official Tracks" ) ); connect( m_button, SIGNAL( clicked() ), SLOT( onModeToggle() ) ); connect( m_tracksModel, SIGNAL( loadingStarted() ), SLOT( onLoadingStarted() ) ); @@ -94,11 +97,18 @@ AlbumInfoWidget::playlistInterface() const return ui->tracksView->playlistInterface(); } +void +AlbumInfoWidget::setMode( ModelMode mode ) +{ + if ( m_tracksModel->mode() != mode ) + onModeToggle(); +} + void AlbumInfoWidget::onModeToggle() { - m_tracksModel->setMode( m_button->isChecked() ? TreeModel::InfoSystem : TreeModel::Database ); + m_tracksModel->setMode( m_button->isChecked() ? InfoSystemMode : DatabaseMode ); m_tracksModel->clear(); m_tracksModel->addTracks( m_album, QModelIndex() ); diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h index 8fe7c286c..9542ab679 100644 --- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h +++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h @@ -35,6 +35,7 @@ #include "infosystem/infosystem.h" #include "dllmacro.h" +#include "typedefs.h" class AlbumModel; class TreeModel; @@ -50,7 +51,7 @@ class DLLEXPORT AlbumInfoWidget : public QWidget, public Tomahawk::ViewPage Q_OBJECT public: - AlbumInfoWidget( const Tomahawk::album_ptr& album, QWidget* parent = 0 ); + AlbumInfoWidget( const Tomahawk::album_ptr& album, Tomahawk::ModelMode startingMode = Tomahawk::InfoSystemMode, QWidget* parent = 0 ); ~AlbumInfoWidget(); virtual QWidget* widget() { return this; } @@ -63,6 +64,8 @@ public: virtual QString longDescription() const { return m_longDescription; } virtual QPixmap pixmap() const { if ( m_pixmap.isNull() ) return Tomahawk::ViewPage::pixmap(); else return m_pixmap; } + void setMode( Tomahawk::ModelMode mode ); + virtual bool isTemporaryPage() const { return true; } virtual bool showStatsBar() const { return false; } diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index 8f24ae62d..3ead1a03c 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -21,7 +21,6 @@ #include "ui_ArtistInfoWidget.h" #include "audio/audioengine.h" -#include "viewmanager.h" #include "playlist/treemodel.h" #include "playlist/playlistmodel.h" #include "playlist/treeproxymodel.h" @@ -63,7 +62,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* TomahawkUtils::unmarginLayout( ui->albumHeader->layout() ); m_albumsModel = new TreeModel( ui->albums ); - m_albumsModel->setMode( TreeModel::InfoSystem ); + m_albumsModel->setMode( InfoSystemMode ); ui->albums->setTreeModel( m_albumsModel ); m_relatedModel = new TreeModel( ui->relatedArtists ); @@ -111,7 +110,7 @@ ArtistInfoWidget::playlistInterface() const void ArtistInfoWidget::onModeToggle() { - m_albumsModel->setMode( m_button->isChecked() ? TreeModel::InfoSystem : TreeModel::Database ); + m_albumsModel->setMode( m_button->isChecked() ? InfoSystemMode : DatabaseMode ); m_albumsModel->clear(); m_albumsModel->addAlbums( m_artist, QModelIndex() ); From c82845d22a7b2041ea9774672284af7008e2d24c Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Mon, 31 Oct 2011 17:15:52 -0400 Subject: [PATCH 077/109] Fix offset querylabel due to wrong fontmetrics --- src/libtomahawk/widgets/querylabel.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/widgets/querylabel.cpp b/src/libtomahawk/widgets/querylabel.cpp index 78a95c3f2..531ef31e5 100644 --- a/src/libtomahawk/widgets/querylabel.cpp +++ b/src/libtomahawk/widgets/querylabel.cpp @@ -352,18 +352,23 @@ QueryLabel::paintEvent( QPaintEvent* event ) QString s = text(); const QString elidedText = fontMetrics().elidedText( s, m_mode, r.width() ); + qDebug() << "ELIDED TEXT:" << elidedText << "orig:" << s; p.save(); p.setRenderHint( QPainter::Antialiasing ); + QFontMetrics fm = fontMetrics(); if ( m_useCustomFont ) + { p.setFont( m_font ); + fm = QFontMetrics( m_font ); + } if ( m_hoverArea.width() ) { if ( elidedText != s ) { m_hoverArea.setLeft( 0 ); - m_hoverArea.setRight( fontMetrics().width( elidedText ) + contentsMargins().left() * 2 ); + m_hoverArea.setRight( fm.width( elidedText ) + contentsMargins().left() * 2 ); m_hoverType = Track; } @@ -386,7 +391,6 @@ QueryLabel::paintEvent( QPaintEvent* event ) } else { - const QFontMetrics& fm = fontMetrics(); int dashX = fm.width( DASH ); int artistX = m_type & Artist ? fm.width( artist() ) : 0; int albumX = m_type & Album ? fm.width( album() ) : 0; From cce80ff53534ab95d82d13532265bba9b87760c0 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Mon, 31 Oct 2011 17:16:45 -0400 Subject: [PATCH 078/109] did not mean to commit that debug --- src/libtomahawk/widgets/querylabel.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libtomahawk/widgets/querylabel.cpp b/src/libtomahawk/widgets/querylabel.cpp index 531ef31e5..0218bfc6d 100644 --- a/src/libtomahawk/widgets/querylabel.cpp +++ b/src/libtomahawk/widgets/querylabel.cpp @@ -352,7 +352,6 @@ QueryLabel::paintEvent( QPaintEvent* event ) QString s = text(); const QString elidedText = fontMetrics().elidedText( s, m_mode, r.width() ); - qDebug() << "ELIDED TEXT:" << elidedText << "orig:" << s; p.save(); p.setRenderHint( QPainter::Antialiasing ); From 4350fad789adb2a7c2a3e26ba58c237d788218cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Mon, 31 Oct 2011 00:10:05 +0100 Subject: [PATCH 079/109] Rdio parser for tracks, playlists, artists, albums. WIP --- resources.qrc | 7 +- src/libtomahawk/dropjob.cpp | 31 ++- src/libtomahawk/dropjob.h | 2 + src/libtomahawk/utils/rdioparser.cpp | 333 ++++++++++++++++++++++++--- src/libtomahawk/utils/rdioparser.h | 40 +++- 5 files changed, 370 insertions(+), 43 deletions(-) diff --git a/resources.qrc b/resources.qrc index d6dc20bdb..369d4955d 100644 --- a/resources.qrc +++ b/resources.qrc @@ -111,7 +111,7 @@ data/sql/dbmigrate-23_to_24.sql data/sql/dbmigrate-24_to_25.sql data/sql/dbmigrate-25_to_26.sql - data/sql/dbmigrate-26_to_27.sql + data/sql/dbmigrate-26_to_27.sql data/js/tomahawk.js data/images/avatar_frame.png data/images/drop-all-songs.png @@ -119,7 +119,7 @@ data/images/drop-top-songs.png data/images/drop-song.png data/images/drop-album.png - data/images/spotify-logo.png + data/images/spotify-logo.png data/images/itunes.png data/images/uploading.png data/images/downloading.png @@ -127,6 +127,7 @@ data/images/headphones-off.png data/images/headphones-sidebar.png data/images/headphones-bigger.png - data/images/no-album-no-case.png + data/images/no-album-no-case.png + data/images/rdio.png diff --git a/src/libtomahawk/dropjob.cpp b/src/libtomahawk/dropjob.cpp index 5b7955f97..530ad5f74 100644 --- a/src/libtomahawk/dropjob.cpp +++ b/src/libtomahawk/dropjob.cpp @@ -112,7 +112,7 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType if ( url.contains( "spotify" ) && url.contains( "track" ) ) return true; - if ( url.contains( "rdio.com" ) && url.contains( "track" ) ) + if ( url.contains( "rdio.com" ) && ( url.contains( "track" ) || /*url.contains( "artist" ) ||*/ url.contains( "album" ) || url.contains( "playlists" ) ) ) return true; } @@ -153,6 +153,9 @@ DropJob::isDropType( DropJob::DropType desired, const QMimeData* data ) if ( url.contains( "spotify" ) && url.contains( "playlist" ) && s_canParseSpotifyPlaylists ) return true; + if ( url.contains( "rdio.com" ) && url.contains( "people" ) && url.contains( "playlist" ) ) + return true; + // we don't know about these.. gotta say yes for now if ( url.contains( "bit.ly" ) || url.contains( "j.mp" ) || @@ -450,6 +453,30 @@ DropJob::handleSpotifyUrls( const QString& urlsRaw ) m_queryCount++; } +void +DropJob::handleRdioUrls( const QString& urlsRaw ) +{ + QStringList urls = urlsRaw.split( QRegExp( "\\s+" ), QString::SkipEmptyParts ); + qDebug() << "Got Rdio urls!!" << urls; + + if ( dropAction() == Default ) + setDropAction( Create ); + + RdioParser* rdio = new RdioParser( this ); + rdio->setCreatePlaylist( dropAction() == Create ); + rdio->parse( urls ); + + /// This currently supports draging and dropping a spotify playlist and artist + if ( dropAction() == Append ) + { + tDebug() << Q_FUNC_INFO << "Asking for spotify browse contents from" << urls; + connect( rdio, SIGNAL( tracks( QList ) ), this, SLOT( onTracksAdded( QList< Tomahawk::query_ptr > ) ) ); + } + + m_queryCount++; +} + + void DropJob::handleAllUrls( const QString& urls ) { @@ -459,6 +486,8 @@ DropJob::handleAllUrls( const QString& urls ) && ( urls.contains( "playlist" ) || urls.contains( "artist" ) || urls.contains( "album" ) || urls.contains( "track" ) ) && s_canParseSpotifyPlaylists ) handleSpotifyUrls( urls ); + else if ( urls.contains( "rdio.com" ) ) + handleRdioUrls( urls ); else handleTrackUrls ( urls ); } diff --git a/src/libtomahawk/dropjob.h b/src/libtomahawk/dropjob.h index 0142ec5bb..369899e11 100644 --- a/src/libtomahawk/dropjob.h +++ b/src/libtomahawk/dropjob.h @@ -102,7 +102,9 @@ public: void setGetWholeAlbums( bool getWholeAlbums ); void tracksFromMimeData( const QMimeData* data, bool allowDuplicates = false, bool onlyLocal = false, bool top10 = false ); void handleXspfs( const QString& files ); + void handleSpotifyUrls( const QString& urls ); + void handleRdioUrls( const QString& urls ); static bool canParseSpotifyPlaylists() { return s_canParseSpotifyPlaylists; } static void setCanParseSpotifyPlaylists( bool parseable ) { s_canParseSpotifyPlaylists = parseable; } diff --git a/src/libtomahawk/utils/rdioparser.cpp b/src/libtomahawk/utils/rdioparser.cpp index 8be1a40d2..028197c65 100644 --- a/src/libtomahawk/utils/rdioparser.cpp +++ b/src/libtomahawk/utils/rdioparser.cpp @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Leo Franchi + * Copyright 2010-2011, Hugo Lindström * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,15 +19,39 @@ #include "rdioparser.h" +#include "shortenedlinkparser.h" +#include "config.h" +#include "utils/tomahawkutils.h" +#include "utils/logger.h" +#include "dropjob.h" +#include "jobview/JobStatusView.h" +#include "jobview/JobStatusModel.h" +#include "dropjobnotifier.h" +#include "viewmanager.h" +#include "sourcelist.h" + +#include +#include +#include +#include #include #include -#include "shortenedlinkparser.h" + +#include using namespace Tomahawk; +QPixmap* RdioParser::s_pixmap = 0; + +#ifdef QCA2_FOUND +QCA::Initializer RdioParser::m_qcaInit = QCA::Initializer(); +#endif + RdioParser::RdioParser( QObject* parent ) : QObject( parent ) , m_count( 0 ) + , m_browseJob( 0 ) + , m_createPlaylist( false ) { } @@ -63,50 +88,273 @@ RdioParser::parseUrl( const QString& url ) return; } - query_ptr query; - m_count++; - - if ( url.contains( "artist" ) && url.contains( "album" ) ) + if ( url.contains( "artist" ) && url.contains( "album" ) && url.contains( "track" ) ) + parseTrack( url ); + else { - // this is a "full" url, no redirection needed - QString realUrl = QUrl::fromUserInput( url ).toString().replace( "_", " " ); - - QString artist, trk, album; - QString matchStr = "/%1/([^/]*)/"; - QRegExp r( QString( matchStr ).arg( "artist" ) ); - - int loc = r.indexIn( realUrl ); - if ( loc >= 0 ) - artist = r.cap( 1 ); - - r = QRegExp( QString( matchStr ).arg( "album" ) ); - loc = r.indexIn( realUrl ); - if ( loc >= 0 ) - album = r.cap( 1 ); - - r = QRegExp( QString( matchStr ).arg( "track" ) ); - loc = r.indexIn( realUrl ); - if ( loc >= 0 ) - trk = r.cap( 1 ); - - if ( !trk.isEmpty() && !artist.isEmpty() ) + DropJob::DropType type = DropJob::None; + if ( url.contains( "artist" ) && url.contains( "album" ) ) + type = DropJob::Album; + else if ( url.contains( "artist" ) ) + type = DropJob::Artist; + else if ( url.contains( "people" ) && url.contains( "playlist" ) ) + type = DropJob::Playlist; + else { - query = Query::get( artist, trk, album, uuid(), true ); + tLog() << "Got Rdio URL I can't parse!" << url; + return; } + + // artist, album, or playlist link requre fetching + fetchObjectsFromUrl( url, type ); } - if ( m_multi ) - { - if ( !query.isNull() ) - m_queries << query; - - if ( m_count == m_total ) - emit tracks( m_queries ); - } - if ( !m_multi && !query.isNull() ) - emit track( query ); } +void +RdioParser::fetchObjectsFromUrl( const QString& url, DropJob::DropType type ) +{ + QList< QPair< QByteArray, QByteArray > > params; + params.append( QPair( "extras", "tracks" ) ); + + QString cleanedUrl = url; + cleanedUrl.replace("#/", ""); + + QByteArray data; + QNetworkRequest request = generateRequest( "getObjectFromUrl", cleanedUrl, params, &data ); + + request.setHeader( QNetworkRequest::ContentTypeHeader, QLatin1String( "application/x-www-form-urlencoded" ) ); + QNetworkReply* reply = TomahawkUtils::nam()->post( request, data ); + connect( reply, SIGNAL( finished() ), this, SLOT( rdioReturned() ) ); + + m_browseJob = new DropJobNotifier( pixmap(), QString( "Rdio" ), type, reply ); + JobStatusView::instance()->model()->addJob( m_browseJob ); + + m_reqQueries.insert( reply ); +} + +void +RdioParser::rdioReturned() +{ + + QNetworkReply* r = qobject_cast< QNetworkReply* >( sender() ); + Q_ASSERT( r ); + m_reqQueries.remove( r ); + m_count++; + r->deleteLater(); + + if ( r->error() == QNetworkReply::NoError ) + { + QJson::Parser p; + bool ok; + QVariantMap res = p.parse( r, &ok ).toMap(); + QVariantMap result = res.value( "result" ).toMap(); + + if ( !ok || result.isEmpty() ) + { + tLog() << "Failed to parse json from Rdio browse item :" << p.errorString() << "On line" << p.errorLine() << "With data:" << res; + + return; + } + + QVariantList tracks = result.value( "tracks" ).toList(); + if ( tracks.isEmpty() ) + { + tLog() << "Got no tracks in result, ignoring!" << result; + return; + } + + // Playlists will have these + m_title = result[ "name" ].toString(); + m_creator = result[ "owner" ].toString(); + + foreach( QVariant track, tracks ) + { + QVariantMap rdioResult = track.toMap(); + QString title, artist, album; + + title = rdioResult.value( "name", QString() ).toString(); + artist = rdioResult.value( "artist", QString() ).toString(); + album = rdioResult.value( "album", QString() ).toString(); + + if ( title.isEmpty() && artist.isEmpty() ) // don't have enough... + { + tLog() << "Didn't get an artist and track name from Rdio, not enough to build a query on. Aborting" << title << artist << album; + return; + } + + Tomahawk::query_ptr q = Tomahawk::Query::get( artist, title, album, uuid(), !m_createPlaylist ); + m_tracks << q; + } + + } else + { + tLog() << "Error in network request to Rdio for track decoding:" << r->errorString(); + } + + + checkFinished(); + + +} + +void +RdioParser::parseTrack( const QString& origUrl ) +{ + QString url = origUrl; + QString artist, trk, album, playlist; + QString realUrl = url.replace( "_", " " ); + QString matchStr = "/%1/([^/]*)/"; + QString matchPlStr = "/%1/(?:[^/]*)/([^/]*)/"; + + QRegExp r( QString( matchStr ).arg( "artist" ) ); + + int loc = r.indexIn( realUrl ); + if ( loc >= 0 ) + artist = r.cap( 1 ); + + r = QRegExp( QString( matchStr ).arg( "album" ) ); + loc = r.indexIn( realUrl ); + if ( loc >= 0 ) + album = r.cap( 1 ); + + r = QRegExp( QString( matchStr ).arg( "track" ) ); + loc = r.indexIn( realUrl ); + if ( loc >= 0 ) + trk = r.cap( 1 ); + + r = QRegExp( QString( matchPlStr ).arg( "playlists" ) ); + loc = r.indexIn( realUrl ); + if ( loc >= 0 ) + playlist = r.cap( 1 ); + + if ( trk.isEmpty() || artist.isEmpty() ) + { + tLog() << "Parsed Rdio track url but it's missing artist or track!" << url; + return; + } + + query_ptr q = Query::get( artist, trk, album, uuid(), !m_createPlaylist ); + m_count++; + m_queries << q; + + checkFinished(); +} + + +QNetworkRequest +RdioParser::generateRequest( const QString& method, const QString& url, const QList< QPair< QByteArray, QByteArray > >& extraParams, QByteArray* data ) +{ + QUrl fetchUrl( "http://api.rdio.com/1/" ); + QUrl toSignUrl = fetchUrl; + + QPair param; + foreach( param, extraParams ) + { + toSignUrl.addEncodedQueryItem( param.first, param.second ); + } + toSignUrl.addQueryItem( "method", method ); + toSignUrl.addEncodedQueryItem("oauth_consumer_key", "gk8zmyzj5xztt8aj48csaart" ); + QString nonce; + for ( int i = 0; i < 8; i++ ) + nonce += QString::number( qrand() % 10 ); + toSignUrl.addQueryItem("oauth_nonce", nonce ); + toSignUrl.addEncodedQueryItem("oauth_signature_method", "HMAC-SHA1"); + toSignUrl.addQueryItem("oauth_timestamp", QString::number(QDateTime::currentMSecsSinceEpoch() / 1000 ) ); + toSignUrl.addEncodedQueryItem("oauth_version", "1.0"); + toSignUrl.addEncodedQueryItem( "url", QUrl::toPercentEncoding( url ) ); + int size = toSignUrl.encodedQueryItems().size(); + for( int i = 0; i < size; i++ ) { + const QPair< QByteArray, QByteArray > item = toSignUrl.encodedQueryItems().at( i ); + data->append( item.first + "=" + item.second + "&" ); + } + data->truncate( data->size() - 1 ); // remove extra & + + QByteArray toSign = "POST&" + QUrl::toPercentEncoding( fetchUrl.toEncoded() ) + '&' + QUrl::toPercentEncoding( *data ); + qDebug() << "Rdio" << toSign; + + toSignUrl.addEncodedQueryItem( "oauth_signature", QUrl::toPercentEncoding( hmacSha1("yt35kakDyW&", toSign ) ) ); + + data->clear(); + size = toSignUrl.encodedQueryItems().size(); + for( int i = 0; i < size; i++ ) { + const QPair< QByteArray, QByteArray > item = toSignUrl.encodedQueryItems().at( i ); + data->append( item.first + "=" + item.second + "&" ); + } + data->truncate( data->size() - 1 ); // remove extra & + + qDebug() << "POST data:" << *data; + + QNetworkRequest request = QNetworkRequest( fetchUrl ); + request.setHeader( QNetworkRequest::ContentTypeHeader, QLatin1String( "application/x-www-form-urlencoded" ) ); + + return request; +} + + +QByteArray +RdioParser::hmacSha1(QByteArray key, QByteArray baseString) +{ +#ifdef QCA2_FOUND + QCA::MessageAuthenticationCode hmacsha1( "hmac(sha1)", QCA::SecureArray() ); + QCA::SymmetricKey keyObject( key ); + hmacsha1.setup( keyObject ); + + hmacsha1.update( QCA::SecureArray( baseString ) ); + QCA::SecureArray resultArray = hmacsha1.final(); + + QByteArray result = resultArray.toByteArray().toBase64(); + return result; +#else + tLog() << "Tomahawk compiled without QCA support, cannot generate HMAC signature"; + return QByteArray(); +#endif +} + +void +RdioParser::checkFinished() +{ + tDebug() << "Checking for Rdio batch playlist job finished" << m_reqQueries.isEmpty(); + if ( m_reqQueries.isEmpty() ) // we're done + { + if ( m_browseJob ) + m_browseJob->setFinished(); + + if ( m_tracks.isEmpty() ) + return; + + if( m_createPlaylist ) + { + m_playlist = Playlist::create( SourceList::instance()->getLocal(), + uuid(), + m_title, + "", + m_creator, + false, + m_tracks ); + + connect( m_playlist.data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), this, SLOT( playlistCreated() ) ); + return; + } + else + { + if ( !m_multi ) + emit track( m_tracks.first() ); + else if ( m_multi && m_count == m_total ) + emit tracks( m_tracks ); + } + + deleteLater(); + } +} + +void +RdioParser::playlistCreated( Tomahawk::PlaylistRevision ) +{ + ViewManager::instance()->show( m_playlist ); +} + + void RdioParser::expandedLinks( const QStringList& urls ) { @@ -117,3 +365,12 @@ RdioParser::expandedLinks( const QStringList& urls ) } } +QPixmap +RdioParser::pixmap() const +{ + if ( !s_pixmap ) + s_pixmap = new QPixmap( RESPATH "images/rdio.png" ); + + return *s_pixmap; +} + diff --git a/src/libtomahawk/utils/rdioparser.h b/src/libtomahawk/utils/rdioparser.h index 9bcf53a1e..10d45fa61 100644 --- a/src/libtomahawk/utils/rdioparser.h +++ b/src/libtomahawk/utils/rdioparser.h @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Leo Franchi + * Copyright 2010-2011, Hugo Lindström * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,16 +19,27 @@ #ifndef RDIOPARSER_H #define RDIOPARSER_H +#include "jobview/JobStatusItem.h" +#include "query.h" +#include "config.h" +#include "dropjob.h" +#include "typedefs.h" +#include "playlist.h" #include #include +#include -#include "query.h" +#ifdef QCA2_FOUND +#include +#include +#endif class QNetworkReply; namespace Tomahawk { +class DropJobNotifier; /** * Small class to parse spotify links into query_ptrs * @@ -38,25 +50,51 @@ class RdioParser : public QObject { Q_OBJECT public: + explicit RdioParser( QObject* parent = 0 ); virtual ~RdioParser(); void parse( const QString& url ); void parse( const QStringList& urls ); + void setCreatePlaylist( bool createPlaylist ) { m_createPlaylist = createPlaylist; } + signals: void track( const Tomahawk::query_ptr& track ); void tracks( const QList< Tomahawk::query_ptr > tracks ); private slots: void expandedLinks( const QStringList& ); + void rdioReturned(); + void playlistCreated( Tomahawk::PlaylistRevision ); private: + void parseTrack( const QString& url ); + void fetchObjectsFromUrl( const QString& url, DropJob::DropType type ); + + QByteArray hmacSha1(QByteArray key, QByteArray baseString); + QNetworkRequest generateRequest( const QString& method, const QString& url, const QList< QPair< QByteArray, QByteArray > >& extraParams, QByteArray* postData ); + QPixmap pixmap() const; + void checkFinished(); void parseUrl( const QString& url ); bool m_multi; int m_count, m_total; QList< query_ptr > m_queries; + QSet< QNetworkReply* > m_reqQueries; + DropJobNotifier* m_browseJob; + + QString m_title, m_creator; + playlist_ptr m_playlist; + + static QPixmap* s_pixmap; + + bool m_createPlaylist; + QList< query_ptr > m_tracks; + +#ifdef QCA2_FOUND + static QCA::Initializer m_qcaInit; +#endif }; } From e941f74f99713694a228388be9591de10aa36967 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Tue, 1 Nov 2011 13:06:31 -0400 Subject: [PATCH 080/109] Add rdio --- data/images/rdio.png | Bin 0 -> 38496 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 data/images/rdio.png diff --git a/data/images/rdio.png b/data/images/rdio.png new file mode 100644 index 0000000000000000000000000000000000000000..6a2e1cf11271ac02042ca5614ce50938c78098ff GIT binary patch literal 38496 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%0AI4 z%sqqY<*a!L!`Exl0eHhFq;HX&V_H^1+d_U>fp#H!hFH+n zI?)T)J`pAqV(Y8}?VQtE2+WTaI6Afe#iC{Ao9-a^0Xfk*{7TE{c}rq%QmHgF*nYz= z6BBB-U6CF*;BpFoyI*-}>U4W7tqZ1c0vXXHc)gb3hurZ)ub)dF3r`i)v}QQho@38r zF=a~9o~N5z3e+cq#|GP{@Ru#sa+;XARmu`EKm|RAbgCQP+f16^nH`oi@3Zq)CzRRMVqSBg{L}jxf?|QJ7wcp~q>9EBEdF({;nB^1Q{Vvc+@{ zwSv=%N&&7RS8S*5<1E(_!({!`GhS{FL;YN{IoJuiR9IT*-8D6}HnPFMn(YYI^Cmf)pfF|`zX018 z*hd!mZl|@_eZv~`6g=37@!ir#s6j8F!IGOn7O7!H;GF2sBDp&QEaingywLC z8)2g1PRJN9A2{@x{u7DKCtUFrC$y@&=qjCw8)3aS?mYPA+2u6DmAvlZ!^bZ1mF&IZ z)QvrI#jZ7aeb$?5($bao@qR_g@rO^5Ofz$f8R;!KLdl5K7u`i>=Va#x0z8rDRzcVx zMTR{uG;Q6tzytYg{F8?F8mMVHzEHuylAixWl+ ze@X31?#;QI<@^If8AR*dX*c3!!Zei%!%%~xp2xOowrrl()ho_T-GiP;H$j=#Q&wT6 zo293*i>NKpy}9|h$47~5n}#Q1w!1hWdP&N%AI%xMvUL`poqgjmp9D|U@S%-pg`i-z zL&Eya9m=0Hy2m-Z&o?)#5On+UYYZBM%GvzSv<8l_3)^H04)Xa^AF*DniHW^fP5u9#z$tWVMy{Sh(R;o!9 zt`zF^2Euo>pE=hRHXe^@&*`0E%dm0Kd!*sWhl-3T=cw$P(6l)D+t7SC+sW&O;3?Te zuPr3voCgw#(I4~&Pxl_jotqCAYD%` zeF?j?Z?+OI9ysv)G@n`U*HAiW_RM=($UdA+tkfU%X?DpCWA{y9612KGtw@<22A!Vf zWcviDu|!BWSYe`4YR(r$`7vkEb@sGe)0h4sRvRJ{^5~@xbqWme^$MC2=cVnlvm5$n zgWJ%S&3#4DktT%HQe}VGM-M}t{e6}*&@~FRgqzk)z!HCxea3pV)L@k`r97y7XqIp( z4)BSDE<@dMz}pBU`irYqD#uU8A%^Q4O(o@;#`9Cu>b1IgH=~?72Yv-g&g03sB)}uXQ z7Vd~-4nm0AF*cpTEm21bFXZaR;Pt`%eklgv{|jURYmn6bK$o+&j)~x1^GRz(_#f(e zuC-KF>I=5+?@WgXj-qEwz&osMCv{A?$pRejw-lCtukg8l4*5xtc3f{nnOo54@fFG6 z#Bv4oNiRxl_b&ysVXCV?`eG$*XN>-n#X69^=Pb{NwGb{D1x?@bZ^`Bwp*t+Bs!H$M!+|R$uO7j~g8y1|leoV1R*Scjn zyQ3MFeF3%my3Lc{ zUli0k_Ki&WCr?&vQE5+7sp9|v3bAAFU248#cysSwcT2^vlSVS8Y`sa~XsovIEw(`; ztJOyM1FE@Z(DcZPZEANDONN|}P!9{!kAaaNA;>zniX{;X3U`7FI;U&#brsWsTyi(u z2tu6TAG=C)Xp~}R7f{bmr{}&jPd?>--tFV><3B%AY?c!5#9fMdm(Rd3;5&OJXMM=K z7kDMSBvi9zz-l!^e+0bbBu@1#<~(lYiTri;JzkMLIHtd&m0`4*eLni*i2o@|Tc_aF z?(9*wkldJ!?){13Z$b8IgZs`0$ynrm>j>gx^+6% zxFmU+T4Oe1V}9z%_j7%$W5Ay|yf0-n(Zs1R!mAt=Soinik2f>@e#C{3^ZYvK4^KZKrkj~;+6k>NokT@|h)9mkVN|f4{t@@v z!~pepeOsnHxkR14)U`1gkFKV={MO@E+X!S)Zcyor;*@*$c9FGocV`h5{8`qcP1M0h z3`sv1>W8USb;b_(S3E}>(+wu)>mUkTR$J5OySDQ75dlq$nPRTHm06+RYxyTh)kcZc zes>c-rB^pp_Rt|iJaVUy)4lh(R*QJg2gh=>^XAIYbGD5+B%sSs1uNo=Acs1^XoDlQ zi!@>s`IKkLf0Z#Mt-NkqSjgx|kL@VzJ-?BY&tw{s*PY)xlj)tcuO)B@E~C%_k8k4q zgGRyb1h20*r|lw1CAz034OfFgt5XbDe0BB?wv0vUEwe|1GE+ZfSk5SB_#?CnW`AH^ zaX^iE9Nxxl?9D3_b7Hi<>Wf=jOD9}{j0?Bm)tpfSlYO#9rN2L9CEJ=JQs&D0yG(iB z4yR5wuk?7Nx+p@!{WwF;}6RCVn(#j9d%LDcjv@s0wGP48z!4 z%^rUnvzj^Hi8z4euuY(S;9q^vCY2{*$KQ638y04?mUNr-A7<0U*fS*W+9)GvB6+H; zW|gExrf*wh(x=*_gk6nD7Q2ioKlE(7`_0?-K}qK=_@=3hK%qaP>Xw3${btK3vZ?`Tnti=$STK2`?QHURP^m%4gIoIt35OKJmHi#ND5Hb+s}%bW?G;sb zO`N;by~BHL2Mt?WbwfgqMmB=VtddvbK8!`WZSJS7T46_(MVJyJHlv8A+1|mcLRiuh zOM6bC#8{QHpH1J9aCFk4)7FdmABsYeg9u`I@&Mw=>KT1!mQ$9ql}Zhh9(=!$v^|s zi0tSc{!Hh=C$$vLMf&+F#oEgs|YsDp_Z&Hxa~o>jTvfp zar6<7+)+xh4~`XSUwOmpXIyFECsVLK^tfh2v)rW8(XCEEeO-$C>)Qw7AEhmmUzLkj zvPP^cxTlqF4hS`|cL!}VoLh$LU<#V8q@K%r6ze*Du$-wwU&j)D3VXIY_jKDq=w%mW zbpq#zL=q_x%A(<^&2-=QZs#)l&yR(-`>1!%1sCL7f0dS5s|eed#R1QtrOL4TEd6EWy(8{e_z<^NhV}IRTxmQu)avMdyvc2XOMuKW zm9s%E*Io7HDub9GYmstPBCh3#3U+S5uW7I4&IS>qrC}j>Ml%>*LZmD(?>0{ zR3HXxQ{)>kSlkMOvE(DW=4?$E%aQr7a}*e0Q}v#W<*5V7BfXd?8N9p3Bdam3ZD;zn0LG9~|=p-N|1! z?|VfY1r2ESX5}>O<|)|Esk3ersZF zqlK~T%|=lau^>&Q2}o~JW7$##q<5ksB18ziLlgv+s#K{_dXLfq1c-F0kuC%XJwkv0 zA*9DM`@HA-3%={(2Ox7j%rG-|S@&A&35Q?YBDuc2=P3UCs&3B2hx)D7n%-))t*ZuN zE$On2k#D(uf`WT*m;W5{A#TkW)ykQdS6EU~A#>s$#;HyN4^(!ZugpP5hL!V#ExC-^ zvKrUDr3u+p;P!D8r8cKyW|I9_7sMXd)nFCr-q_Lw?e}(di@iPs@>)pPz`B=wSD2tb zs<+5v40Wbgk|cqiesqCL}D>ti^ zNuFkH&r*;4;kF}$mOhIksiq=Q zvPDdDw>uA9*)1da2rTyXW!uB6dW;-HF0u zEa|KAWDl|J7=U|Qm-&a|5(Y<=D{EY?ZYpGeod}Wj+*ZNU43TP8nmL(sKMUx2ekZE$ z*cW~i6QMp@)^<^Su{~*FyyUJ@V75 zoxjlP7%K6!Mv4H^3Af~Evv#wlC&&3c23bh*a5d329h|OWGQz?JoL5iyKWe9#a<=X#9yP= zIdII7Sj!-@8=mazz13YnJ~_PTJ81FlNHgRP*Tc_;LP5Pin|F(TyjSMHMO(R zTEenpZTP0esu8^dQPT^S2Q2%tGpxk5e~2}1n7xu7t@@PRw^5NpQaY2|mqx?773MCHh z?gKBEE6kepTHjF)Z#%$a;3DhvRsS*}jSME)b9!@P2nDU!Q|=+D(Pj!-aYP|j>C%Y} zb>wjP+A3ldU^`bNI+wTwtYPcM&>$T)Fi;YG7h8+3*$J&CIWs;bs4HT(V`q*{` zaiE4+KaZ}yPs9Dk#{=(3_Tz4Avxw~W2S(ZgXm`FN;@Y8vUDD-_c}E9l9dH%S zI4mjBWbf?W6>jdlRJ(Hueo>G0IcGi=;J^9DO2-S6$}`ZdJK>=ar12bUSWoU$z>Rji zls<%v4QgzyXL`BPyMg!pxfp`MA0d`w{aDUh+t>g6wKTCatLXD3Z7pKHVg`r;|Gb$! z^#nC1kL+HpW^3QncA8spUX+nBeUcrs41jU}9A35AK!J6zINJl-JcFp%qb##6WEf;E z6f(e*GW^VTM5+#4*fz0w$#5}+o3`YT!mhw8e5C-YD<3c40{{%>6a%`Qw|BK!OjCs zig(L-6E8ue;w%O~XXtg)G4xKW7uJ>x`|E&P0@laFmE8Sg{}Z&rslFbk)6LNO{ZnxTm}xVipxN&%WFCfML8fSF=7^bQT_7 zxfMJikWk~^lhZp|6bZW(TXH$rd)RyT8C0ReeY#Zfl0vL?3Y?gTh^b>1rk1?!UjFJ6 zb+{Z?{6*OP+0i)BIaF-MZny1BF)jdVI#Z)t#QM{4AVD4NrO7jy-eOz!4Qh}2=dUx}{jdI^CAj~VatjG5F8OB?3XXJ^4OfK@AbJi; zKG`;A1YbdVJDBKqARMvyvI2b04+jrnkK!D|XHR!-CCM19C0+{lvmfXj`X-jl^&N3f zrD9_Ie7v!n5bC#on?6c%L;@Q=(6oRE4)uy$q{w-k7xiosGNV6qzAMQJcpM$Jw5zc* zfG-I3t)iIFR4H?~pw({MjVb|vaxxWRPKtuTvRS`?3gGHx=(hM?1Z<+er!LYEbg3=G zP!}jYhyD@x`nr7E~^LO)!` zdM5~>o7GrQ+ttAFO=mwbmcPH$@2rLn#)eX!E*a=$-GpkS@38E~8%m@eba}}+TLjey zk6{r3N3^)Eq8Ea~MMJkikGTGRQ;IZY+p+@xnW;FLg(_^Ee2yh~sRq&Eb@09W0z=_Y ztU9i(k6qb_2TSdGcHedG%{dINBJNM{Bk-W&5`N>DuNgvz!=D>F z;4S53bR=VQBkJ3-+VwW#rPAqUy&dj3f%+L&MkGH{l3=YjD5rcT~cfsXnZdIyTf(N-+KS-6Zz*;RJb(0(8>riT;?(d__+x9Otvc3fG$E-N_ zP9~IZ9edc_moxAA#U=9l{j1dm0=t##2ApuR)~mfwc6-5R{Vd{IOwSA)=LQQD?Ev>XpM{} z{+%;LXBP~Sxdr$QfYeD*I+&9J#55pXZ*~=CSL(TPT*0&>&iWEP2#TRkhEm_IUPy!> zqu~2QZ-8PQhY=be6LyRlnkh`i+eKy@6SuGd+R^yKftdkT>!%Z0im4%tzFRdYaGT)j zyOk6nt!kep<@pnvY2&p8;I|gJ88>Gq>K8UqgeunM?2G<}mD-C_AXrV7!NPB%S3tcj z)G_n$jPZ(mV%yy>RB*RLGP;_Y(4$$K|K){)OTOy-%+V@u7k7?g(xl@h33zJ%c zNtw%H1wnSA!3cHLsy73BA;Z(f*9GNHb6Pay8p*!uwLOpK+7oMitKaf_;_Dg|BJyByu%b;7*;vMp~38^tXF zJ&&tGW%@?0aFMk#o)2C8z-#6Jj4+$))quQy{`qX)tuOKYwpD*sHb}~J2F^+8kBKrk zr8?Wrf@42p#}kEXS?WQAn#J860``~9WJ-RAx0xlpLX1Sfp=%=Wq6YvcL-?IiEaxlb z#9+Ln_S(Gfu->9kWS|v zR(4qk+@|1e8hoLo@^MP5;L=(in|$b3qS@vlCBC;l<&2%M*Aq#|BDc`W;x9Vu3igy# zg*3B;84J4aquw4ouZq5F*`A|+t&r>?*m37~-@V>;j9BU!*yo6Pqj3kU$zp;xJt~py zUiQxPyoH6|ddJ{Gq$Vu_yP*4M{>*3ct>OsW@&s8j$0@_Dq-LYm{gzd3s#B{Pdqyo^ zb_AW76MqpareHpBn}6U$ZRpWXT73C<#*7*Qh0aWf48Lf0ZkO z{2Cj}VoT&i8EUk?TTgdq1=tHwH~YW!hjsItQ7oMu#u*fLyNN=^20?!@Y=juTxF2Mk z&1jkelG|^f&`Q>h>K6G2O;}wqW7oihY-ts%(a3o1M{xaAfY+;&&UPoG-;NBuQHrBL zgkazuZTAX#4G^NA!35!Z&Yxz1S)8oQ6_Nd?ObI2}c45Q=W*EE#=Fhr}V?LRMvoZkM z8q0DGyarPX8L^&N;l9z4BHD#)N3XeNhi&aZW`UT-{jQjkc)s5W4vl~#!r0YmHYwqf z2cWYdpZu$a7F+ARWT7aO8BCsZu2*gw53(fYkq-H`*PmB6OwU1PNOB>ufkea2BWD1H zXM1gyR|yt@6bS`96F4k$ZTx?1e^F4iJU}70AsvAIwj*gkfz?4xgr7!xBVIWyu(}*K z3$E{09z*9S1Eawd{Y&bf7NE*$EjaU9a|*7=AyRdH+uVm(pqAf zrCee?-`=v^d|o6A3h^(L{g7RVgDb z@Ty^z!y>r`LT3#yX(Ldl`f>eqT63EyB}lPZfYi)caOYvrk1K)=uNpr6Dq8RKGBUEd zHV>9>S0M+Fk2(?{v<0mOSB?_Qdlk#omC0jv9Y`3_y)r3!(tG&Wk+Mc~1 zm&vUvhWCUyx4jl`ogix=zN)EceMSFPe=QiCg`ZcxFHsR+Sg(C@IWGCCKkM-8S3|vO z*YJZ1T^q9D9A%@0iCl5Ho7nw4^MmpA&v0s7OB*#?Pxk$N#r86D5XAaCB#>`(BAS82XkVW!OrMp&p>Uq2%Zy?uk7!;qP zm7A{JK-fTO{vnEh+G^W1_@Fg#^}2*!zynib1Cn~?mrE)iJ1iXZ7OUN41aJPl6bj+D z?X*0~P`vW#QHdUJg@u1=lCE7<2ZV{3qs-K%5H3od3UM%MH%eVMPYlh-PYYUTXF1f> zqrekoOkOaXtEGmi*)jq0gwCC~12^1HZ{h`Y0^SIaOQ{dasWziWa|UHVcz<{6*@EiZ z_ww7%UPNdWJMXmbk(0KOVTBK>7H7L%nZ~QoL@QEd2G#a@)%F3^DBxR2j`Di4)P%QO zp5$eZ34#pDq{^?k3O*hs>?2mk)VYOKjt+TWa@#!5DdX&mnwMnDU`SPT+MG!*&<|}} zVK_3h3~2^~%;MUge8)q*J|D*>E1AMF_;g}dFONjkO`9&vA^nd&wU|6mDNJp!u%UPX z;00>OPompzo)s$GPQt1BreeKxDKm#meR1eSI}rb!A68`_S3YNTtU1+6;d{6Gpxy}% zxYU-qVBor;%mS)H&Jk=ffp*p&9`~*Uk32(;A#v;9m;e`}GqB@kXJmQA8R}h+ppF1r zT}kT4s<~b6{X5^fm)lQZ9z5kNkZWA#{8W;D|E3e?Q&^2|qPMQ)m zG*n7&GqK;(i1+sn1@Tj-7#bBAjM}h;z3b+C`{jH8wZ}XJ=I>MwNIQjqgqa()CI;qD z?e55QP{OurC^=zbW~`dBzoE2RSIjGz)dh`Lmb-3qT=Gk zQ;k!_D^pVoa>1b4HtfvW zdS~dZTogoMngYy}Zwha{b{*5LkBWQ~tvNeDyr%XZq+j zBX&~?522kcxde8TD+{(}_Tdi)e6@nvy&91h62poAu#5oO0a_K~keyAvy>&k&i=zo> z0FG-%nw6D}qpM0x=HzR#%9t8VipIcHN5^)J8>*p^hGgHXJ4+xY9)pv%JxZRn19T}6 z$THMsy^%&XWU+QByAsFP7ig{|JdIpxHhq}_JeyRBxU9$u56z@FM6r{hEAU-xa#fk< zIo^6ZPt9j5B(rwCzw9064sJW~xkdhxzJF5`Gwkzdc<84oYH#r9gJB^23QfQCBAb{rwqA?RFC8*G+vRIx|g;o3XBT-Ni4gWJVH z7rBBy?J-WR8|4@EUxr@(^}R_~eBAr`dgiW>=OSs~{VVV{V>ivDx(dVrBB|LEdD>Ua zH_J%v=yu!v!u5u)ThLh}`5cp4y`oV2mR=Mj6c)VK=TrX>&h<$mC*umZ?b%9R+Ga6w z`|#J{$>BPc)Rt6_PXo6QB%|G5;w1oHz4!SAHUCe@M9dUU!n+0~R$+N)yHnK!wytZ}flH19kwK}kj) zdNcV8oMVUgGG17hoQf~>jTlR;Z6YxyBDc4rjz#?x*}I3~eNYaDfa`60+GVWApQnb_ zFpupU*$344o87Zxy*1fyj7=KNs@6aE>#K3KhlAHdg=}Qy!b2B8?^fOHx~cOv|I`&> z$-%9*2N$mxUw&1b)#NEns^A}5$;*XidTUqMJKm=;Kzn)@A=Z6v^}ZEy9)8Ad!6PpQ z6=#DpYmg%~nAeS9s@NZn9b&0VWEz`KXSDoFTVvZ;gvTD`(S%*%E5h}CE8dq+-qO+! zf`yH5up8s{v9I-{tw?qtG!G%fS~dy?Hoyo`g%skK2sZ zJeyAFKp&A>w!z%!rT2j7#x@Bm`!=yU;>2Ep;m1`-zJ3q;R^1Hw%4*b=+26w4CO}1) zb?aaC*0`Kh1?&;Zr_hqbynC3mRDDsZx-J>SA0JP#mi9>&HlgIrdpDXK;Kq=yzXl`t z`&Zm9CzgM$nA}y-|ARB507}p~3RZ{5C!NuLS+^W90z+Fam+URGYnxCYPv88OQffIybeGNTg4S9JE%5}WxbhC>U;v1*?#wX;0Cb88 zMn}0cUprp_f|p3t!U^**yR?KU^~k`8wx(%1k!m;h(>CZ^>6y5WeY3 z{!Js+vCxBCdQgW%4miikzor?6}G@mp?DMv=?^i8ed^L~)FV9?ZNy99_wK zh`Xs~%vf}*^fFVYprn*;4y;^7M@C`@&H zXO{gIxK4E#qy=c`LGg$s9)8c%&b|5-?-FlqLdCsbRB)eLO?5Td=DFB?_nI!R!yW6< zU#poD#;wh=CojaaZT1=6VJ#bH%PzPz-1r8FZ>z`4E{<59{!7ZpL~qx^%yv89glAqTQ~c(eG%%=hp_{l8uAAF6FsiG(ZabhHwond38OSwjoD3NC z@l7$sPsbsOY{^*Qx#b?YmPRS4Gt;Ahrv@>wbDa2FVM}3$gvo#tCyUVjmchGG|LpzcEs43F^YpjII#qKlKGtqA$>CdS zj)%%*Mo!wMms~E2+1=Bj_$AeitY9TmzT$g3)ObFMo?xP-?pCv1Glg?QkYe1Q2({hg zyWS-IMcOJ$>Z<6oYsYM5-dGD>yBFmO{vF5r#>cnhe#V1GZ=UF-wkAH!FI*KkoMY8C zfRe5!7ToBNMyQlrP0cIL9rwb9YuRJX#1N5=Uu84uhukGUW?9Bxu*BCPZ{lIXg9FUDuO0TgK1CHZ?iY-;BHQ=)x>ajP?nKR#A4-i z@B(n3mr`=Z8Y!@-Tmcv1CJ}7TqXFn4HmdC~n`c7EOos)@x|&l0+CvkFnqN&AA@&Wl z)tNwvvRPS0Hb7?OBxua6o{KUCi!wd{G0*PZGc7SyudH_ znD1g|*HZdlfxrZ*zCr07YzD0bQnZ_cLxJ)=tjw}OM4do$hZp&W$ffHkC!^mW9~V{P zV}KzUQ*)v|Xk1HX!LzMe9wN};7Os|Ns8yTOq*8f2M0Dob1vn<9NDa&&HD;kc(dwmJ{GS32$`LkLG!vVf_4bOaDj^$^;p z=JY#3DVp=&=Kh@yvyIk{tXdGc@nR}GU{buN?oV9EQ$q;YSv2b5`~Dn-i;)fj1z0OxX30?`W}4?wLsT81S5hMy33_k zDP@y>ia$1!BQasTNR?`$WKd>i#}O7W{AqEHNCHL+Lxf7-A@_ncn8i&X|7HQbLQ)AW z{Y+)S>mMimlt5opW=7~$QSS!BnP{6kbM?n4xw!hfNiEC97HJuWAnA*FbJN{IP z3giz5Coo7*wtLBf{V0<7)^D%8J0DEJfV&CCE`KQFK=q_&&CP5+I9Hk)Y3*`bWXX|QezkpxZyA=R0D zuHE1hhq>r~LX1|hJ%2d(8X=)K#@aOg>M}h0Y53|Da>|7(*}VmDZ^l|QFg_Qu9n>{Q z{^_Z1123fpFPHlF@s~)eN&$j0Q;(V6MmhTjS_AijeA7$eIdeyZJ*Hc~J(zo)EM=Q_ zNzU9ePr%63CTx!344ESM(~73ZX6LEhJy-F`+HTbgAu+vCHzvg=Rg+xE-DaVI8&|%Y z|A?EL?31nbDwKhGdGIM>%il;44_)7c*vc+Pbi|!Frl(M%cb+nCoB!YnK`nJ7ojtM8 zu31loziUnqfUgi-6;<6-wnl+#qIjBAd1B)s3p=f?ix-o^)?x-|?S{8{{8O^Cf6?m| zH6&HZrpoDyH#|tPqI?Y=p0)YJDEe$bu(s#L%|1DG+xqtHn#OJBVl5q6bwcF!-L%Oa z&i&s}`Gg1Js>4Va?>7Aq4D5hdF(f%KIMLSO)q$B9)~&(pvROBLtvQUFGJmC~)uAu8Rr`;xh!`58#O=r=H&6^dM$Lo zqgv}xT%Ta>Q+7XQDva7?ZkG>^^s!#jxATdp@fqDFRxU7egeIZuA@(n9>s^?7RNv>W z$!A0@I-pboZID|*j>Na@2P16?mA9@26~_$(eUMXO^%=slBkt{;mcs}|V5J^?cNNjm zH%-}H)^OO}k_lR@P+!Xd1udoUe=|>4XvxwsI`64FU0VMJI&Df>Tns~s@4;IER0y_I zj6SI=ch}v^W2MSO-6d5)au4^eK!+1-Xz<9@(UAa`*vxou9jRvgLtOgd!!=cyg!?>K zPaDXl=GJq^c%M>iQdj`~rjd+02I8+X7lw@@(zRq)g@r~oy8`O38ljvzdad5Lj}vE+ zu$+$ea8`e5gx%g~>6gN(>+{xjG6tXXNNIz@)OFk2+P-P~@d4>2MpgM5WzS1#&PP+| ze6;BA%`$VTUgQ~CZjdMbVS&9L}YSJBEnuSj`E&@#?~@U@AIa@8vXlcu|rjmWDr z)?Qj@=bfU`GMPT#RAZZbAos_F6TYT~bU!>5?ChAx&HL<;8JMUj#=!Fzmr1qXV7Vb5 zP}sM`Ps`VbM;t9|QabP&cItS-NLzLKsj|`+NbsMkkUZOQ1AF3`l5C+BC zR;26HM{SjDliN!6ooPo{jO7M20oRn$CGv(IKmp#VxP-#LlJ(+bh7Q${{~kYX=kY=Q zsN}G?N?PB;J)rkB=g_sra1FDw?i#ER^Y3od!DI)iN|Onb#;VlTCM48E)pW)k{t3_T zVrgpqt$8qj76@yLcQ~ZLX}AZKlS~^{A#AC&{ME0u^njpLa>t0_9z>Fnm(DRx}WffCN@W zhC<7%gD}0#^?go=Ggm)Flvd`o=e9?^AW1o}*Wt=jvTk0B`DVk-0gMFHP`j&rTl;il zjj6qNptGeVD)Wf)s3m#v;0q5Jdv&l7^a)l%m{zh>3B*XMPjOJY06i%jk* z8d@gWbmX|~Xw5RQEzItGUtt+guN5L5VS6E1$8*rap{W(!;sHU)h?@O4dCpd2$v>Iz zuSYnk0c(yx5jl=N8C*>I)64}gH%Pmqi|ypTxVe^Ce(HKdK)mPH^b9&OX1iZwu7!+I zbvhu@&f|vwtqdSYU-tgnFGgbvKvmBh8dhPAQSFziEjFOWCElA+X8ZHtdISZu4{(xt zE%8~f&I=wuS+@kRrsc(GA)cn zhCMhONFk)H1+&r0H0A!CkhGHpxUG%zK)5zjh2^+Jw!+`u_ug0%$tU%3E^52aTx2<5 z*WXf~5~q(qc^;q@$Wa*uN8 zqYvsb-d_D@1_b>y4*a=O{z@rjBjZY}4ONzJRy^oOD0v)#B}RwOx+e@ho0NI6j@fC+ zsSvVW-Z!=%*FTS zHUV3hxn2q`1f4cb@ls)>nX;NekR)l*3n9pEhl26rBNZW@3*L6e5`Q$Q2i-K3X|DVJ zCR6n5>Afo{MQ_ivw3zp}ri++)n9LH(P=)07Z1N|Np#P7~z8dboE)Q2%b``scPYn{m zGUAaLJATYR97yPnuTRQV0{cuOwDnm`_hmgF1tr^^u%074g8y}#sh{2hH{;*yAN(Qj z`T?sbZ#vN%+#PwOS+*MID_-q4dAMmCEG|q7 z?~QZ&tn)T*wR6<Qjnf-0~$J?Xn z?0oe8;TzwA)q;XQ9PC;X|EFvDQjjAUhB?-B{H$t4*>f7PYzSQhY9La@eq$MeOl8jU zR_bf4P?R|J$035G+|NVP)HWxi!2nJKd~xxD$1?lwW)5tNpa?RNIfyy5q*((c*~sq; zL;I9&dRkHF)v~SxFQwx?=DRA0iy?&@)*R#)6Lv`0BWHsRbdzr4r*gSVqdv?oSVZ~M`8B2g&j!!mT41+b zX^R;AeCPKiWA9qx2v9b2ydrbJP_J~`e;*}a=(JZTPBf?F??@QNL8p+!?szP446kDf zI}7^3ZunnJGm{03%QVOwYyif%$~PV{xJhAu$}zJ^i=1l?HO^Zt=sEV0df+jLQ18K# zk+kS|hvKY41IsJgyuP>&<;j%$yBc4nI(E>Gph<-LX#O|^Lnq;UwTm|sUxi>aD0prB z{*nbg#fzCmi&xY?nhX_x{8AkR8IdD=xVhE>XLCNT8(rUwy?JskBX? zt#yd{zsD>5KmW;pv`DlRf_{SwY-Tt!Cybta(7EJG1WZ<(h5xtiKdwK-j)(Mf!kt*5 z1OU$|5eci=-GMl6v@r+6I%z}rZ$GJ~-{&DVZ!Sr3d5BBi;(P3+m9n70$;nT^NHmi) z2l?h&O&I7XfHH<&tPOITgd;858fF%~+Fz!b9FjZmN7+DrZXT~yNtwN{?A1P~q#^2JH*x}H315NqC|A#}P zcq=sW566U@V%?M^0kJ>O495yQEjP5wBR?bZ0fYc|?^l}A zvoZPyEW6PAfHNWAk8^!RwIE_n=E9-vL`(5i!-X0;`py9<(MwdQPX{o&1O!!Xw5QouN<<0Uk`1}8|yvQ5rpw4t0{Xir&BOqOWL}uIEQ_IMMmiX{>QkP zl)#f%7k!#_)q2QV)owSclecSfW%<&Sab|0WK_g#dddYC4QZg?q4u*`w^^^;ECD`p$wgU!Cj$ z5l5JA?8{-4i$OrU?&84nw6yH!2N9cgsV@4@ZyAdH;mD*-dpK&1DnDF3SL3KMtoVuk zv3*iJ0bzkEEhyO0jWYP1nrLAt$~rc<$5+>eJeD~&oq2*YuIrvneQNhutXvQ}C!*~j zyu}b1gb73_5VccQFL$?#u7F|!F=rq)=R7ro5T%Qo$Z5;ZA+^;+ueInEPNASS@1YO( zrLLz(^Ch?at#vum)h9do?}k0<@Yx%gGN)a5a3wBpri}b;TZ7WACrp)W6Vpt}FbQ2x z_Nh*Kr!O0+?lrfnDp2Dsf%o4gCe}c=azAZKWnxNY+a5>&bzVI! z5|(6EyjPn_q0}+iew3vkGjld*dbO%Z!hJ6xu2^lsD9CG zJ~dupd8_|+cys(^m+f`F!f#gcS7uULCu?kEX06JkA|xwc*ycF(X*FcM0EvBFE8_mZ z{|>|HaU101_8wF_(Q~iPM8~4uj6OYMOmCriOP;WZVVFQm>69^$vo;y^+&x`g_=@|+ z>c>&d6F3yf&`Jt(H9Zf=Qzf3~%W=$AwkLM;SX~E3NosbeHe);nAiE+ACJl$a- z7zp6p$3c|(pR3HA4?L0@%Ln1f=v6YEkyCP6oV0apJzP~+f1QFpI2KXQ#?hEkTK&ef z-@k}!a6nNvY$mT03k7Nf?t3Z{sHog&wTLk{NpsgJuXHvW44~II9DV>_$h+h0vjgm+0^Zb8j z1qWcU-2KPn;l-?f4^X7UJo@1ljAa$Bq2pB8>SBg~5||@5vSe(Gov}?F4ltL;qMX-c zz8%gv6@*EXl1=^ND@^lmWA$Ys92|BW^-)c%h*_hAf{z>R2v{9p_(}|q7@g>5^QP{s zwxKQms>}#{m8d=3>?ZdQtq6Zg;JTyli8Q#(<>W-Dspd!-Q8t4KcqWx0T*tfI4!lea z=jat+XUi=F{yE?w47-EgdkFtUw9Iw*MqOUbmv*#h8WDMQnD9f*5aJPdIn8&)@!dLD zi60^ByrU8_+Lfuiu&8;vtv&>us2#j7ohd)JXKEOKH2K9wX@%Qaie#DZpln%|VVUP# zT49|CaV^d@YSgrga+40paVN{a$vbU(YPnaVk;mP_D$nnws==I3nT=qhGFNZ$%GN|$ zS*(R&tA7PJbD~I@F88g`SnI{-I~QFaJmL`I=qIqlZ4WjX%YLt}ZV4HE{c$aZ`jc}A z+`zMqTS6!4@=pfH^Zw4vsmY(a1dz$!la$VWS zy?vPYbJHQlVU`*{uKRZvi3K;D`u7EY(y*Tg#Z;~LQoanw2+O|{XW;Z!u}uF=&msQ9 z#wGKLgpyOg@+#XF^TvFveeKjG|8U3=x*DZ@srxBqq!hjo)!b!IE5yf9jlMI;o6D7^ z8Qgt|UVB6NU2&2bvXVvtTcf69-7YiC^?lp#aK4Z*4_oKG;Dk0+X#;C?Lx73t_`Fi) zK)m6O1()b;En)tvm#zdnKY4-CWZgS!>Tas4)JBjQu4oL{3)z+vr>mM)&p$&D^$bnr zwu~tHYy59dZ-ht<-rlYkiB1iITzJ#-r~|cFB35##$)mZk+PNB|0XuHk96OnQQ<8{n zeiE%z{DtFiOV7_t=dZ|<{u+*p@sYaX;ZdMz$~0SRPGRPFEUh<=1crI9VJ^>xq^9QwULe6TTdl-0_jxGuTAI7a+{eg>_r8PyjAR-; ziicqikn!2TIFNIOz6h&pq|Pjma9J2UQ~_B2lG7@~LQTi?dRB@{aFj@L*m!6@B_22# z^e7uPMKqQ|&zX(O&K?1qjGSL#B(VPhn@A2`yS{fT zyZ{BU#2nPzUaMZQ$1M2%33POU;Wxr57O*Z{!EpOFPZ9PRhPpTIxH6o4+RjFuydT3Gc&qVut zX=)nyN!lI{??zgp55I(xPZqQ1>5Cz*SO)@o?0wQzUI6sz^UK}5G-q~Xd-Pvh?TXzD zE6rXZCYX7xM7m5H)V@5tc|DDNK2pi;*TAiTfcL(&PWP-D^mb1>{xifIKzLsiw+Z@m zo6?`xuT=)VwQOXUm@8k!Z&8zNlWN~2kyl%CRd>+8ENdcX+@j2)R@!J(^(&jD5yj{d zKL5C`GQIxn^$LCEz6s9&AC9xL4jmm&LsZUp^6Gtl{1*q$1cLhLuR)HR9G`jq%>EznADVUm literal 0 HcmV?d00001 From 372eda8f2f3822b9d3e9eddd7f591472d1dfcf20 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Tue, 1 Nov 2011 13:53:17 -0400 Subject: [PATCH 081/109] fix rdio icon --- data/images/rdio.png | Bin 38496 -> 43811 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/data/images/rdio.png b/data/images/rdio.png index 6a2e1cf11271ac02042ca5614ce50938c78098ff..1cb29a3091029b59061fb7e1dd3375ceff7cf524 100644 GIT binary patch literal 43811 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
    &?Lu?^9&bx|?3I090}Nba{F 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 literal 38496 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%0AI4 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 From 855180f9053984357054603760aa73bb48f2bf80 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Tue, 1 Nov 2011 19:00:45 -0400 Subject: [PATCH 082/109] Fix animations in breadcrumb and remove some dead code --- src/libtomahawk/widgets/Breadcrumb.cpp | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/libtomahawk/widgets/Breadcrumb.cpp b/src/libtomahawk/widgets/Breadcrumb.cpp index e93d3b7ab..59dc6cde0 100644 --- a/src/libtomahawk/widgets/Breadcrumb.cpp +++ b/src/libtomahawk/widgets/Breadcrumb.cpp @@ -121,10 +121,10 @@ Breadcrumb::updateButtons( const QModelIndex& updateFrom ) // Animate all buttons except the first if ( m_buttons.count() > 0 ) { - QPropertyAnimation* animation = new QPropertyAnimation( btn, "x" ); + QPropertyAnimation* animation = new QPropertyAnimation( btn, "pos" ); animation->setDuration( 300 ); - animation->setStartValue( m_buttons.last()->pos().x() ); - animation->setEndValue( btn->pos().x() ); + animation->setStartValue( m_buttons.last()->pos()); + animation->setEndValue( btn->pos() ); animation->start( QAbstractAnimation::DeleteWhenStopped ); } @@ -165,11 +165,3 @@ Breadcrumb::breadcrumbComboChanged( const QModelIndex& childSelected ) tDebug() << "Combo changed:" << childSelected.data(); updateButtons( childSelected ); } - - -// void -// Breadcrumb::currentIndexChanged( const QModelIndex& current, const QModelIndex& previous ) -// { -// -// } -// From 11779e20372ab715d8e7baea793318df304c8d24 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Tue, 1 Nov 2011 19:08:52 -0400 Subject: [PATCH 083/109] Load resolver cache after we know about which resolvers we have --- src/libtomahawk/AtticaManager.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/AtticaManager.cpp b/src/libtomahawk/AtticaManager.cpp index 55b5e11b3..5303fcd17 100644 --- a/src/libtomahawk/AtticaManager.cpp +++ b/src/libtomahawk/AtticaManager.cpp @@ -45,7 +45,6 @@ AtticaManager::AtticaManager( QObject* parent ) // resolvers m_manager.addProviderFile( QUrl( "http://bakery.tomahawk-player.org:10480/resolvers/providers.xml" ) ); - QTimer::singleShot( 0, this, SLOT( loadPixmapsFromCache() ) ); } @@ -62,6 +61,8 @@ AtticaManager::loadPixmapsFromCache() if ( !cacheDir.cd( "atticacache" ) ) // doesn't exist, no cache return; + qDebug() << "Loading resolvers from cache dir:" << cacheDir.absolutePath(); + qDebug() << "Currently we know about these resolvers:" << m_resolverStates.keys(); foreach ( const QString& file, cacheDir.entryList( QStringList() << "*.png", QDir::Files | QDir::NoSymLinks ) ) { // load all the pixmaps @@ -204,6 +205,8 @@ AtticaManager::resolversList( BaseJob* j ) m_resolverStates = TomahawkSettings::instance()->atticaResolverStates(); // load icon cache from disk, and fetch any we are missing + loadPixmapsFromCache(); + foreach ( Content resolver, m_resolvers ) { if ( !m_resolverStates.contains( resolver.id() ) ) From d442c2158a97b7c2f3af8044e8e2005c605486e4 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Tue, 1 Nov 2011 20:00:52 -0400 Subject: [PATCH 084/109] Show a loading spinner in album views so sourceinfopages etc don't look white for a few seconds --- src/libtomahawk/playlist/albummodel.cpp | 6 ++++++ src/libtomahawk/playlist/albummodel.h | 2 ++ src/libtomahawk/playlist/albumview.cpp | 6 +++++- src/libtomahawk/playlist/albumview.h | 3 ++- .../playlist/dynamic/widgets/LoadingSpinner.cpp | 8 +++++++- src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.h | 2 +- 6 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/libtomahawk/playlist/albummodel.cpp b/src/libtomahawk/playlist/albummodel.cpp index 7c629777d..5662d5036 100644 --- a/src/libtomahawk/playlist/albummodel.cpp +++ b/src/libtomahawk/playlist/albummodel.cpp @@ -254,6 +254,8 @@ AlbumModel::addCollection( const collection_ptr& collection, bool overwrite ) Database::instance()->enqueue( QSharedPointer( cmd ) ); m_title = tr( "All albums from %1" ).arg( collection->source()->friendlyName() ); + + emit loadingStarted(); } @@ -280,6 +282,8 @@ AlbumModel::addFilteredCollection( const collection_ptr& collection, unsigned in m_title = tr( "All albums from %1" ).arg( collection->source()->friendlyName() ); else m_title = tr( "All albums" ); + + emit loadingStarted(); } @@ -292,6 +296,8 @@ AlbumModel::addAlbums( const QList& albums ) if ( m_overwriteOnAdd ) clear(); + emit loadingFinished(); + int c = rowCount( QModelIndex() ); QPair< int, int > crows; crows.first = c; diff --git a/src/libtomahawk/playlist/albummodel.h b/src/libtomahawk/playlist/albummodel.h index 596bf5f42..6a73124ac 100644 --- a/src/libtomahawk/playlist/albummodel.h +++ b/src/libtomahawk/playlist/albummodel.h @@ -94,6 +94,8 @@ signals: void trackCountChanged( unsigned int tracks ); + void loadingStarted(); + void loadingFinished(); private slots: void onDataChanged(); diff --git a/src/libtomahawk/playlist/albumview.cpp b/src/libtomahawk/playlist/albumview.cpp index a077ffa4a..4718bdf41 100644 --- a/src/libtomahawk/playlist/albumview.cpp +++ b/src/libtomahawk/playlist/albumview.cpp @@ -32,6 +32,7 @@ #include "albummodel.h" #include "viewmanager.h" #include "utils/logger.h" +#include "dynamic/widgets/LoadingSpinner.h" #define SCROLL_TIMEOUT 280 @@ -42,7 +43,7 @@ AlbumView::AlbumView( QWidget* parent ) : QListView( parent ) , m_model( 0 ) , m_proxyModel( 0 ) -// , m_delegate( 0 ) + , m_loadingSpinner( new LoadingSpinner( this ) ) { setDragEnabled( true ); setDropIndicatorShown( false ); @@ -109,6 +110,9 @@ AlbumView::setAlbumModel( AlbumModel* model ) connect( m_proxyModel, SIGNAL( filterChanged( QString ) ), SLOT( onFilterChanged( QString ) ) ); connect( m_proxyModel, SIGNAL( rowsInserted( QModelIndex, int, int ) ), SLOT( onViewChanged() ) ); + connect( m_model, SIGNAL( loadingStarted() ), m_loadingSpinner, SLOT( fadeIn() ) ); + connect( m_model, SIGNAL( loadingFinished() ), m_loadingSpinner, SLOT( fadeOut() ) ); + setAcceptDrops( false ); onViewChanged(); // Fetch covers if albums were added to model before model was attached to view } diff --git a/src/libtomahawk/playlist/albumview.h b/src/libtomahawk/playlist/albumview.h index 68736c55e..d590bc050 100644 --- a/src/libtomahawk/playlist/albumview.h +++ b/src/libtomahawk/playlist/albumview.h @@ -28,6 +28,7 @@ #include "albumproxymodel.h" class AlbumModel; +class LoadingSpinner; class DLLEXPORT AlbumView : public QListView, public Tomahawk::ViewPage { @@ -74,7 +75,7 @@ private slots: private: AlbumModel* m_model; AlbumProxyModel* m_proxyModel; -// PlaylistItemDelegate* m_delegate; + LoadingSpinner* m_loadingSpinner; QTimer m_timer; }; diff --git a/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.cpp b/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.cpp index d7f12f7f3..83b739954 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.cpp @@ -1,6 +1,6 @@ /* === This file is part of Tomahawk Player - === * - * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010 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 @@ -58,6 +58,9 @@ LoadingSpinner::~LoadingSpinner() void LoadingSpinner::fadeIn() { + if ( isVisible() ) + return; + show(); m_anim->start(); @@ -71,6 +74,9 @@ LoadingSpinner::fadeIn() void LoadingSpinner::fadeOut() { + if ( !isVisible() ) + return; + m_showHide->setDirection( QTimeLine::Backward ); if ( m_showHide->state() != QTimeLine::Running ) diff --git a/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.h b/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.h index 5720df34c..c2602cfa2 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.h +++ b/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.h @@ -1,6 +1,6 @@ /* === This file is part of Tomahawk Player - === * - * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010 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 From 5c700394a862b49b2ec961c700189da5edd5a970 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Tue, 1 Nov 2011 22:27:14 -0400 Subject: [PATCH 085/109] Try harder to make artist name hover disappear, not perfect yet --- src/libtomahawk/playlist/albumitemdelegate.cpp | 17 ++++++++++++++--- src/libtomahawk/playlist/albumitemdelegate.h | 2 ++ src/libtomahawk/playlist/albumview.cpp | 7 ++++--- src/libtomahawk/playlist/albumview.h | 2 ++ .../playlist/dynamic/widgets/LoadingSpinner.h | 2 +- 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/libtomahawk/playlist/albumitemdelegate.cpp b/src/libtomahawk/playlist/albumitemdelegate.cpp index ee461f225..377c0eddd 100644 --- a/src/libtomahawk/playlist/albumitemdelegate.cpp +++ b/src/libtomahawk/playlist/albumitemdelegate.cpp @@ -157,6 +157,7 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, // If the user is hovering over an artist rect, draw a background so she knows it's clickable QRect r = textRect; r.setTop( r.bottom() - painter->fontMetrics().height() ); + r.adjust( 4, 0, -4, -1 ); if ( m_hoveringOver == index ) TomahawkUtils::drawQueryBackground( painter, opt.palette, r, 1.5 ); @@ -183,7 +184,8 @@ AlbumItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const if ( event->type() != QEvent::MouseButtonRelease && event->type() != QEvent::MouseMove && - event->type() != QEvent::MouseButtonPress ) + event->type() != QEvent::MouseButtonPress && + event->type() != QEvent::Leave ) return false; if ( m_artistNameRects.contains( index ) ) @@ -202,6 +204,7 @@ AlbumItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const emit updateIndex( index ); } + event->accept(); return true; } else if ( event->type() == QEvent::MouseButtonRelease ) @@ -212,21 +215,29 @@ AlbumItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const ViewManager::instance()->show( item->album()->artist() ); + event->accept(); return true; } else if ( event->type() == QEvent::MouseButtonPress ) { // Stop the whole album from having a down click action as we just want the artist name to be clicked + event->accept(); return true; } } } + whitespaceMouseEvent(); + + return false; +} + +void +AlbumItemDelegate::whitespaceMouseEvent() +{ if ( m_hoveringOver.isValid() ) { QModelIndex old = m_hoveringOver; m_hoveringOver = QPersistentModelIndex(); emit updateIndex( old ); } - - return false; } diff --git a/src/libtomahawk/playlist/albumitemdelegate.h b/src/libtomahawk/playlist/albumitemdelegate.h index 37a7226da..86b31bdf5 100644 --- a/src/libtomahawk/playlist/albumitemdelegate.h +++ b/src/libtomahawk/playlist/albumitemdelegate.h @@ -33,6 +33,8 @@ Q_OBJECT public: AlbumItemDelegate( QAbstractItemView* parent = 0, AlbumProxyModel* proxy = 0 ); + void whitespaceMouseEvent(); + protected: void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const; diff --git a/src/libtomahawk/playlist/albumview.cpp b/src/libtomahawk/playlist/albumview.cpp index 4718bdf41..d0a117a95 100644 --- a/src/libtomahawk/playlist/albumview.cpp +++ b/src/libtomahawk/playlist/albumview.cpp @@ -43,6 +43,7 @@ AlbumView::AlbumView( QWidget* parent ) : QListView( parent ) , m_model( 0 ) , m_proxyModel( 0 ) + , m_delegate( 0 ) , m_loadingSpinner( new LoadingSpinner( this ) ) { setDragEnabled( true ); @@ -79,9 +80,9 @@ void AlbumView::setProxyModel( AlbumProxyModel* model ) { m_proxyModel = model; - AlbumItemDelegate* del = new AlbumItemDelegate( this, m_proxyModel ); - connect( del, SIGNAL( updateIndex( QModelIndex ) ), this, SLOT( update( QModelIndex ) ) ); - setItemDelegate( del ); + m_delegate = new AlbumItemDelegate( this, m_proxyModel ); + connect( m_delegate, SIGNAL( updateIndex( QModelIndex ) ), this, SLOT( update( QModelIndex ) ) ); + setItemDelegate( m_delegate ); QListView::setModel( m_proxyModel ); } diff --git a/src/libtomahawk/playlist/albumview.h b/src/libtomahawk/playlist/albumview.h index d590bc050..b16f9e898 100644 --- a/src/libtomahawk/playlist/albumview.h +++ b/src/libtomahawk/playlist/albumview.h @@ -29,6 +29,7 @@ class AlbumModel; class LoadingSpinner; +class AlbumItemDelegate; class DLLEXPORT AlbumView : public QListView, public Tomahawk::ViewPage { @@ -75,6 +76,7 @@ private slots: private: AlbumModel* m_model; AlbumProxyModel* m_proxyModel; + AlbumItemDelegate* m_delegate; LoadingSpinner* m_loadingSpinner; QTimer m_timer; diff --git a/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.h b/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.h index c2602cfa2..556dcd11c 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.h +++ b/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.h @@ -54,4 +54,4 @@ private: #endif -class QPaintEvent; \ No newline at end of file +class QPaintEvent; From df31cbbe77b3b7c583c005e6a5c9612eb5263675 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 2 Nov 2011 11:43:15 +0100 Subject: [PATCH 086/109] * Should fix mingw compilation. --- src/libtomahawk/utils/rdioparser.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libtomahawk/utils/rdioparser.cpp b/src/libtomahawk/utils/rdioparser.cpp index 028197c65..a5e78ba77 100644 --- a/src/libtomahawk/utils/rdioparser.cpp +++ b/src/libtomahawk/utils/rdioparser.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include From 8ebbb558420912c435e54d4a7d32aa104c74f124 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 2 Nov 2011 11:45:33 +0100 Subject: [PATCH 087/109] * I see why this broke. Fixed now? --- src/libtomahawk/utils/rdioparser.cpp | 1 - src/libtomahawk/utils/rdioparser.h | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/utils/rdioparser.cpp b/src/libtomahawk/utils/rdioparser.cpp index a5e78ba77..028197c65 100644 --- a/src/libtomahawk/utils/rdioparser.cpp +++ b/src/libtomahawk/utils/rdioparser.cpp @@ -34,7 +34,6 @@ #include #include #include -#include #include #include diff --git a/src/libtomahawk/utils/rdioparser.h b/src/libtomahawk/utils/rdioparser.h index 10d45fa61..23b5dfab7 100644 --- a/src/libtomahawk/utils/rdioparser.h +++ b/src/libtomahawk/utils/rdioparser.h @@ -19,6 +19,7 @@ #ifndef RDIOPARSER_H #define RDIOPARSER_H + #include "jobview/JobStatusItem.h" #include "query.h" #include "config.h" @@ -30,9 +31,10 @@ #include #include +#include + #ifdef QCA2_FOUND #include -#include #endif class QNetworkReply; From 673fdf1b674aa01045d6a048bae69f91cc53049f Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Wed, 2 Nov 2011 13:13:23 +0100 Subject: [PATCH 088/109] Automatically init && update submodules when using INTERNAL_* --- CMakeLists.txt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7472b2971..5bd4a8d59 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -127,8 +127,16 @@ ENDIF() #macro_log_feature(LIBLASTFM_FOUND "LastFm" "Qt library for the Last.fm webservices" "https://github.com/mxcl/liblastfm" FALSE "" "liblastfm is needed for scrobbling tracks to Last.fm and fetching cover artwork") set(LIBLASTFM_FOUND true) +#### submodules start # this installs headers and such and should really be handled in a separate package by packagers +IF( INTERNAL_JREEN OR INTERNAL_QTWEETLIB ) + IF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.gitmodules) + EXECUTE_PROCESS(COMMAND git submodule init WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) + EXECUTE_PROCESS(COMMAND git submodule update WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) + ENDIF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.gitmodules) +ENDIF() + IF( INTERNAL_JREEN ) ADD_SUBDIRECTORY( ${THIRDPARTY_DIR}/jreen ) SET( LIBJREEN_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/thirdparty/jreen/include ) @@ -141,7 +149,6 @@ ELSE( INTERNAL_JREEN ) ENDIF( INTERNAL_JREEN ) macro_log_feature(LIBJREEN_FOUND "Jreen" "Qt XMPP Library" "https://github.com/euroelessar/jreen" FALSE "" "Jreen is needed for the Jabber SIP plugin. \n\n Use -DINTERNAL_JREEN=ON to build the git submodule inside Tomahawk \n Be aware this installs a full jreen with headers and everything!\n") -# this installs headers and such and should really be handled in a separate package by packagers IF( INTERNAL_QTWEETLIB ) ADD_SUBDIRECTORY( ${THIRDPARTY_DIR}/qtweetlib ) # copy headers to build/QTweetLib so we can use proper includes inside the code @@ -155,6 +162,7 @@ ELSE( INTERNAL_QTWEETLIB ) macro_optional_find_package(QTweetLib) ENDIF( INTERNAL_QTWEETLIB ) macro_log_feature(QTWEETLIB_FOUND "QTweetLib" "Qt Twitter Library" "https://github.com/minimoog/QTweetLib" FALSE "" "QTweetLib is needed for the Twitter SIP plugin. \n\n Use -DINTERNAL_QTWEETLIB=ON to build the git submodule inside Tomahawk \n") +#### submodules end ### libportfwd SET( LIBPORTFWD_INCLUDE_DIR ${THIRDPARTY_DIR}/libportfwd/include ) From a98f6db08923722b20ebb9bf65294f7d7c4720b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Wed, 2 Nov 2011 14:29:34 +0100 Subject: [PATCH 089/109] Adding Rdio and WeAreHunted to charts --- .../infoplugins/generic/chartsplugin.cpp | 210 +++++++++++------- 1 file changed, 130 insertions(+), 80 deletions(-) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp index 235e26740..2bf0faba5 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp @@ -49,7 +49,7 @@ ChartsPlugin::ChartsPlugin() /// Add resources here - m_chartResources << "billboard" << "itunes"; + m_chartResources << "billboard" << "itunes" << "rdio" << "wearehunted"; m_supportedGetTypes << InfoChart << InfoChartCapabilities; } @@ -275,115 +275,143 @@ ChartsPlugin::chartTypes() QVariantMap charts; QString chartName; QStringList defaultChain; - if ( source == "itunes" ) + if ( source == "wearehunted" || source == "itunes" ) { + // Some charts can have an extra param, itunes has geo, WAH has emerging/mainstream // Itunes has geographic-area based charts. So we build a breadcrumb of // ITunes - Country - Albums - Top Chart Type // - Tracks - Top Chart Type - QHash< QString, QVariantMap > countries; + // WeAreHunted has Mainstream/Emerging + // WeAreHunted - Type - Artists - Chart Type + // - Tracks - Chart Type + QHash< QString, QVariantMap > extraType; foreach( const QVariant& chartObj, chartObjs.values() ) { - const QVariantMap chart = chartObj.toMap(); - const QString id = chart.value( "id" ).toString(); - const QString geo = chart.value( "geo" ).toString(); - QString name = chart.value( "genre" ).toString(); - const QString type = chart.value( "type" ).toString(); - const bool isDefault = ( chart.contains( "default" ) && chart[ "default" ].toInt() == 1 ); + if( !chartObj.toMap().isEmpty() ){ + const QVariantMap chart = chartObj.toMap(); + const QString id = chart.value( "id" ).toString(); + const QString geo = chart.value( "geo" ).toString(); + QString name = chart.value( "genre" ).toString(); + const QString type = chart.value( "type" ).toString(); + const bool isDefault = ( chart.contains( "default" ) && chart[ "default" ].toInt() == 1 ); - QString country; - if ( !m_cachedCountries.contains( geo ) ) - { - QLocale l( QString( "en_%1" ).arg( geo ) ); - country = Tomahawk::CountryUtils::fullCountryFromCode( geo ); + QString extra; + if( !geo.isEmpty() ){ - for ( int i = 1; i < country.size(); i++ ) - { - if ( country.at( i ).isUpper() ) + if ( !m_cachedCountries.contains( geo ) ) { - country.insert( i, " " ); - i++; + QLocale l( QString( "en_%1" ).arg( geo ) ); + extra = Tomahawk::CountryUtils::fullCountryFromCode( geo ); + + for ( int i = 1; i < extra.size(); i++ ) + { + if ( extra.at( i ).isUpper() ) + { + extra.insert( i, " " ); + i++; + } + } + m_cachedCountries[ geo ] = extra; } + else + { + extra = m_cachedCountries[ geo ]; + } + }else extra = chart.value( "extra" ).toString(); + + if ( name.isEmpty() ) // not a specific chart, an all chart + name = tr( "Top Overall" ); + + InfoStringHash c; + c[ "id" ] = id; + c[ "label" ] = name; + c[ "type" ] = "album"; + if ( isDefault ) + c[ "default" ] = "true"; + + QList extraTypeData = extraType[ extra ][ type ].value< QList< InfoStringHash > >(); + extraTypeData.append( c ); + + extraType[ extra ].insert( type, QVariant::fromValue< QList< InfoStringHash > >( extraTypeData ) ); + if ( isDefault ) + { + defaultChain.clear(); + defaultChain.append( extra ); + defaultChain.append( type ); + defaultChain.append( name ); } - m_cachedCountries[ geo ] = country; } - else + foreach( const QString& c, extraType.keys() ) { - country = m_cachedCountries[ geo ]; + charts[ c ] = extraType[ c ]; + qDebug() << "extraType has types:" << c; } - - if ( name.isEmpty() ) // not a specific chart, an all chart - name = tr( "Top Overall" ); - - InfoStringHash c; - c[ "id" ] = id; - c[ "label" ] = name; - c[ "type" ] = "album"; - if ( isDefault ) - c[ "default" ] = "true"; - - QList countryTypeData = countries[ country ][ type ].value< QList< InfoStringHash > >(); - countryTypeData.append( c ); - - countries[ country ].insert( type, QVariant::fromValue< QList< InfoStringHash > >( countryTypeData ) ); - if ( isDefault ) - { - defaultChain.clear(); - defaultChain.append( country ); - defaultChain.append( type ); - defaultChain.append( name ); + if( source == "itunes" ){ + chartName = "iTunes"; + } + if( source == "wearehunted" ){ + chartName = "WeAreHunted"; } } - foreach( const QString& c, countries.keys() ) - { - charts[ c ] = countries[ c ]; -// qDebug() << "Country has types:" << countries[ c ]; - } - chartName = "iTunes"; - } else + }else { // We'll just build: // [Source] - Album - Chart Type // [Source] - Track - Chart Type QList< InfoStringHash > albumCharts; QList< InfoStringHash > trackCharts; + QList< InfoStringHash > artistCharts; + foreach( const QVariant& chartObj, chartObjs.values() ) { - const QVariantMap chart = chartObj.toMap(); - const QString type = chart.value( "type" ).toString(); - const bool isDefault = ( chart.contains( "default" ) && chart[ "default" ].toInt() == 1 ); + if( !chartObj.toMap().isEmpty() ){ + const QVariantMap chart = chartObj.toMap(); + const QString type = chart.value( "type" ).toString(); + const bool isDefault = ( chart.contains( "default" ) && chart[ "default" ].toInt() == 1 ); - InfoStringHash c; - c[ "id" ] = chart.value( "id" ).toString(); - c[ "label" ] = chart.value( "name" ).toString(); - if ( isDefault ) - c[ "default" ] = "true"; + InfoStringHash c; + c[ "id" ] = chart.value( "id" ).toString(); + c[ "label" ] = chart.value( "name" ).toString(); + if ( isDefault ) + c[ "default" ] = "true"; - if ( type == "Album" ) - { - c[ "type" ] = "album"; - albumCharts.append( c ); - } - else if ( type == "Track" ) - { - c[ "type" ] = "tracks"; - trackCharts.append( c ); - } + if ( type == "Album" ) + { + c[ "type" ] = "album"; + albumCharts.append( c ); + } + else if ( type == "Track" ) + { + c[ "type" ] = "tracks"; + trackCharts.append( c ); - if ( isDefault ) - { - defaultChain.clear(); - defaultChain.append( type + "s" ); //UGLY but it's plural to the user, see below - defaultChain.append( c[ "label" ] ); + }else if ( type == "Artist" ) + { + c[ "type" ] = "artists"; + artistCharts.append( c ); + + } + + if ( isDefault ) + { + defaultChain.clear(); + defaultChain.append( type + "s" ); //UGLY but it's plural to the user, see below + defaultChain.append( c[ "label" ] ); + } } + if( !artistCharts.isEmpty() ) + charts.insert( tr( "Artists" ), QVariant::fromValue< QList< InfoStringHash > >( artistCharts ) ); + if( !albumCharts.isEmpty() ) + charts.insert( tr( "Albums" ), QVariant::fromValue< QList< InfoStringHash > >( albumCharts ) ); + if( !trackCharts.isEmpty() ) + charts.insert( tr( "Tracks" ), QVariant::fromValue< QList< InfoStringHash > >( trackCharts ) ); + + /// @note For displaying purposes, upper the first letter + /// @note Remeber to lower it when fetching this! + chartName = source; + chartName[0] = chartName[0].toUpper(); } - charts.insert( tr( "Albums" ), QVariant::fromValue< QList< InfoStringHash > >( albumCharts ) ); - charts.insert( tr( "Tracks" ), QVariant::fromValue< QList< InfoStringHash > >( trackCharts ) ); - - /// @note For displaying purposes, upper the first letter - /// @note Remeber to lower it when fetching this! - chartName = source; - chartName[0] = chartName[0].toUpper(); } /// Add the possible charts and its types to breadcrumb @@ -436,6 +464,7 @@ ChartsPlugin::chartReturned() QVariantList chartResponse = res.value( "list" ).toList(); QList< InfoStringHash > top_tracks; QList< InfoStringHash > top_albums; + QStringList top_artists; /// Deside what type, we need to handle it differently /// @todo: We allready know the type, append it to breadcrumb hash @@ -444,6 +473,8 @@ ChartsPlugin::chartReturned() setChartType( Album ); else if( res.value( "type" ).toString() == "Track" ) setChartType( Track ); + else if( res.value( "type" ).toString() == "Artist" ) + setChartType( Artist ); else setChartType( None ); @@ -499,10 +530,29 @@ ChartsPlugin::chartReturned() top_tracks << pair; } + }else if( chartType() == Artist ) + { + if ( artist.isEmpty() ) // don't have enough... + { + tLog() << "Didn't get an artist from charts, not enough to build a query on. Aborting" << artist; + + } + else + { + top_artists << artist; + } + } } } + if( chartType() == Artist ) + { + tDebug() << "ChartsPlugin:" << "\tgot " << top_artists.size() << " artists"; + returnedData["artists"] = QVariant::fromValue( top_artists ); + returnedData["type"] = "artists"; + } + if( chartType() == Track ) { tDebug() << "ChartsPlugin:" << "\tgot " << top_tracks.size() << " tracks"; From f025edf095aecce9caf0a633333a50c20f5bece1 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Wed, 2 Nov 2011 20:41:57 -0400 Subject: [PATCH 090/109] Possible fix for multiple breadcrumbs. Erase buttons before resettin the model. --- .../infosystem/infoplugins/generic/chartsplugin.cpp | 3 ++- src/libtomahawk/widgets/Breadcrumb.cpp | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp index 2bf0faba5..161364b73 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp @@ -73,7 +73,7 @@ ChartsPlugin::namChangedSlot( QNetworkAccessManager *nam ) m_nam = QWeakPointer< QNetworkAccessManager >( nam ); /// Then get each chart from resource - /// We need to fetch them before they are asked for + /// We want to prepopulate the breadcrumb to fetch them before they are asked for if( !m_chartResources.isEmpty() && m_nam ){ @@ -194,6 +194,7 @@ ChartsPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requ } Tomahawk::InfoSystem::InfoStringHash criteria; + emit getCachedInfo( criteria, 0, requestData ); } diff --git a/src/libtomahawk/widgets/Breadcrumb.cpp b/src/libtomahawk/widgets/Breadcrumb.cpp index 59dc6cde0..aec9a7f49 100644 --- a/src/libtomahawk/widgets/Breadcrumb.cpp +++ b/src/libtomahawk/widgets/Breadcrumb.cpp @@ -56,6 +56,10 @@ Breadcrumb::~Breadcrumb() void Breadcrumb::setModel( QAbstractItemModel* model ) { + foreach ( BreadcrumbButton* b, m_buttons ) + b->deleteLater();; + m_buttons.clear(); + m_model = model; updateButtons( QModelIndex() ); } From b94445b32a56cc286aea50e1928026bd40387688 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Wed, 2 Nov 2011 20:42:16 -0400 Subject: [PATCH 091/109] Remove super spammy debug --- src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp index 161364b73..46d064c7b 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp @@ -345,7 +345,7 @@ ChartsPlugin::chartTypes() foreach( const QString& c, extraType.keys() ) { charts[ c ] = extraType[ c ]; - qDebug() << "extraType has types:" << c; +// qDebug() << "extraType has types:" << c; } if( source == "itunes" ){ chartName = "iTunes"; From cf818f64c658f00ee00792e09d7539692acc3da1 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Wed, 2 Nov 2011 23:16:25 -0400 Subject: [PATCH 092/109] use Show/Hide Footnotes instead of just Footnotes. No other way to distinguish this is actually a button, and be consistent with Show/Hide Queue --- src/libtomahawk/context/ContextWidget.cpp | 4 ++++ src/libtomahawk/context/ContextWidget.ui | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/context/ContextWidget.cpp b/src/libtomahawk/context/ContextWidget.cpp index 747dbac87..f2006e31c 100644 --- a/src/libtomahawk/context/ContextWidget.cpp +++ b/src/libtomahawk/context/ContextWidget.cpp @@ -276,10 +276,14 @@ ContextWidget::onAnimationFinished() m_scene->setSceneRect( ui->contextView->viewport()->rect() ); layoutViews( false ); setQuery( m_query, true ); + + ui->toggleButton->setText( tr( "Hide Footnotes" ) ); } else { setFixedHeight( m_minHeight ); + + ui->toggleButton->setText( tr( "Show Footnotes" ) ); } } diff --git a/src/libtomahawk/context/ContextWidget.ui b/src/libtomahawk/context/ContextWidget.ui index 336fbb413..77a9f2fe8 100644 --- a/src/libtomahawk/context/ContextWidget.ui +++ b/src/libtomahawk/context/ContextWidget.ui @@ -35,7 +35,7 @@ - Footnotes + Show Footnotes Qt::AlignCenter From 124706afab96d69efcbc847675e2287101ca14db Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Wed, 2 Nov 2011 22:46:56 -0400 Subject: [PATCH 093/109] Disable on-hovered stars when mouse moves away, make sure to repaint in that case --- src/GetNewStuffDelegate.cpp | 12 ++++++++---- src/GetNewStuffDelegate.h | 3 +++ src/GetNewStuffDialog.cpp | 4 +++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/GetNewStuffDelegate.cpp b/src/GetNewStuffDelegate.cpp index 8a5e5d25f..1e6466de8 100644 --- a/src/GetNewStuffDelegate.cpp +++ b/src/GetNewStuffDelegate.cpp @@ -259,7 +259,6 @@ bool GetNewStuffDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ) { Q_UNUSED( option ); - m_hoveringOver = -1; if ( event->type() != QEvent::MouseButtonRelease && event->type() != QEvent::MouseMove ) @@ -288,15 +287,12 @@ GetNewStuffDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, cons if ( fullStars.contains( me->pos() ) ) { - tDebug() << "A star was pressed...which one?"; - const int eachStar = starsWidth / 5; const int clickOffset = me->pos().x() - fullStars.x(); const int whichStar = (clickOffset / eachStar) + 1; if ( event->type() == QEvent::MouseButtonRelease ) { - tDebug() << "Clicked on:" << whichStar; model->setData( index, whichStar, GetNewStuffModel::RatingRole ); } else if ( event->type() == QEvent::MouseMove ) @@ -310,5 +306,13 @@ GetNewStuffDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, cons return true; } } + + if ( m_hoveringOver > -1 ) + { + QModelIndex idx = model->index( m_hoveringItem.first, m_hoveringItem.second ); + emit update( idx ); + m_hoveringOver = -1; + m_hoveringItem = QPair(); + } return false; } diff --git a/src/GetNewStuffDelegate.h b/src/GetNewStuffDelegate.h index 853e3c74d..93e32bd9a 100644 --- a/src/GetNewStuffDelegate.h +++ b/src/GetNewStuffDelegate.h @@ -31,6 +31,9 @@ public: virtual void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; virtual QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const; +signals: + void update( const QModelIndex& idx ); + protected: virtual bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ); diff --git a/src/GetNewStuffDialog.cpp b/src/GetNewStuffDialog.cpp index a0102c648..3a5997b26 100644 --- a/src/GetNewStuffDialog.cpp +++ b/src/GetNewStuffDialog.cpp @@ -30,7 +30,9 @@ GetNewStuffDialog::GetNewStuffDialog( QWidget *parent, Qt::WindowFlags f ) ui->setupUi( this ); ui->listView->setModel( m_model ); - ui->listView->setItemDelegate( new GetNewStuffDelegate( ui->listView ) ); + GetNewStuffDelegate* del = new GetNewStuffDelegate( ui->listView ); + connect( del, SIGNAL( update( QModelIndex ) ), ui->listView, SLOT( update( QModelIndex ) ) ); + ui->listView->setItemDelegate( del ); ui->listView->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); ui->listView->setMouseTracking( true ); From 9418fd0fb8d1d726149650f35c28f9ac7d80f1bd Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Wed, 2 Nov 2011 22:48:23 -0400 Subject: [PATCH 094/109] Skip to next track if we have a QNR and it emits a failed signal --- src/libtomahawk/audio/audioengine.cpp | 13 +++++++++++++ src/libtomahawk/audio/audioengine.h | 2 ++ src/libtomahawk/utils/rdioparser.cpp | 1 + 3 files changed, 16 insertions(+) diff --git a/src/libtomahawk/audio/audioengine.cpp b/src/libtomahawk/audio/audioengine.cpp index 2831c74a1..c92be1f37 100644 --- a/src/libtomahawk/audio/audioengine.cpp +++ b/src/libtomahawk/audio/audioengine.cpp @@ -424,6 +424,9 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result ) tLog() << "Starting new song:" << m_currentTrack->url(); emit loading( m_currentTrack ); + if ( QNetworkReply* qnr_io = qobject_cast< QNetworkReply* >( io.data() ) ) + connect( qnr_io, SIGNAL( error( QNetworkReply::NetworkError ) ), this, SLOT( ioStreamError( QNetworkReply::NetworkError ) ) ); + if ( !isHttpResult( m_currentTrack->url() ) && !isLocalResult( m_currentTrack->url() ) ) { if ( QNetworkReply* qnr_io = qobject_cast< QNetworkReply* >( io.data() ) ) @@ -573,6 +576,16 @@ AudioEngine::playItem( Tomahawk::PlaylistInterface* playlist, const Tomahawk::re } } +void +AudioEngine::ioStreamError( QNetworkReply::NetworkError error ) +{ + if ( error != QNetworkReply::NoError ) + { + if ( canGoNext() ) + loadNextTrack(); + } +} + void AudioEngine::playlistNextTrackReady() diff --git a/src/libtomahawk/audio/audioengine.h b/src/libtomahawk/audio/audioengine.h index 038579d9c..f888ce42c 100644 --- a/src/libtomahawk/audio/audioengine.h +++ b/src/libtomahawk/audio/audioengine.h @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -134,6 +135,7 @@ private slots: void setCurrentTrack( const Tomahawk::result_ptr& result ); + void ioStreamError( QNetworkReply::NetworkError ); private: void setState( AudioState state ); diff --git a/src/libtomahawk/utils/rdioparser.cpp b/src/libtomahawk/utils/rdioparser.cpp index 028197c65..c16db62bc 100644 --- a/src/libtomahawk/utils/rdioparser.cpp +++ b/src/libtomahawk/utils/rdioparser.cpp @@ -371,6 +371,7 @@ RdioParser::pixmap() const if ( !s_pixmap ) s_pixmap = new QPixmap( RESPATH "images/rdio.png" ); + return *s_pixmap; } From d6778a55cf94ba73635cb1410cdbcd19fd3f93e9 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Wed, 2 Nov 2011 22:48:48 -0400 Subject: [PATCH 095/109] Only animate when visible --- src/libtomahawk/widgets/Breadcrumb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/widgets/Breadcrumb.cpp b/src/libtomahawk/widgets/Breadcrumb.cpp index aec9a7f49..8409283ff 100644 --- a/src/libtomahawk/widgets/Breadcrumb.cpp +++ b/src/libtomahawk/widgets/Breadcrumb.cpp @@ -123,7 +123,7 @@ Breadcrumb::updateButtons( const QModelIndex& updateFrom ) btn->show(); // Animate all buttons except the first - if ( m_buttons.count() > 0 ) + if ( m_buttons.count() > 0 && isVisible() ) { QPropertyAnimation* animation = new QPropertyAnimation( btn, "pos" ); animation->setDuration( 300 ); From 663752010c6df87b8a9094ba51a702ff9f322da5 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Wed, 2 Nov 2011 23:11:59 -0400 Subject: [PATCH 096/109] Fix some rdio stuff --- src/libtomahawk/dropjob.cpp | 16 ++++++++-------- src/libtomahawk/utils/rdioparser.cpp | 5 ++++- src/libtomahawk/utils/rdioparser.h | 1 - 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/libtomahawk/dropjob.cpp b/src/libtomahawk/dropjob.cpp index 530ad5f74..12432e682 100644 --- a/src/libtomahawk/dropjob.cpp +++ b/src/libtomahawk/dropjob.cpp @@ -112,7 +112,8 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType if ( url.contains( "spotify" ) && url.contains( "track" ) ) return true; - if ( url.contains( "rdio.com" ) && ( url.contains( "track" ) || /*url.contains( "artist" ) ||*/ url.contains( "album" ) || url.contains( "playlists" ) ) ) + if ( url.contains( "rdio.com" ) && ( ( ( url.contains( "track" ) && url.contains( "artist" ) && url.contains( "album" ) ) + || url.contains( "playlists" ) ) ) ) return true; } @@ -122,6 +123,8 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType return true; if ( url.contains( "spotify" ) && url.contains( "album" ) ) return true; + if ( url.contains( "rdio.com" ) && ( url.contains( "artist" ) && url.contains( "album" ) && !url.contains( "track" ) ) ) + return true; } if ( acceptedType.testFlag( Artist ) ) @@ -130,6 +133,8 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType return true; if ( url.contains( "spotify" ) && url.contains( "artist" ) ) return true; + if ( url.contains( "rdio.com" ) && ( url.contains( "artist" ) && !url.contains( "album" ) && !url.contains( "track" ) ) ) + return true; } // We whitelist t.co and bit.ly (and j.mp) since they do some link checking. Often playable (e.g. spotify..) links hide behind them, @@ -463,16 +468,11 @@ DropJob::handleRdioUrls( const QString& urlsRaw ) setDropAction( Create ); RdioParser* rdio = new RdioParser( this ); + connect( rdio, SIGNAL( tracks( QList ) ), this, SLOT( onTracksAdded( QList< Tomahawk::query_ptr > ) ) ); + rdio->setCreatePlaylist( dropAction() == Create ); rdio->parse( urls ); - /// This currently supports draging and dropping a spotify playlist and artist - if ( dropAction() == Append ) - { - tDebug() << Q_FUNC_INFO << "Asking for spotify browse contents from" << urls; - connect( rdio, SIGNAL( tracks( QList ) ), this, SLOT( onTracksAdded( QList< Tomahawk::query_ptr > ) ) ); - } - m_queryCount++; } diff --git a/src/libtomahawk/utils/rdioparser.cpp b/src/libtomahawk/utils/rdioparser.cpp index c16db62bc..7e7d2e28f 100644 --- a/src/libtomahawk/utils/rdioparser.cpp +++ b/src/libtomahawk/utils/rdioparser.cpp @@ -236,7 +236,7 @@ RdioParser::parseTrack( const QString& origUrl ) query_ptr q = Query::get( artist, trk, album, uuid(), !m_createPlaylist ); m_count++; - m_queries << q; + m_tracks << q; checkFinished(); } @@ -334,6 +334,7 @@ RdioParser::checkFinished() m_tracks ); connect( m_playlist.data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), this, SLOT( playlistCreated() ) ); + return; } else @@ -342,6 +343,8 @@ RdioParser::checkFinished() emit track( m_tracks.first() ); else if ( m_multi && m_count == m_total ) emit tracks( m_tracks ); + + m_tracks.clear(); } deleteLater(); diff --git a/src/libtomahawk/utils/rdioparser.h b/src/libtomahawk/utils/rdioparser.h index 23b5dfab7..3302b4715 100644 --- a/src/libtomahawk/utils/rdioparser.h +++ b/src/libtomahawk/utils/rdioparser.h @@ -82,7 +82,6 @@ private: bool m_multi; int m_count, m_total; - QList< query_ptr > m_queries; QSet< QNetworkReply* > m_reqQueries; DropJobNotifier* m_browseJob; From 3523609cad2bd10a756f33c4dd4c07987be1e677 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 3 Nov 2011 16:43:53 -0400 Subject: [PATCH 097/109] Don't always emit a new nam for plugins when it hasn't actually changed --- .../infosystem/infosystemworker.cpp | 20 +++++++++++++------ src/libtomahawk/utils/tomahawkutils.cpp | 9 +++++++++ src/libtomahawk/utils/tomahawkutils.h | 2 ++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/libtomahawk/infosystem/infosystemworker.cpp b/src/libtomahawk/infosystem/infosystemworker.cpp index 324a9bdb5..6252861ec 100644 --- a/src/libtomahawk/infosystem/infosystemworker.cpp +++ b/src/libtomahawk/infosystem/infosystemworker.cpp @@ -361,18 +361,26 @@ InfoSystemWorker::nam() const void InfoSystemWorker::newNam() { -// qDebug() << Q_FUNC_INFO << " begin"; - QNetworkAccessManager *oldNam = TomahawkUtils::nam(); if ( oldNam && oldNam->thread() == thread() ) { -// qDebug() << Q_FUNC_INFO << "Using old nam as it's the same thread (GUI) as me"; - m_nam = QWeakPointer< QNetworkAccessManager >( oldNam ); - emit namChanged( m_nam.data() ); + if ( m_nam.data() != oldNam ) + { + m_nam = QWeakPointer< QNetworkAccessManager >( oldNam ); + emit namChanged( m_nam.data() ); + } return; } -// qDebug() << Q_FUNC_INFO << "No nam exists, or it's a different thread, creating a new one"; + if ( + oldNam && + !m_nam.isNull() && + oldNam->configuration() == m_nam.data()->configuration() && + oldNam->networkAccessible() == m_nam.data()->networkAccessible() && + oldNam->proxyFactory() == m_nam.data()->proxyFactory() + ) + return; + QNetworkAccessManager* newNam; #ifdef LIBLASTFM_FOUND newNam = new lastfm::NetworkAccessManager( this ); diff --git a/src/libtomahawk/utils/tomahawkutils.cpp b/src/libtomahawk/utils/tomahawkutils.cpp index b4e64fedc..3797557da 100644 --- a/src/libtomahawk/utils/tomahawkutils.cpp +++ b/src/libtomahawk/utils/tomahawkutils.cpp @@ -513,6 +513,15 @@ NetworkProxyFactory::setProxy( const QNetworkProxy& proxy ) } +bool NetworkProxyFactory::operator==( const NetworkProxyFactory& other ) +{ + if ( m_noProxyHosts != other.m_noProxyHosts or m_proxy != other.m_proxy ) + return false; + + return true; +} + + NetworkProxyFactory* proxyFactory() { diff --git a/src/libtomahawk/utils/tomahawkutils.h b/src/libtomahawk/utils/tomahawkutils.h index e8bcd23ce..924045a1d 100644 --- a/src/libtomahawk/utils/tomahawkutils.h +++ b/src/libtomahawk/utils/tomahawkutils.h @@ -65,6 +65,8 @@ namespace TomahawkUtils void setProxy( const QNetworkProxy &proxy ); QNetworkProxy proxy() { return m_proxy; } + bool operator==( const NetworkProxyFactory &other ); + private: QStringList m_noProxyHosts; QNetworkProxy m_proxy; From e7826d725e9d2dba6fc630bc51ba1fd2146ccd84 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 3 Nov 2011 17:22:10 -0400 Subject: [PATCH 098/109] Don't lose settings when you crash...sync immediately --- src/settingsdialog.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index 307e6d569..676888071 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -233,6 +233,7 @@ SettingsDialog::~SettingsDialog() m_resolversModel->saveScriptResolvers(); s->applyChanges(); + s->sync(); } else qDebug() << "Settings dialog cancelled, NOT saving prefs."; From 8152207334d809a558fb119cd1e32984aa76e497 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 3 Nov 2011 17:43:16 -0400 Subject: [PATCH 099/109] Cast and dereference so that the check actually works --- .../infosystem/infosystemworker.cpp | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/libtomahawk/infosystem/infosystemworker.cpp b/src/libtomahawk/infosystem/infosystemworker.cpp index 6252861ec..b58394130 100644 --- a/src/libtomahawk/infosystem/infosystemworker.cpp +++ b/src/libtomahawk/infosystem/infosystemworker.cpp @@ -372,14 +372,19 @@ InfoSystemWorker::newNam() return; } - if ( - oldNam && - !m_nam.isNull() && - oldNam->configuration() == m_nam.data()->configuration() && - oldNam->networkAccessible() == m_nam.data()->networkAccessible() && - oldNam->proxyFactory() == m_nam.data()->proxyFactory() - ) - return; + if + ( + oldNam && + !m_nam.isNull() && + oldNam->configuration() == m_nam.data()->configuration() && + oldNam->networkAccessible() == m_nam.data()->networkAccessible() + ) + { + TomahawkUtils::NetworkProxyFactory fac1 = *( dynamic_cast< TomahawkUtils::NetworkProxyFactory * >( oldNam->proxyFactory() ) ); + TomahawkUtils::NetworkProxyFactory fac2 = *( dynamic_cast< TomahawkUtils::NetworkProxyFactory * >( m_nam.data()->proxyFactory() ) ); + if ( fac1 == fac2 ) + return; + } QNetworkAccessManager* newNam; #ifdef LIBLASTFM_FOUND From 52b77464f1b272acf3a1151bc4a800bef2208a25 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 3 Nov 2011 20:51:00 -0400 Subject: [PATCH 100/109] Don't re-fetch charts on nam change --- .../infosystem/infoplugins/generic/chartsplugin.cpp | 3 ++- .../infosystem/infoplugins/generic/spotifyPlugin.cpp | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp index 46d064c7b..a0b3f9475 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp @@ -75,7 +75,8 @@ ChartsPlugin::namChangedSlot( QNetworkAccessManager *nam ) /// Then get each chart from resource /// We want to prepopulate the breadcrumb to fetch them before they are asked for - if( !m_chartResources.isEmpty() && m_nam ){ + if ( !m_chartResources.isEmpty() && m_nam && m_allChartsMap.isEmpty() ) + { tDebug() << "ChartsPlugin: InfoChart fetching possible resources"; foreach ( QVariant resource, m_chartResources ) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp index c9549c81e..245ceb593 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp @@ -66,6 +66,10 @@ SpotifyPlugin::namChangedSlot( QNetworkAccessManager *nam ) m_nam = QWeakPointer< QNetworkAccessManager >( nam ); + // we never need to re-fetch + if ( !m_allChartsMap.isEmpty() ) + return; + /// We need to fetch possible types before they are asked for tDebug() << "SpotifyPlugin: InfoChart fetching possible resources"; From b26bd5e2960dd9d9d338b478e43b4e6ff1d7d00e Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 3 Nov 2011 21:07:43 -0400 Subject: [PATCH 101/109] Don't assert on script output in debug mode, just show the error --- src/libtomahawk/resolvers/qtscriptresolver.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/resolvers/qtscriptresolver.cpp b/src/libtomahawk/resolvers/qtscriptresolver.cpp index f0b760d82..0d4bd5978 100644 --- a/src/libtomahawk/resolvers/qtscriptresolver.cpp +++ b/src/libtomahawk/resolvers/qtscriptresolver.cpp @@ -31,6 +31,7 @@ #include #include #include +#include // FIXME: bloody hack, remove this for 0.3 // this one adds new functionality to old resolvers @@ -186,9 +187,9 @@ ScriptEngine::javaScriptConsoleMessage( const QString& message, int lineNumber, tLog() << "JAVASCRIPT:" << m_scriptPath << message << lineNumber << sourceID; /// I guess there is somereason for a assert in here, maybe fatal js errors, but /// undefined is not so fatal - if(sourceID != "undefined") - Q_ASSERT( false ); - +#ifdef QT_DEBUG + QMessageBox::critical( 0, "Script Resolver Error", QString( "%1 %2 %3 %4" ).arg( m_scriptPath ).arg( message ).arg( lineNumber ).arg( sourceID ) ); +#endif } From 1c14f562ad67219e26be7a5ba6d62c0bcffaf96f Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 3 Nov 2011 21:09:18 -0400 Subject: [PATCH 102/109] Remove the http iofactory. AudioEngine handles http:// urls by giving them to phonon directly. Lets not download an mp3 twice. --- src/libtomahawk/network/servent.cpp | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/libtomahawk/network/servent.cpp b/src/libtomahawk/network/servent.cpp index 3d26e6b04..881c330cf 100644 --- a/src/libtomahawk/network/servent.cpp +++ b/src/libtomahawk/network/servent.cpp @@ -80,12 +80,6 @@ Servent::Servent( QObject* parent ) boost::bind( &Servent::remoteIODeviceFactory, this, _1 ); this->registerIODeviceFactory( "servent", fac ); } - - { - boost::function(result_ptr)> fac = - boost::bind( &Servent::httpIODeviceFactory, this, _1 ); - this->registerIODeviceFactory( "http", fac ); - } } @@ -880,12 +874,3 @@ Servent::localFileIODeviceFactory( const Tomahawk::result_ptr& result ) return QSharedPointer( io ); } - - -QSharedPointer -Servent::httpIODeviceFactory( const Tomahawk::result_ptr& result ) -{ - QNetworkRequest req( result->url() ); - QNetworkReply* reply = TomahawkUtils::nam()->get( req ); - return QSharedPointer( reply, &QObject::deleteLater ); -} From 844eb3868aaba2027009fd5cc5aa1dee7ea8c1ef Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 3 Nov 2011 21:25:15 -0400 Subject: [PATCH 103/109] Make sure to always hide the loading spinner, even if it's on a hidden page --- src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.cpp | 3 --- src/libtomahawk/playlist/treemodel.cpp | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.cpp b/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.cpp index 83b739954..19a74a2eb 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.cpp @@ -74,9 +74,6 @@ LoadingSpinner::fadeIn() void LoadingSpinner::fadeOut() { - if ( !isVisible() ) - return; - m_showHide->setDirection( QTimeLine::Backward ); if ( m_showHide->state() != QTimeLine::Running ) diff --git a/src/libtomahawk/playlist/treemodel.cpp b/src/libtomahawk/playlist/treemodel.cpp index 56afbc2e2..a9c4a0b1d 100644 --- a/src/libtomahawk/playlist/treemodel.cpp +++ b/src/libtomahawk/playlist/treemodel.cpp @@ -851,6 +851,8 @@ TreeModel::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QV if ( m_receivedInfoData.contains( requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >() ) ) break; + emit loadingFinished(); + QVariantMap returnedData = output.value< QVariantMap >(); if ( returnedData.isEmpty() ) break; From db5313129eb8f53a0a760fdc7e6371bfe227922d Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 3 Nov 2011 21:43:16 -0400 Subject: [PATCH 104/109] Show rated resolver star in yellow, so it's clear what you have rated and can no longer change --- src/GetNewStuffDelegate.cpp | 23 ++++++++++++++--------- src/GetNewStuffDelegate.h | 2 +- src/GetNewStuffModel.cpp | 2 ++ src/GetNewStuffModel.h | 3 ++- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/GetNewStuffDelegate.cpp b/src/GetNewStuffDelegate.cpp index 1e6466de8..1e1c5d381 100644 --- a/src/GetNewStuffDelegate.cpp +++ b/src/GetNewStuffDelegate.cpp @@ -191,8 +191,10 @@ GetNewStuffDelegate::paint( QPainter* painter, const QStyleOptionViewItem& optio if ( i == 1 ) m_cachedStarRects[ QPair(index.row(), index.column()) ] = r; - QPixmap pm; - if ( m_hoveringOver > -1 && ( m_hoveringItem.first == index.row() && m_hoveringItem.second == index.column() ) ) + const bool userHasRated = index.data( GetNewStuffModel::UserHasRatedRole ).toBool(); + if ( !userHasRated && // Show on-hover animation if the user hasn't rated it yet, and is hovering over it + m_hoveringOver > -1 && + m_hoveringItem == index ) { if ( i <= m_hoveringOver ) // positive star painter->drawPixmap( r, m_onHoverStar ); @@ -201,8 +203,13 @@ GetNewStuffDelegate::paint( QPainter* painter, const QStyleOptionViewItem& optio } else { - if ( i <= rating ) // positive star - painter->drawPixmap( r, m_ratingStarPositive ); + if ( i <= rating ) // positive or rated star + { + if ( userHasRated ) + painter->drawPixmap( r, m_onHoverStar ); + else + painter->drawPixmap( r, m_ratingStarPositive ); + } else painter->drawPixmap( r, m_ratingStarNegative ); } @@ -299,8 +306,7 @@ GetNewStuffDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, cons { // 0-indexed m_hoveringOver = whichStar; - m_hoveringItem.first = index.row(); - m_hoveringItem.second = index.column(); + m_hoveringItem = index; } return true; @@ -309,10 +315,9 @@ GetNewStuffDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, cons if ( m_hoveringOver > -1 ) { - QModelIndex idx = model->index( m_hoveringItem.first, m_hoveringItem.second ); - emit update( idx ); + emit update( m_hoveringItem ); m_hoveringOver = -1; - m_hoveringItem = QPair(); + m_hoveringItem = QPersistentModelIndex(); } return false; } diff --git a/src/GetNewStuffDelegate.h b/src/GetNewStuffDelegate.h index 93e32bd9a..570210226 100644 --- a/src/GetNewStuffDelegate.h +++ b/src/GetNewStuffDelegate.h @@ -42,7 +42,7 @@ private: int m_widestTextWidth; int m_hoveringOver; - QPair m_hoveringItem; + QPersistentModelIndex m_hoveringItem; mutable QHash< QPair, QRect > m_cachedButtonRects; mutable QHash< QPair, QRect > m_cachedStarRects; }; diff --git a/src/GetNewStuffModel.cpp b/src/GetNewStuffModel.cpp index cb2aa99cc..405ca968b 100644 --- a/src/GetNewStuffModel.cpp +++ b/src/GetNewStuffModel.cpp @@ -94,6 +94,8 @@ GetNewStuffModel::data( const QModelIndex& index, int role ) const return resolver.author(); case StateRole: return (int)AtticaManager::instance()->resolverState( resolver ); + case UserHasRatedRole: + return AtticaManager::instance()->userHasRated( resolver ); } return QVariant(); } diff --git a/src/GetNewStuffModel.h b/src/GetNewStuffModel.h index 60e2e9104..378057e1e 100644 --- a/src/GetNewStuffModel.h +++ b/src/GetNewStuffModel.h @@ -38,7 +38,8 @@ public: DescriptionRole = Qt::UserRole + 5, TypeRole = Qt::UserRole + 6, // Category in attica-speak. What sort of item this is (resolver, etc). AuthorRole = Qt::UserRole + 7, - StateRole = Qt::UserRole + 8 + StateRole = Qt::UserRole + 8, + UserHasRatedRole = Qt::UserRole + 9 }; enum Types { From 56cb00013083b09f0c72c6c591c932e6f5938b02 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 3 Nov 2011 22:25:14 -0400 Subject: [PATCH 105/109] Changelogify 0.3. Phew! --- ChangeLog | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/ChangeLog b/ChangeLog index 096ff0f30..15f431f6f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,12 +1,72 @@ Version 0.3.0: + * Fixed bug where we would download http:// tracks twice. + * Make artist names in the album view clickable. + * Don't start playing if a tomahawk:// link was clicked while Tomahawk + is paused. + * Make artist name clickable in header of Album pages. + * Fixed adding social actions such as loved tracks from other sources. + * Added a drop shadow to cover images, and put CD placeholder in jewel + case. + * Added shuffle and repeat support to tree view. + * Draw a speaker next to the currently playing playlist. + * Refresh station previews whenever a filter is changed. + * Support and show official releases on album and track pages. + * Filter out duplicates from station previews and upcoming tracks. + * Resolve lists top-down. + * Added YouTube resolver. + * Fixed bug where going offline then online would not re-connect to many + peers. + * Added support for auto-updating live xspf playlists. + * Don't show an age of 41 for tracks that have no age information. + * Show config UI for resolvers that have them as so on as you add the resolver. + * Add support for Echo Nest Personal Catalogs and User Radio. Synchronize + your catalog with The Echo Nest and enable personal recommendations + from you and your friends. + * Added support for Grooveshark resolver (requires Grooveshark Anywhere). + * Fixed re-resolving when resolvers or sources go offline. + * Correctly sort recently played tracks on the Dashboard. + * Show a Lion full-screen toggle button if running on Lion. + * Display list of who is currently listening along to you. + * Show headphone icon in source item to allow users to listen along; paint + headphones red on a source if the user is listening along to it. + * Added new job status view in the bottom of the source list that displays + current jobs such as resolving, parsing playlists, and loading from + database. + * Parse and convert a Spotify playlist url when dropped anywhere on the + sidebar + * Convert resolvers to use asynchronous calls to avoid blocking Tomahawk's + UI, greatly increasing responsiveness of Tomahawk while resolving. + * Fixed no playlists overlay not disappearing when playlists were added. + * Add support for parsing itunes track, artist and album links. + * Fixed crash when syncing playlists with peers. + * Add support for browsing, downloading and rating resolvers + from inside Tomahawk directly. + * Support multi-folder selection and scanning. + * Actually remove deleted files from the collection. + * Fixed handling of special characters in tomahawk:// links + * Improve sidebar performance by caching pixmaps and shrinking them. + * Send updated playlists to peers when tracks are moved/copied. + * Remove splitter handles in sidebar * Fixed Tomahawk preventing system shutdown / logut. * Ignore leading 'The' when sorting artists. * Added Charts page, which shows various sources' top hits & artists. * The Collection tree-views can now be filtered. + * Fixed crash when pressing enter in an empty playlist. * Moved the song queue below to the left, below the sidebar. * Added Footnotes, a contextual view that you can slide it. * Show recently added playlists in dashboard rather than recently opened playlists. + * Fixed seek slider and give it some smooth animation between ticks. + * Fixed Twitter issue where it would repeatedly send DMs to friends. + * Add a new drag and drop menu when dropping items onto playlists, + allowing users to drop the dragged tracks, the whole album, or + the whole artists's tracks. + * Bring Tomahawk window to the front when clicking a Tomahawk link. + * Fixed crash in source list when initially syncing with remote sources. + * Open temporary artist, album, and search playlists as temporary items + in the sidebar. + * Fixed sorting of playlists and items in the artist view. + * Allow dragging and dropping albums and artists to playlists. * Added MPRIS 2.1 support. Version 0.2.3: From 2f5ebd20712c38a4e20f20af786b16faee5dc27b Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 4 Nov 2011 08:05:27 +0100 Subject: [PATCH 106/109] * Don't get stuck searching for another item if the TreeModel is empty. --- src/libtomahawk/playlist/treeproxymodel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/playlist/treeproxymodel.cpp b/src/libtomahawk/playlist/treeproxymodel.cpp index 5530d9aae..206bbd1b7 100644 --- a/src/libtomahawk/playlist/treeproxymodel.cpp +++ b/src/libtomahawk/playlist/treeproxymodel.cpp @@ -329,9 +329,9 @@ TreeProxyModel::siblingItem( int itemsAway ) Tomahawk::result_ptr TreeProxyModel::siblingItem( int itemsAway, bool readOnly ) { - qDebug() << Q_FUNC_INFO; - QModelIndex idx = currentIndex(); + if ( !idx.isValid() ) + return Tomahawk::result_ptr(); if ( m_shuffled ) idx = index( qrand() % rowCount( idx.parent() ), 0, idx.parent() ); From f53369089ac8ced2ab5cf168d8a8f2e33efa8d93 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Fri, 4 Nov 2011 12:59:05 -0400 Subject: [PATCH 107/109] Trim the address we get from upnp in case it's " ", otherwise it's not null --- src/libtomahawk/network/portfwdthread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/network/portfwdthread.cpp b/src/libtomahawk/network/portfwdthread.cpp index 78af1c27c..b0cb98e12 100644 --- a/src/libtomahawk/network/portfwdthread.cpp +++ b/src/libtomahawk/network/portfwdthread.cpp @@ -81,7 +81,7 @@ PortFwdThread::work() qDebug() << "Trying to setup portfwd on" << tryport; if ( m_portfwd->add( tryport, m_port ) ) { - QString pubip = QString( m_portfwd->external_ip().c_str() ); + QString pubip = QString( m_portfwd->external_ip().c_str() ).trimmed(); m_externalAddress = QHostAddress( pubip ); m_externalPort = tryport; tDebug() << "External servent address detected as" << pubip << ":" << m_externalPort; From 2ae503612b56d494f1647782c7b48185a6657149 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Fri, 4 Nov 2011 20:01:08 -0300 Subject: [PATCH 108/109] Update libechonest version in Readme --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index d035b51a2..1c1b8701f 100644 --- a/README +++ b/README @@ -34,7 +34,7 @@ Dependencies TagLib 1.6.2 - http://developer.kde.org/~wheeler/taglib.html Boost 1.3 - http://www.boost.org/ CLucene 0.9.23 (0.9.21 will fail) - http://clucene.sourceforge.net/download.shtml - libechonest 1.1.9 - http://projects.kde.org/projects/playground/libs/libechonest/ + libechonest 1.2.0 - http://projects.kde.org/projects/playground/libs/libechonest/ The following dependencies are optional, but recommended: From 69f1d776eb9541dba1c6de738af05618f2e59175 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Fri, 4 Nov 2011 21:20:59 -0400 Subject: [PATCH 109/109] Revert "Remove the http iofactory. AudioEngine handles http:// urls by giving them to phonon directly. Lets not download an mp3 twice." This reverts commit 1c14f562ad67219e26be7a5ba6d62c0bcffaf96f. --- src/libtomahawk/network/servent.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/libtomahawk/network/servent.cpp b/src/libtomahawk/network/servent.cpp index 881c330cf..3d26e6b04 100644 --- a/src/libtomahawk/network/servent.cpp +++ b/src/libtomahawk/network/servent.cpp @@ -80,6 +80,12 @@ Servent::Servent( QObject* parent ) boost::bind( &Servent::remoteIODeviceFactory, this, _1 ); this->registerIODeviceFactory( "servent", fac ); } + + { + boost::function(result_ptr)> fac = + boost::bind( &Servent::httpIODeviceFactory, this, _1 ); + this->registerIODeviceFactory( "http", fac ); + } } @@ -874,3 +880,12 @@ Servent::localFileIODeviceFactory( const Tomahawk::result_ptr& result ) return QSharedPointer( io ); } + + +QSharedPointer +Servent::httpIODeviceFactory( const Tomahawk::result_ptr& result ) +{ + QNetworkRequest req( result->url() ); + QNetworkReply* reply = TomahawkUtils::nam()->get( req ); + return QSharedPointer( reply, &QObject::deleteLater ); +}