From 92240b6e0c4f9774ab3504d8544b38a3d8fdc946 Mon Sep 17 00:00:00 2001
From: Christian Muehlhaeuser <muesli@gmail.com>
Date: Fri, 9 Sep 2011 10:27:37 +0200
Subject: [PATCH] * Added InfoSystem support to TreeModel. * Added 'Official
 Releases Only' toggle-button on Artist Page. * Cleaned up a few sources.

---
 src/libtomahawk/CMakeLists.txt                |   2 +
 .../infoplugins/generic/lastfmplugin.cpp      |  29 ++-
 .../infoplugins/generic/musicbrainzPlugin.cpp | 175 ++++++++++++++--
 .../infoplugins/generic/musicbrainzPlugin.h   |   4 +
 src/libtomahawk/playlist/trackmodelitem.cpp   |   6 +-
 src/libtomahawk/playlist/treeitemdelegate.cpp |   4 +-
 src/libtomahawk/playlist/treemodel.cpp        | 191 +++++++++++++-----
 src/libtomahawk/playlist/treemodel.h          |  14 +-
 src/libtomahawk/playlist/treemodelitem.cpp    | 105 ++++++++++
 src/libtomahawk/playlist/treemodelitem.h      |  10 +
 src/libtomahawk/playlist/treeproxymodel.cpp   |  23 ++-
 src/libtomahawk/widgets/HeaderWidget.cpp      |  14 +-
 src/libtomahawk/widgets/HeaderWidget.h        |  11 +-
 src/libtomahawk/widgets/combobox.cpp          |  42 ++--
 src/libtomahawk/widgets/combobox.h            |  10 +-
 .../widgets/infowidgets/ArtistInfoWidget.cpp  |  23 ++-
 .../widgets/infowidgets/ArtistInfoWidget.h    |   2 +
 .../widgets/infowidgets/ArtistInfoWidget.ui   |  35 +++-
 18 files changed, 543 insertions(+), 157 deletions(-)

diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt
index 507d43c44..3aeabfeb7 100644
--- a/src/libtomahawk/CMakeLists.txt
+++ b/src/libtomahawk/CMakeLists.txt
@@ -197,6 +197,7 @@ set( libSources
     widgets/HeaderLabel.cpp
     widgets/HeaderWidget.cpp
     widgets/combobox.cpp
+    widgets/ToggleButton.cpp
     widgets/SocialPlaylistWidget.cpp
     widgets/infowidgets/sourceinfowidget.cpp
     widgets/infowidgets/ArtistInfoWidget.cpp
@@ -395,6 +396,7 @@ set( libHeaders
     widgets/HeaderLabel.h
     widgets/HeaderWidget.h
     widgets/combobox.h
+    widgets/ToggleButton.h
     widgets/SocialPlaylistWidget.h
     widgets/infowidgets/sourceinfowidget.h
     widgets/infowidgets/ArtistInfoWidget.h
diff --git a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp
index d4b638a80..b187111af 100644
--- a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp
+++ b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp
@@ -419,20 +419,20 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria,
         case InfoChartCapabilities:
         {
             QList<Chart> track_charts;
-            track_charts.append(Chart("chart.getTopTracks", "Top Tracks", "tracks"));
-            track_charts.append(Chart("chart.getLovedTracks", "Loved Tracks", "tracks"));
-            track_charts.append(Chart("chart.getHypedTracks", "Hyped Tracks", "tracks"));
+            track_charts.append( Chart( "chart.getTopTracks", "Top Tracks", "tracks" ) );
+            track_charts.append( Chart( "chart.getLovedTracks", "Loved Tracks", "tracks" ) );
+            track_charts.append( Chart( "chart.getHypedTracks", "Hyped Tracks", "tracks" ) );
 
             QList<Chart> artist_charts;
-            artist_charts.append(Chart("chart.getTopArtists", "Top Artists", "artists"));
-            artist_charts.append(Chart("chart.getHypedArtists", "Hyped Artists", "artists"));
+            artist_charts.append( Chart( "chart.getTopArtists", "Top Artists", "artists" ) );
+            artist_charts.append( Chart( "chart.getHypedArtists", "Hyped Artists", "artists" ) );
 
             QVariantMap charts;
-            charts.insert("Tracks", QVariant::fromValue<QList<Chart> >(track_charts));
-            charts.insert("Artists", QVariant::fromValue<QList<Chart> >(artist_charts));
+            charts.insert( "Tracks", QVariant::fromValue<QList<Chart> >( track_charts ) );
+            charts.insert( "Artists", QVariant::fromValue<QList<Chart> >( artist_charts ) );
 
             QVariantMap result;
-            result.insert("Last.fm", QVariant::fromValue<QVariantMap>(charts));
+            result.insert( "Last.fm", QVariant::fromValue<QVariantMap>( charts ) );
 
             emit info(
                 requestId,
@@ -533,6 +533,7 @@ LastFmPlugin::similarArtistsReturned()
     emit updateCache( criteria, 2419200000, requestData.type, returnedData );
 }
 
+
 void
 LastFmPlugin::chartReturned()
 {
@@ -544,7 +545,8 @@ LastFmPlugin::chartReturned()
     const QRegExp artists_rx( "chart\\.\\S+artists\\S*", Qt::CaseInsensitive );
     const QString url = reply->url().toString();
 
-    if( url.contains( tracks_rx ) ) {
+    if ( url.contains( tracks_rx ) )
+    {
         QList<lastfm::Track> tracks = parseTrackList( reply );
         QList<ArtistTrackPair> top_tracks;
         foreach( const lastfm::Track &t, tracks ) {
@@ -557,7 +559,9 @@ LastFmPlugin::chartReturned()
         returnedData["tracks"] = QVariant::fromValue( top_tracks );
         returnedData["type"] = "tracks";
 
-    } else if( url.contains( artists_rx ) ) {
+    }
+    else if ( url.contains( artists_rx ) )
+    {
         QList<lastfm::Artist> list = lastfm::Artist::list( reply );
         QStringList al;
         tDebug() << "LastFmPlugin:"<< "\tgot " << list.size() << " artists";
@@ -565,7 +569,9 @@ LastFmPlugin::chartReturned()
             al << a.toString();
         returnedData["artists"] = al;
         returnedData["type"] = "artists";
-    } else {
+    }
+    else
+    {
         tDebug() << "LastfmPlugin:: got non tracks and non artists";
     }
 
@@ -579,6 +585,7 @@ LastFmPlugin::chartReturned()
     // TODO update cache
 }
 
+
 void
 LastFmPlugin::topTracksReturned()
 {
diff --git a/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.cpp
index d64f2f88c..fe0fedff7 100644
--- a/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.cpp
+++ b/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.cpp
@@ -31,7 +31,7 @@ MusicBrainzPlugin::MusicBrainzPlugin()
     : InfoPlugin()
 {
     qDebug() << Q_FUNC_INFO;
-    m_supportedGetTypes << Tomahawk::InfoSystem::InfoArtistReleases;
+    m_supportedGetTypes << Tomahawk::InfoSystem::InfoArtistReleases << Tomahawk::InfoSystem::InfoAlbumSongs;
 }
 
 
@@ -44,7 +44,6 @@ MusicBrainzPlugin::~MusicBrainzPlugin()
 void
 MusicBrainzPlugin::namChangedSlot( QNetworkAccessManager *nam )
 {
-    qDebug() << Q_FUNC_INFO;
     if ( !nam )
         return;
 
@@ -55,8 +54,6 @@ MusicBrainzPlugin::namChangedSlot( QNetworkAccessManager *nam )
 void
 MusicBrainzPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
 {
-    qDebug() << Q_FUNC_INFO;
-
     if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoCriteriaHash >() )
     {
         emit info( requestId, requestData, QVariant() );
@@ -69,16 +66,39 @@ MusicBrainzPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestDat
         return;
     }
 
-    if ( requestData.type == InfoArtistReleases )
+    switch ( requestData.type )
     {
-        QString requestString( "http://musicbrainz.org/ws/2/artist" );
-        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 ) );
+        case InfoArtistReleases:
+        {
+            QString requestString( "http://musicbrainz.org/ws/2/artist" );
+            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() ) );
+            connect( reply, SIGNAL( finished() ), SLOT( artistSearchSlot() ) );
+            break;
+        }
+
+        case InfoAlbumSongs:
+        {
+            QString requestString( "http://musicbrainz.org/ws/2/artist" );
+            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() ) );
+            break;
+        }
+
+        default:
+        {
+            Q_ASSERT( false );
+            break;
+        }
     }
 }
 
@@ -86,7 +106,6 @@ MusicBrainzPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestDat
 bool
 MusicBrainzPlugin::isValidTrackData( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
 {
-    qDebug() << Q_FUNC_INFO;
     if ( requestData.input.isNull() || !requestData.input.isValid() || !requestData.input.canConvert< QVariantMap >() )
     {
         emit info( requestId, requestData, QVariant() );
@@ -113,7 +132,6 @@ MusicBrainzPlugin::isValidTrackData( uint requestId, Tomahawk::InfoSystem::InfoR
 void
 MusicBrainzPlugin::artistSearchSlot()
 {
-    qDebug() << Q_FUNC_INFO;
     QNetworkReply* oldReply = qobject_cast<QNetworkReply*>( sender() );
     if ( !oldReply )
         return; //timeout will handle it
@@ -131,17 +149,89 @@ MusicBrainzPlugin::artistSearchSlot()
     QString requestString( "http://musicbrainz.org/ws/2/release?status=official&type=album|ep" );
     QUrl url( requestString );
     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( albumSearchSlot() ) );
+    connect( newReply, SIGNAL( finished() ), SLOT( albumFoundSlot() ) );
 }
 
 
 void
 MusicBrainzPlugin::albumSearchSlot()
 {
-    qDebug() << Q_FUNC_INFO;
+    QNetworkReply* oldReply = qobject_cast<QNetworkReply*>( sender() );
+    if ( !oldReply )
+        return; //timeout will handle it
+
+    QDomDocument doc;
+    doc.setContent( oldReply->readAll() );
+    QDomNodeList domNodeList = doc.elementsByTagName( "artist" );
+    if ( domNodeList.isEmpty() )
+    {
+        emit info( oldReply->property( "requestId" ).toUInt(), oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
+        return;
+    }
+
+    QString artist_id = domNodeList.at( 0 ).toElement().attribute( "id" );
+    QString requestString( "http://musicbrainz.org/ws/2/release?status=official&type=album|ep" );
+    QUrl url( requestString );
+    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() ) );
+}
+
+
+void
+MusicBrainzPlugin::tracksSearchSlot()
+{
+    QNetworkReply* oldReply = qobject_cast<QNetworkReply*>( sender() );
+    if ( !oldReply )
+        return; //timeout will handle it
+
+    QDomDocument doc;
+    doc.setContent( oldReply->readAll() );
+    QDomNodeList domNodeList = doc.elementsByTagName( "release" );
+    if ( domNodeList.isEmpty() )
+    {
+        emit info( oldReply->property( "requestId" ).toUInt(), oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
+        return;
+    }
+
+    Tomahawk::InfoSystem::InfoRequestData requestData = oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
+    InfoCriteriaHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoCriteriaHash >();
+
+    QDomElement element;
+    for ( int i = 0; i < domNodeList.count(); i++ )
+    {
+        QDomNodeList albumNodeList = domNodeList.at( i ).toElement().elementsByTagName( "title" );
+        if ( albumNodeList.at( 0 ).toElement().text() == hash["album"] )
+            element = domNodeList.at( i ).toElement();
+    }
+
+    if ( element.isNull() )
+    {
+        emit info( oldReply->property( "requestId" ).toUInt(), oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
+        return;
+    }
+
+    QString release_id = element.attribute( "id" );
+    QString requestString = QString( "http://musicbrainz.org/ws/2/release/%1?inc=recordings" ).arg( release_id );
+    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() ) );
+}
+
+
+void
+MusicBrainzPlugin::albumFoundSlot()
+{
     QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() );
     if ( !reply )
         return; //timeout will handle it
@@ -163,6 +253,55 @@ MusicBrainzPlugin::albumSearchSlot()
             albums << album;
     }
 
-    qDebug() << Q_FUNC_INFO << "Found albums:" << albums;
-    emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant( albums ) );
+    Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
+    QVariantMap returnedData;
+    returnedData["albums"] = albums;
+    emit info( reply->property( "requestId" ).toUInt(), requestData, returnedData );
+
+    Tomahawk::InfoSystem::InfoCriteriaHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoCriteriaHash>();
+    Tomahawk::InfoSystem::InfoCriteriaHash criteria;
+    criteria["artist"] = origData["artist"];
+    emit updateCache( criteria, 0, requestData.type, returnedData );
+}
+
+
+void
+MusicBrainzPlugin::tracksFoundSlot()
+{
+    QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() );
+    if ( !reply )
+        return; //timeout will handle it
+
+    QDomDocument doc;
+    doc.setContent( reply->readAll() );
+    QDomNodeList domNodeList = doc.elementsByTagName( "recording" );
+    if ( domNodeList.isEmpty() )
+    {
+        emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
+        return;
+    }
+
+    QStringList tracks;
+    for ( int i = 0; i < domNodeList.count(); i++ )
+    {
+        QDomNodeList trackNodeList = domNodeList.at( i ).toElement().elementsByTagName( "title" );
+
+        for ( int j = 0; j < trackNodeList.count(); j++ )
+        {
+            QString track = trackNodeList.at( j ).toElement().text();
+            if ( !tracks.contains( track ) )
+                tracks << track;
+        }
+    }
+
+    Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
+    QVariantMap returnedData;
+    returnedData["tracks"] = tracks;
+    emit info( reply->property( "requestId" ).toUInt(), requestData, returnedData );
+
+    Tomahawk::InfoSystem::InfoCriteriaHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoCriteriaHash>();
+    Tomahawk::InfoSystem::InfoCriteriaHash criteria;
+    criteria["artist"] = origData["artist"];
+    criteria["album"] = origData["album"];
+    emit updateCache( criteria, 0, requestData.type, returnedData );
 }
diff --git a/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.h b/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.h
index c1ccb0733..9586fa1af 100644
--- a/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.h
+++ b/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.h
@@ -61,6 +61,10 @@ virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoCriteriaH
 private slots:
     void artistSearchSlot();
     void albumSearchSlot();
+    void tracksSearchSlot();
+
+    void albumFoundSlot();
+    void tracksFoundSlot();
 
 private:
     bool isValidTrackData( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
diff --git a/src/libtomahawk/playlist/trackmodelitem.cpp b/src/libtomahawk/playlist/trackmodelitem.cpp
index 36f1883c1..3f363fbc0 100644
--- a/src/libtomahawk/playlist/trackmodelitem.cpp
+++ b/src/libtomahawk/playlist/trackmodelitem.cpp
@@ -109,11 +109,7 @@ TrackModelItem::setupItem( const Tomahawk::query_ptr& query, TrackModelItem* par
     m_isPlaying = false;
     toberemoved = false;
     m_query = query;
-    if ( query->numResults() )
-    {
-//        emit dataChanged();
-    }
-    else
+    if ( !query->numResults() )
     {
         connect( query.data(), SIGNAL( resultsAdded( QList<Tomahawk::result_ptr> ) ),
                                SIGNAL( dataChanged() ) );
diff --git a/src/libtomahawk/playlist/treeitemdelegate.cpp b/src/libtomahawk/playlist/treeitemdelegate.cpp
index 4d18f60d2..1328b15cd 100644
--- a/src/libtomahawk/playlist/treeitemdelegate.cpp
+++ b/src/libtomahawk/playlist/treeitemdelegate.cpp
@@ -69,9 +69,9 @@ TreeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
     {
         text = item->album()->name();
     }
-    else if ( !item->result().isNull() )
+    else if ( !item->result().isNull() || !item->query().isNull() )
     {
-        float opacity = item->result()->score();
+        float opacity = item->result().isNull() ? 0.0 : item->result()->score();
         opacity = qMax( (float)0.3, opacity );
         QColor textColor = TomahawkUtils::alphaBlend( option.palette.color( QPalette::Foreground ), option.palette.color( QPalette::Background ), opacity );
 
diff --git a/src/libtomahawk/playlist/treemodel.cpp b/src/libtomahawk/playlist/treemodel.cpp
index 8d5b634ab..9da5eda58 100644
--- a/src/libtomahawk/playlist/treemodel.cpp
+++ b/src/libtomahawk/playlist/treemodel.cpp
@@ -29,15 +29,15 @@
 #include "utils/tomahawkutils.h"
 #include "utils/logger.h"
 
-static QString s_tmInfoIdentifier = QString( "TREEMODEL" );
-
 using namespace Tomahawk;
 
 
 TreeModel::TreeModel( QObject* parent )
     : QAbstractItemModel( parent )
     , m_rootItem( new TreeModelItem( 0, this ) )
+    , m_infoId( uuid() )
     , m_columnStyle( AllColumns )
+    , m_mode( Database )
 {
     setIcon( QPixmap( RESPATH "images/music-icon.png" ) );
 
@@ -100,7 +100,7 @@ TreeModel::getCover( const QModelIndex& index )
     trackInfo["pptr"] = QString::number( (qlonglong)item );
     m_coverHash.insert( (qlonglong)item, index );
 
-    requestData.caller = s_tmInfoIdentifier;
+    requestData.caller = m_infoId;
     requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( trackInfo );
     requestData.customData = QVariantMap();
     Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
@@ -261,7 +261,7 @@ TreeModel::data( const QModelIndex& index, int role ) const
 
     if ( role == Qt::SizeHintRole )
     {
-        if ( !entry->result().isNull() )
+        if ( !entry->result().isNull() || !entry->query().isNull() )
         {
             return QSize( 128, 20 );
         }
@@ -288,7 +288,7 @@ TreeModel::data( const QModelIndex& index, int role ) const
     {
         return entry->album()->name();
     }
-    else
+    else if ( !entry->result().isNull() )
     {
         const result_ptr& result = entry->result();
         switch( index.column() )
@@ -322,6 +322,10 @@ TreeModel::data( const QModelIndex& index, int role ) const
                 return QVariant();
         }
     }
+    else if ( !entry->query().isNull() && index.column() == Name )
+    {
+        return entry->query()->track();
+    }
 
     return QVariant();
 }
@@ -408,7 +412,7 @@ TreeModel::mimeData( const QModelIndexList &indexes ) const
     // lets try with album only
     fail = false;
     resultData.clear();
-    foreach ( const QModelIndex& i, indexes)
+    foreach ( const QModelIndex& i, indexes )
     {
         if ( i.column() > 0 || indexes.contains( i.parent() ) )
             continue;
@@ -436,11 +440,10 @@ TreeModel::mimeData( const QModelIndexList &indexes ) const
         return mimeData;
     }
 
-
     // lets try with tracks only
     fail = false;
     resultData.clear();
-    foreach ( const QModelIndex& i, indexes)
+    foreach ( const QModelIndex& i, indexes )
     {
         if ( i.column() > 0 || indexes.contains( i.parent() ) )
             continue;
@@ -549,8 +552,6 @@ TreeModel::addAllCollections()
 void
 TreeModel::addArtists( const artist_ptr& artist )
 {
-    qDebug() << Q_FUNC_INFO;
-
     if ( artist.isNull() )
         return;
 
@@ -565,37 +566,70 @@ TreeModel::addArtists( const artist_ptr& artist )
 void
 TreeModel::addAlbums( const artist_ptr& artist, const QModelIndex& parent )
 {
-    qDebug() << Q_FUNC_INFO;
-
     emit loadingStarted();
-    DatabaseCommand_AllAlbums* cmd = new DatabaseCommand_AllAlbums( m_collection, artist );
-    cmd->setData( parent.row() );
 
-    connect( cmd, SIGNAL( albums( QList<Tomahawk::album_ptr>, QVariant ) ),
-                    SLOT( onAlbumsAdded( QList<Tomahawk::album_ptr>, QVariant ) ) );
+    if ( m_mode == Database )
+    {
+        DatabaseCommand_AllAlbums* cmd = new DatabaseCommand_AllAlbums( m_collection, artist );
+        cmd->setData( parent.row() );
 
-    Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
+        connect( cmd, SIGNAL( albums( QList<Tomahawk::album_ptr>, QVariant ) ),
+                        SLOT( onAlbumsAdded( QList<Tomahawk::album_ptr>, QVariant ) ) );
+
+        Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
+    }
+    else if ( m_mode == InfoSystem )
+    {
+        Tomahawk::InfoSystem::InfoCriteriaHash artistInfo;
+        artistInfo["artist"] = artist->name();
+
+        Tomahawk::InfoSystem::InfoRequestData requestData;
+        requestData.caller = m_infoId;
+        requestData.customData["row"] = parent.row();
+        requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( artistInfo );
+        requestData.type = Tomahawk::InfoSystem::InfoArtistReleases;
+        Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
+    }
+    else
+        Q_ASSERT( false );
 }
 
 
 void
 TreeModel::addTracks( const album_ptr& album, const QModelIndex& parent )
 {
-    qDebug() << Q_FUNC_INFO;
-
     emit loadingStarted();
-    DatabaseCommand_AllTracks* cmd = new DatabaseCommand_AllTracks( m_collection );
-    cmd->setAlbum( album.data() );
 
     QList< QVariant > rows;
     rows << parent.row();
     rows << parent.parent().row();
-    cmd->setData( QVariant( rows ) );
 
-    connect( cmd, SIGNAL( tracks( QList<Tomahawk::query_ptr>, QVariant ) ),
-                    SLOT( onTracksAdded( QList<Tomahawk::query_ptr>, QVariant ) ) );
+    if ( m_mode == Database )
+    {
+        DatabaseCommand_AllTracks* cmd = new DatabaseCommand_AllTracks( m_collection );
+        cmd->setAlbum( album.data() );
+        cmd->setData( QVariant( rows ) );
 
-    Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
+        connect( cmd, SIGNAL( tracks( QList<Tomahawk::query_ptr>, QVariant ) ),
+                        SLOT( onTracksAdded( QList<Tomahawk::query_ptr>, QVariant ) ) );
+
+        Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
+    }
+    else if ( m_mode == InfoSystem )
+    {
+        Tomahawk::InfoSystem::InfoCriteriaHash artistInfo;
+        artistInfo["artist"] = album->artist()->name();
+        artistInfo["album"] = album->name();
+
+        Tomahawk::InfoSystem::InfoRequestData requestData;
+        requestData.caller = m_infoId;
+        requestData.customData["rows"] = QVariant( rows );
+        requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( artistInfo );
+        requestData.type = Tomahawk::InfoSystem::InfoAlbumSongs;
+        Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
+    }
+    else
+        Q_ASSERT( false );
 }
 
 
@@ -714,6 +748,7 @@ TreeModel::onTracksAdded( const QList<Tomahawk::query_ptr>& tracks, const QVaria
         return;
 
     QList< QVariant > rows = data.toList();
+    tDebug() << "Adding to:" << rows;
 
     QModelIndex parent = index( rows.first().toUInt(), 0, index( rows.at( 1 ).toUInt(), 0, QModelIndex() ) );
     TreeModelItem* parentItem = itemFromIndex( parent );
@@ -728,8 +763,11 @@ TreeModel::onTracksAdded( const QList<Tomahawk::query_ptr>& tracks, const QVaria
     TreeModelItem* item = 0;
     foreach( const query_ptr& query, tracks )
     {
-        qDebug() << query->toString();
-        item = new TreeModelItem( query->results().first(), parentItem );
+        if ( query->numResults() )
+            item = new TreeModelItem( query->results().first(), parentItem );
+        else
+            item = new TreeModelItem( query, parentItem );
+
         item->index = createIndex( parentItem->children.count() - 1, 0, item );
 
         connect( item, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) );
@@ -742,35 +780,84 @@ TreeModel::onTracksAdded( const QList<Tomahawk::query_ptr>& tracks, const QVaria
 void
 TreeModel::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output )
 {
-    if ( requestData.caller != s_tmInfoIdentifier ||
-       ( requestData.type != Tomahawk::InfoSystem::InfoAlbumCoverArt && requestData.type != Tomahawk::InfoSystem::InfoArtistImages ) )
+    if ( requestData.caller != m_infoId )
     {
         return;
     }
 
-    if ( !output.canConvert< QVariantMap >() )
+    switch ( requestData.type )
     {
-        qDebug() << "Cannot convert fetched art from a QByteArray";
-        return;
-    }
+        case Tomahawk::InfoSystem::InfoAlbumCoverArt:
+        case Tomahawk::InfoSystem::InfoArtistImages:
+        {
+            Tomahawk::InfoSystem::InfoCriteriaHash pptr = requestData.input.value< Tomahawk::InfoSystem::InfoCriteriaHash >();
+            QVariantMap returnedData = output.value< QVariantMap >();
+            const QByteArray ba = returnedData["imgbytes"].toByteArray();
+            if ( ba.length() )
+            {
+                QPixmap pm;
+                pm.loadFromData( ba );
+                bool ok;
+                qlonglong p = pptr["pptr"].toLongLong( &ok );
+                TreeModelItem* ai = itemFromIndex( m_coverHash.take( p ) );
+                if ( !ai )
+                    return;
 
-    Tomahawk::InfoSystem::InfoCriteriaHash pptr = requestData.input.value< Tomahawk::InfoSystem::InfoCriteriaHash >();
-    QVariantMap returnedData = output.value< QVariantMap >();
-    const QByteArray ba = returnedData["imgbytes"].toByteArray();
-    if ( ba.length() )
-    {
-        QPixmap pm;
-        pm.loadFromData( ba );
-        bool ok;
-        qlonglong p = pptr["pptr"].toLongLong( &ok );
-        TreeModelItem* ai = itemFromIndex( m_coverHash.take( p ) );
-        if ( !ai )
-            return;
+                if ( !pm.isNull() )
+                    ai->cover = pm;
 
-        if ( !pm.isNull() )
-            ai->cover = pm;
+                emit dataChanged( ai->index, ai->index.sibling( ai->index.row(), columnCount( QModelIndex() ) - 1 ) );
+            }
 
-        emit dataChanged( ai->index, ai->index.sibling( ai->index.row(), columnCount( QModelIndex() ) - 1 ) );
+            break;
+        }
+
+        case Tomahawk::InfoSystem::InfoArtistReleases:
+        {
+            QVariantMap returnedData = output.value< QVariantMap >();
+            QStringList albums = returnedData[ "albums" ].toStringList();
+            QList<album_ptr> al;
+
+            InfoSystem::InfoCriteriaHash inputInfo;
+            inputInfo = requestData.input.value< InfoSystem::InfoCriteriaHash >();
+            artist_ptr artist = Artist::get( inputInfo[ "artist" ], false );
+            Q_ASSERT( !artist.isNull() );
+
+            foreach ( const QString& albumName, albums )
+            {
+                int albumId = 0;
+                Tomahawk::album_ptr album = Tomahawk::Album::get( albumId, albumName, artist );
+                al << album;
+            }
+            onAlbumsAdded( al, requestData.customData[ "row" ] );
+
+            break;
+        }
+
+        case Tomahawk::InfoSystem::InfoAlbumSongs:
+        {
+            QVariantMap returnedData = output.value< QVariantMap >();
+            QStringList tracks = returnedData[ "tracks" ].toStringList();
+            QList<query_ptr> ql;
+
+            InfoSystem::InfoCriteriaHash inputInfo;
+            inputInfo = requestData.input.value< InfoSystem::InfoCriteriaHash >();
+
+            foreach ( const QString& trackName, tracks )
+            {
+                query_ptr query = Query::get( inputInfo[ "artist" ], trackName, inputInfo[ "album" ], uuid() );
+                ql << query;
+            }
+            onTracksAdded( ql, requestData.customData[ "rows" ] );
+
+            break;
+        }
+
+        default:
+        {
+            Q_ASSERT( false );
+            break;
+        }
     }
 }
 
@@ -779,7 +866,6 @@ void
 TreeModel::infoSystemFinished( QString target )
 {
     Q_UNUSED( target );
-//    qDebug() << Q_FUNC_INFO;
 }
 
 
@@ -836,10 +922,3 @@ TreeModel::indexFromArtist( const Tomahawk::artist_ptr& artist ) const
 
     return QModelIndex();
 }
-
-
-QModelIndex
-TreeModel::indexFromAlbum( const Tomahawk::album_ptr& album ) const
-{
-    return QModelIndex();
-}
diff --git a/src/libtomahawk/playlist/treemodel.h b/src/libtomahawk/playlist/treemodel.h
index 6f04d1645..2828784d2 100644
--- a/src/libtomahawk/playlist/treemodel.h
+++ b/src/libtomahawk/playlist/treemodel.h
@@ -50,9 +50,12 @@ public:
         AlbumPosition
     };
 
-    enum ColumnStyle // Default style is AllColumns
+    enum ColumnStyle
     { AllColumns = 0, TrackOnly };
 
+    enum ModelMode
+    { Database = 0, InfoSystem };
+
     explicit TreeModel( QObject* parent = 0 );
     virtual ~TreeModel();
 
@@ -65,15 +68,16 @@ public:
     virtual int albumCount() const { return rowCount( QModelIndex() ); }
 
     virtual bool hasChildren( const QModelIndex& parent = QModelIndex() ) const;
-
     virtual int rowCount( const QModelIndex& parent = QModelIndex() ) const;
     virtual int columnCount( const QModelIndex& parent = QModelIndex() ) const;
 
-    virtual void clear();
+    virtual ModelMode mode() const { return m_mode; }
+    virtual void setMode( 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;
 
+    virtual void clear();
     virtual void removeIndex( const QModelIndex& index );
     virtual void removeIndexes( const QList<QModelIndex>& indexes );
 
@@ -106,8 +110,6 @@ public:
     virtual void setIcon( const QPixmap& pixmap ) { m_icon = pixmap; }
 
     QModelIndex indexFromArtist( const Tomahawk::artist_ptr& artist ) const;
-    QModelIndex indexFromAlbum( const Tomahawk::album_ptr& album ) const;
-
     TreeModelItem* itemFromIndex( const QModelIndex& index ) const
     {
         if ( index.isValid() )
@@ -155,11 +157,13 @@ private slots:
 private:
     QPersistentModelIndex m_currentIndex;
     TreeModelItem* m_rootItem;
+    QString m_infoId;
 
     QString m_title;
     QString m_description;
     QPixmap m_icon;
     ColumnStyle m_columnStyle;
+    ModelMode m_mode;
 
     QList<Tomahawk::artist_ptr> m_artistsFilter;
 
diff --git a/src/libtomahawk/playlist/treemodelitem.cpp b/src/libtomahawk/playlist/treemodelitem.cpp
index a9e07a1e3..8d3c14880 100644
--- a/src/libtomahawk/playlist/treemodelitem.cpp
+++ b/src/libtomahawk/playlist/treemodelitem.cpp
@@ -135,3 +135,108 @@ TreeModelItem::TreeModelItem( const Tomahawk::result_ptr& result, TreeModelItem*
 
     toberemoved = false;
 }
+
+
+TreeModelItem::TreeModelItem( const Tomahawk::query_ptr& query, TreeModelItem* parent, int row )
+    : QObject( parent )
+    , m_query( query )
+{
+    this->parent = parent;
+    fetchingMore = false;
+    m_isPlaying = false;
+
+    if ( parent )
+    {
+        if ( row < 0 )
+        {
+            parent->children.append( this );
+            row = parent->children.count() - 1;
+        }
+        else
+        {
+            parent->children.insert( row, this );
+        }
+
+        this->model = parent->model;
+    }
+
+    toberemoved = false;
+
+    connect( query.data(), SIGNAL( resultsAdded( QList<Tomahawk::result_ptr> ) ),
+                             SLOT( onResultsChanged() ) );
+
+    connect( query.data(), SIGNAL( resultsRemoved( Tomahawk::result_ptr ) ),
+                             SLOT( onResultsChanged() ) );
+
+    connect( query.data(), SIGNAL( resultsChanged() ),
+                             SLOT( onResultsChanged() ) );
+}
+
+
+void
+TreeModelItem::onResultsChanged()
+{
+    if ( m_query->numResults() )
+        m_result = m_query->results().first();
+    else
+        m_result = result_ptr();
+
+    emit dataChanged();
+}
+
+
+QString
+TreeModelItem::name() const
+{
+    if ( !m_artist.isNull() )
+    {
+        return m_artist->name();
+    }
+    else if ( !m_album.isNull() )
+    {
+        return m_album->name();
+    }
+    else if ( !m_result.isNull() )
+    {
+        return m_result->track();
+    }
+    else if ( !m_query.isNull() )
+    {
+        return m_query->track();
+    }
+
+    Q_ASSERT( false );
+    return QString();
+}
+
+
+QString
+TreeModelItem::artistName() const
+{
+    if ( !m_result.isNull() )
+    {
+        return m_result->artist()->name();
+    }
+    else if ( !m_query.isNull() )
+    {
+        return m_query->artist();
+    }
+
+    return QString();
+}
+
+
+QString
+TreeModelItem::albumName() const
+{
+    if ( !m_result.isNull() && !m_result->album().isNull() )
+    {
+        return m_result->album()->name();
+    }
+    else if ( !m_query.isNull() )
+    {
+        return m_query->album();
+    }
+
+    return QString();
+}
diff --git a/src/libtomahawk/playlist/treemodelitem.h b/src/libtomahawk/playlist/treemodelitem.h
index 01f3b226e..e3d650c08 100644
--- a/src/libtomahawk/playlist/treemodelitem.h
+++ b/src/libtomahawk/playlist/treemodelitem.h
@@ -39,16 +39,22 @@ public:
     explicit TreeModelItem( const Tomahawk::artist_ptr& artist, TreeModelItem* parent = 0, int row = -1 );
     explicit TreeModelItem( const Tomahawk::album_ptr& album, TreeModelItem* parent = 0, int row = -1 );
     explicit TreeModelItem( const Tomahawk::result_ptr& result, TreeModelItem* parent = 0, int row = -1 );
+    explicit TreeModelItem( const Tomahawk::query_ptr& query, TreeModelItem* parent = 0, int row = -1 );
 
     const Tomahawk::artist_ptr& artist() const { return m_artist; };
     const Tomahawk::album_ptr& album() const { return m_album; };
     const Tomahawk::result_ptr& result() const { return m_result; };
+    const Tomahawk::query_ptr& query() const { return m_query; };
 
     bool isPlaying() { return m_isPlaying; }
     void setIsPlaying( bool b ) { m_isPlaying = b; emit dataChanged(); }
 
     void setCover( const QPixmap& cover ) { this->cover = cover; emit dataChanged(); }
 
+    QString name() const;
+    QString artistName() const;
+    QString albumName() const;
+
     TreeModelItem* parent;
     QList<TreeModelItem*> children;
     QHash<QString, TreeModelItem*> hash;
@@ -63,10 +69,14 @@ public:
 signals:
     void dataChanged();
 
+private slots:
+    void onResultsChanged();
+
 private:
     Tomahawk::artist_ptr m_artist;
     Tomahawk::album_ptr m_album;
     Tomahawk::result_ptr m_result;
+    Tomahawk::query_ptr m_query;
 
     bool m_isPlaying;
 };
diff --git a/src/libtomahawk/playlist/treeproxymodel.cpp b/src/libtomahawk/playlist/treeproxymodel.cpp
index 9f73b3b74..0550a9b86 100644
--- a/src/libtomahawk/playlist/treeproxymodel.cpp
+++ b/src/libtomahawk/playlist/treeproxymodel.cpp
@@ -181,7 +181,7 @@ TreeProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent
     TreeModelItem* pi = sourceModel()->itemFromIndex( sourceModel()->index( sourceRow, 0, sourceParent ) );
     Q_ASSERT( pi );
 
-    if ( !pi->result().isNull() )
+    if ( m_model->mode() == TreeModel::Database && !pi->result().isNull() )
     {
         QList< Tomahawk::result_ptr > rl = m_cache.values( sourceParent );
         foreach ( const Tomahawk::result_ptr& result, rl )
@@ -201,7 +201,7 @@ TreeProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent
 
             TreeModelItem* ti = sourceModel()->itemFromIndex( sourceModel()->index( i, 0, sourceParent ) );
 
-            if ( ti->result()->track() == pi->result()->track() &&
+            if ( ti->name() == pi->name() &&
                ( ti->result()->albumpos() == pi->result()->albumpos() || ti->result()->albumpos() == 0 ) )
             {
                 if ( !pi->result()->isOnline() && ti->result()->isOnline() )
@@ -227,13 +227,9 @@ TreeProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent
     QStringList sl = m_filter.split( " ", QString::SkipEmptyParts );
     foreach( const QString& s, sl )
     {
-        QString album;
-        if ( !pi->result()->album().isNull() )
-            album = pi->result()->album()->name();
-
-        if ( !pi->result()->track().contains( s, Qt::CaseInsensitive ) &&
-             !album.contains( s, Qt::CaseInsensitive ) &&
-             !pi->result()->album()->artist()->name().contains( s, Qt::CaseInsensitive ) )
+        if ( !pi->name().contains( s, Qt::CaseInsensitive ) &&
+             !pi->albumName().contains( s, Qt::CaseInsensitive ) &&
+             !pi->artistName().contains( s, Qt::CaseInsensitive ) )
         {
             return false;
         }
@@ -254,6 +250,11 @@ TreeProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) co
     if ( !p2 )
         return false;
 
+    if ( !p1->result().isNull() && p2->result().isNull() )
+        return true;
+    if ( p1->result().isNull() && !p2->result().isNull() )
+        return false;
+
     const QString& lefts = textForItem( p1 );
     const QString& rights = textForItem( p2 );
 
@@ -370,6 +371,10 @@ TreeProxyModel::textForItem( TreeModelItem* item ) const
     {
         return DatabaseImpl::sortname( item->result()->track() );
     }
+    else if ( !item->query().isNull() )
+    {
+        return item->query()->track();
+    }
 
     return QString();
 }
diff --git a/src/libtomahawk/widgets/HeaderWidget.cpp b/src/libtomahawk/widgets/HeaderWidget.cpp
index 94dd03ca4..fb126db72 100644
--- a/src/libtomahawk/widgets/HeaderWidget.cpp
+++ b/src/libtomahawk/widgets/HeaderWidget.cpp
@@ -18,25 +18,29 @@
 
 #include "HeaderWidget.h"
 
-
 #include "utils/stylehelper.h"
 
 #include <QStyle>
 #include <QStylePainter>
 #include <QStyleOption>
 
-HeaderWidget::HeaderWidget(QWidget *parent) : QWidget(parent)
+
+HeaderWidget::HeaderWidget( QWidget* parent )
+    : QWidget( parent )
 {
 }
 
+
 HeaderWidget::~HeaderWidget()
 {
 }
 
-void HeaderWidget::paintEvent(QPaintEvent *e)
+
+void
+HeaderWidget::paintEvent( QPaintEvent* e )
 {
-    QStylePainter p(this);
+    QStylePainter p( this );
     QRect r = e->rect();
 
-    StyleHelper::horizontalHeader(&p, r);
+    StyleHelper::horizontalHeader( &p, r );
 }
diff --git a/src/libtomahawk/widgets/HeaderWidget.h b/src/libtomahawk/widgets/HeaderWidget.h
index be13a276c..1912d9185 100644
--- a/src/libtomahawk/widgets/HeaderWidget.h
+++ b/src/libtomahawk/widgets/HeaderWidget.h
@@ -25,7 +25,6 @@
 
 #include "dllmacro.h"
 
-
 /**
  * \class HeaderWidget
  * \brief A QWidget subclass with a background for use in headers.
@@ -35,16 +34,10 @@ class DLLEXPORT HeaderWidget : public QWidget
 Q_OBJECT
 
 public:
-    HeaderWidget(QWidget *parent = 0);
+    HeaderWidget( QWidget* parent = 0 );
     virtual ~HeaderWidget();
 
-    virtual void paintEvent(QPaintEvent *);
-
-
-private:
-
-
+    virtual void paintEvent( QPaintEvent* );
 };
 
-
 #endif
diff --git a/src/libtomahawk/widgets/combobox.cpp b/src/libtomahawk/widgets/combobox.cpp
index 6a53b8fb5..e02016cf1 100644
--- a/src/libtomahawk/widgets/combobox.cpp
+++ b/src/libtomahawk/widgets/combobox.cpp
@@ -18,7 +18,6 @@
 
 #include "combobox.h"
 
-
 #include "utils/stylehelper.h"
 
 #include <QStyle>
@@ -26,36 +25,41 @@
 #include <QStylePainter>
 #include <QStyleOptionComboBox>
 
-ComboBox::ComboBox(QWidget *parent) : QComboBox(parent)
+
+ComboBox::ComboBox( QWidget* parent )
+    : QComboBox( parent )
 {
 }
 
+
 ComboBox::~ComboBox()
 {
 }
 
-void ComboBox::paintEvent(QPaintEvent *)
+
+void
+ComboBox::paintEvent( QPaintEvent* )
 {
-    QStylePainter p(this);
-    p.setPen(palette().color(QPalette::Text));
+    QStylePainter p( this );
+    p.setPen( palette().color( QPalette::Text ) );
     QStyleOptionComboBox cb;
-    initStyleOption(&cb);
+    initStyleOption( &cb );
     QRect r = cb.rect;
 
+    StyleHelper::horizontalHeader( &p, r );
 
-    StyleHelper::horizontalHeader(&p, r);
-   
-    if( cb.state & QStyle::State_MouseOver ) {
-        QRect highlightRect(r);
-        QSize shrink(3,4);
-        QSize hS(highlightRect.size());
+    if ( cb.state & QStyle::State_MouseOver )
+    {
+        QRect highlightRect( r );
+        QSize shrink( 3, 4 );
+        QSize hS( highlightRect.size() );
         hS -= shrink;
-        highlightRect.setSize(hS);
-        highlightRect.translate(0,2);
+        highlightRect.setSize( hS );
+        highlightRect.translate( 0, 2 );
         p.save();
-        p.setRenderHint(QPainter::Antialiasing);
+        p.setRenderHint( QPainter::Antialiasing );
         p.setBrush( StyleHelper::headerHighlightColor() );
-        p.drawRoundedRect(highlightRect, 10.0, 10.0);
+        p.drawRoundedRect( highlightRect, 10.0, 10.0 );
         p.restore();
     }
 
@@ -65,15 +69,13 @@ void ComboBox::paintEvent(QPaintEvent *)
     p.setBrush( StyleHelper::headerTextColor() );
     p.drawText( r, cb.currentText, to );
 
-
     bool reverse = cb.direction == Qt::RightToLeft;
     int menuButtonWidth = 12;
     int left = !reverse ? r.right() - menuButtonWidth : r.left();
     int right = !reverse ? r.right() : r.left() + menuButtonWidth;
-    QRect arrowRect((left + right) / 2 + (reverse ? 6 : -6), r.center().y() - 3, 9, 9);
+    QRect arrowRect( ( left + right ) / 2 + ( reverse ? 6 : -6 ), r.center().y() - 3, 9, 9 ); //FIXME: no consts please
 
     QStyleOption arrowOpt = cb;
     arrowOpt.rect = arrowRect;
-    StyleHelper::drawArrow(QStyle::PE_IndicatorArrowDown, &p, &arrowOpt);
-
+    StyleHelper::drawArrow( QStyle::PE_IndicatorArrowDown, &p, &arrowOpt );
 }
diff --git a/src/libtomahawk/widgets/combobox.h b/src/libtomahawk/widgets/combobox.h
index 2cf672a6d..18176bf87 100644
--- a/src/libtomahawk/widgets/combobox.h
+++ b/src/libtomahawk/widgets/combobox.h
@@ -33,16 +33,10 @@ class DLLEXPORT ComboBox : public QComboBox
 Q_OBJECT
 
 public:
-    ComboBox(QWidget *parent = 0);
+    ComboBox( QWidget* parent = 0 );
     virtual ~ComboBox();
 
-    virtual void paintEvent(QPaintEvent *);
-
-
-private:
-
-
+    virtual void paintEvent( QPaintEvent* );
 };
 
-
 #endif
diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp
index 1e0fb7890..6f03385e6 100644
--- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp
+++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp
@@ -26,6 +26,7 @@
 #include "database/databasecommand_alltracks.h"
 #include "database/databasecommand_allalbums.h"
 
+#include "utils/stylehelper.h"
 #include "utils/tomahawkutils.h"
 #include "utils/logger.h"
 
@@ -52,8 +53,11 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget*
     TomahawkUtils::unmarginLayout( layout() );
     TomahawkUtils::unmarginLayout( ui->layoutWidget->layout() );
     TomahawkUtils::unmarginLayout( ui->layoutWidget1->layout() );
+    TomahawkUtils::unmarginLayout( ui->layoutWidget2->layout() );
+    TomahawkUtils::unmarginLayout( ui->albumHeader->layout() );
 
     m_albumsModel = new TreeModel( ui->albums );
+    m_albumsModel->setMode( TreeModel::InfoSystem );
     ui->albums->setTreeModel( m_albumsModel );
 
     m_relatedModel = new TreeModel( ui->relatedArtists );
@@ -64,8 +68,14 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget*
     m_topHitsModel->setStyle( TrackModel::Short );
     ui->topHits->setTrackModel( m_topHitsModel );
 
+    ui->albumHeader->setContentsMargins( 0, 0, 4, 0 );
+    ui->button->setFixedWidth( 200 );
+    ui->button->setDown( true );
+
     m_pixmap = QPixmap( RESPATH "images/no-album-art-placeholder.png" ).scaledToWidth( 48, Qt::SmoothTransformation );
 
+    connect( ui->button, SIGNAL( toggled( bool ) ), SLOT( onModeToggle( bool ) ) );
+
     connect( Tomahawk::InfoSystem::InfoSystem::instance(),
              SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
              SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) );
@@ -82,9 +92,19 @@ ArtistInfoWidget::~ArtistInfoWidget()
 }
 
 
+void
+ArtistInfoWidget::onModeToggle( bool officialReleases )
+{
+    m_albumsModel->setMode( officialReleases ? TreeModel::InfoSystem : TreeModel::Database );
+    m_albumsModel->clear();
+    m_albumsModel->addAlbums( m_artist, QModelIndex() );
+}
+
+
 void
 ArtistInfoWidget::load( const artist_ptr& artist )
 {
+    m_artist = artist;
     m_title = artist->name();
     m_albumsModel->addAlbums( artist, QModelIndex() );
 
@@ -109,9 +129,6 @@ ArtistInfoWidget::load( const artist_ptr& artist )
 
     requestData.type = Tomahawk::InfoSystem::InfoArtistSongs;
     Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
-
-    requestData.type = Tomahawk::InfoSystem::InfoArtistReleases;
-    Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
 }
 
 
diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h
index b9e97259d..a5e49d2d4 100644
--- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h
+++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h
@@ -90,6 +90,8 @@ private slots:
     void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output );
     void infoSystemFinished( QString target );
 
+    void onModeToggle( bool officialReleases );
+
 private:
     Ui::ArtistInfoWidget *ui;
 
diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui
index aa6b45159..bc6115712 100644
--- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui
+++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui
@@ -66,13 +66,26 @@
        </layout>
       </widget>
      </widget>
-     <widget class="QWidget" name="">
-      <layout class="QVBoxLayout" name="verticalLayout">
+     <widget class="QWidget" name="layoutWidget2">
+      <layout class="QVBoxLayout" name="verticalLayout_5">
        <item>
-        <widget class="HeaderLabel" name="label_3">
-         <property name="text">
-          <string>Albums</string>
-         </property>
+        <widget class="HeaderWidget" name="albumHeader" native="true">
+         <layout class="QHBoxLayout" name="horizontalLayout">
+          <item>
+           <widget class="HeaderLabel" name="label_3">
+            <property name="text">
+             <string>Albums</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="ToggleButton" name="button">
+            <property name="text">
+             <string>Official Releases Only</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
         </widget>
        </item>
        <item>
@@ -95,6 +108,16 @@
    <extends>QLabel</extends>
    <header location="global">widgets/HeaderLabel.h</header>
   </customwidget>
+  <customwidget>
+   <class>HeaderWidget</class>
+   <extends>QWidget</extends>
+   <header location="global">widgets/HeaderWidget.h</header>
+  </customwidget>
+  <customwidget>
+   <class>ToggleButton</class>
+   <extends>QPushButton</extends>
+   <header location="global">widgets/ToggleButton.h</header>
+  </customwidget>
   <customwidget>
    <class>ArtistView</class>
    <extends>QTreeView</extends>