From 79d8b081d773c6ff37da8761c7f7910ea4d4e4bf Mon Sep 17 00:00:00 2001
From: Christian Muehlhaeuser <muesli@gmail.com>
Date: Thu, 23 Feb 2012 03:21:21 +0100
Subject: [PATCH] * Speed up cover loading in TreeView.

---
 src/audiocontrols.cpp                         |  6 +--
 src/libtomahawk/album.cpp                     | 36 ++++++++++++++--
 src/libtomahawk/album.h                       | 14 +++++-
 src/libtomahawk/artist.cpp                    | 36 ++++++++++++++--
 src/libtomahawk/artist.h                      | 14 +++++-
 src/libtomahawk/audio/audioengine.cpp         |  4 +-
 .../playlist/albumitemdelegate.cpp            | 19 ++------
 src/libtomahawk/playlist/albumitemdelegate.h  |  1 -
 src/libtomahawk/playlist/artistview.cpp       | 43 +++++++++++++++++++
 src/libtomahawk/playlist/artistview.h         |  3 ++
 src/libtomahawk/playlist/treeitemdelegate.cpp | 16 ++-----
 src/libtomahawk/playlist/treeitemdelegate.h   |  2 -
 src/libtomahawk/playlist/treemodel.cpp        | 12 ++++++
 src/libtomahawk/playlist/treemodel.h          |  2 +
 .../widgets/infowidgets/AlbumInfoWidget.cpp   |  4 +-
 .../widgets/infowidgets/ArtistInfoWidget.cpp  |  4 +-
 src/tomahawkapp.cpp                           |  5 +--
 17 files changed, 165 insertions(+), 56 deletions(-)

diff --git a/src/audiocontrols.cpp b/src/audiocontrols.cpp
index f536be7c6..20c7637c9 100644
--- a/src/audiocontrols.cpp
+++ b/src/audiocontrols.cpp
@@ -264,11 +264,11 @@ AudioControls::onAlbumCoverUpdated()
 void
 AudioControls::setAlbumCover()
 {
-    if ( !m_currentTrack->album()->cover().isNull() )
+    if ( !m_currentTrack->album()->cover( ui->coverImage->size() ).isNull() )
     {
         QPixmap cover;
-        cover.loadFromData( m_currentTrack->album()->cover() );
-        ui->coverImage->setPixmap( cover.scaled( ui->coverImage->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ) );
+        cover = m_currentTrack->album()->cover( ui->coverImage->size() );
+        ui->coverImage->setPixmap( cover );
     }
     else
         ui->coverImage->setPixmap( m_defaultCover );
diff --git a/src/libtomahawk/album.cpp b/src/libtomahawk/album.cpp
index fd56e8409..846dc15a6 100644
--- a/src/libtomahawk/album.cpp
+++ b/src/libtomahawk/album.cpp
@@ -32,6 +32,7 @@ using namespace Tomahawk;
 
 Album::~Album()
 {
+    delete m_cover;
 }
 
 
@@ -71,6 +72,7 @@ Album::Album( unsigned int id, const QString& name, const Tomahawk::artist_ptr&
     , m_id( id )
     , m_name( name )
     , m_artist( artist )
+    , m_cover( 0 )
     , m_infoLoaded( false )
 {
     connect( Tomahawk::InfoSystem::InfoSystem::instance(),
@@ -97,11 +99,14 @@ Album::artist() const
 }
 
 
-QByteArray
-Album::cover() const
+#ifndef ENABLE_HEADLESS
+QPixmap
+Album::cover( const QSize& size, bool forceLoad ) const
 {
     if ( !m_infoLoaded )
     {
+        if ( !forceLoad )
+            return QPixmap();
         m_uuid = uuid();
 
         Tomahawk::InfoSystem::InfoStringHash trackInfo;
@@ -117,8 +122,31 @@ Album::cover() const
         Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
     }
 
-    return m_cover;
+    if ( !m_cover )
+        m_cover = new QPixmap();
+
+    if ( m_cover->isNull() && !m_coverBuffer.isEmpty() )
+    {
+        m_cover->loadFromData( m_coverBuffer );
+    }
+
+    if ( !m_cover->isNull() && !size.isEmpty() )
+    {
+        if ( m_coverCache.contains( size.width() ) )
+        {
+            return m_coverCache.value( size.width() );
+        }
+        else
+        {
+            QPixmap scaledCover;
+            scaledCover = m_cover->scaled( size, Qt::KeepAspectRatio, Qt::SmoothTransformation );
+            m_coverCache.insert( size.width(), scaledCover );
+        }
+    }
+
+    return *m_cover;
 }
+#endif
 
 
 void
@@ -137,7 +165,7 @@ Album::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVaria
         const QByteArray ba = returnedData["imgbytes"].toByteArray();
         if ( ba.length() )
         {
-            m_cover = ba;
+            m_coverBuffer = ba;
         }
     }
 
diff --git a/src/libtomahawk/album.h b/src/libtomahawk/album.h
index b929e9b49..741c7d65a 100644
--- a/src/libtomahawk/album.h
+++ b/src/libtomahawk/album.h
@@ -19,8 +19,13 @@
 #ifndef TOMAHAWKALBUM_H
 #define TOMAHAWKALBUM_H
 
+#include "config.h"
+
 #include <QtCore/QObject>
 #include <QtCore/QSharedPointer>
+#ifndef ENABLE_HEADLESS
+    #include <QtGui/QPixmap>
+#endif
 
 #include "typedefs.h"
 #include "playlistinterface.h"
@@ -44,7 +49,9 @@ public:
     unsigned int id() const { return m_id; }
     QString name() const { return m_name; }
     artist_ptr artist() const;
-    QByteArray cover() const;
+#ifndef ENABLE_HEADLESS
+    QPixmap cover( const QSize& size, bool forceLoad = true ) const;
+#endif
     bool infoLoaded() const { return m_infoLoaded; }
 
     Tomahawk::playlistinterface_ptr playlistInterface();
@@ -64,10 +71,13 @@ private:
     unsigned int m_id;
     QString m_name;
     artist_ptr m_artist;
-    QByteArray m_cover;
+    QByteArray m_coverBuffer;
+    mutable QPixmap* m_cover;
     bool m_infoLoaded;
     mutable QString m_uuid;
 
+    mutable QHash< int, QPixmap > m_coverCache;
+
     Tomahawk::playlistinterface_ptr m_playlistInterface;
 };
 
diff --git a/src/libtomahawk/artist.cpp b/src/libtomahawk/artist.cpp
index 0c0ba332c..534e15825 100644
--- a/src/libtomahawk/artist.cpp
+++ b/src/libtomahawk/artist.cpp
@@ -31,6 +31,7 @@ using namespace Tomahawk;
 
 Artist::~Artist()
 {
+    delete m_cover;
 }
 
 
@@ -69,6 +70,7 @@ Artist::Artist( unsigned int id, const QString& name )
     : QObject()
     , m_id( id )
     , m_name( name )
+    , m_cover( 0 )
     , m_infoLoaded( false )
 {
     m_sortname = DatabaseImpl::sortname( name, true );
@@ -89,11 +91,14 @@ Artist::onTracksAdded( const QList<Tomahawk::query_ptr>& tracks )
 }
 
 
-QByteArray
-Artist::cover() const
+#ifndef ENABLE_HEADLESS
+QPixmap
+Artist::cover( const QSize& size, bool forceLoad ) const
 {
     if ( !m_infoLoaded )
     {
+        if ( !forceLoad )
+            return QPixmap();
         m_uuid = uuid();
 
         Tomahawk::InfoSystem::InfoStringHash trackInfo;
@@ -108,8 +113,31 @@ Artist::cover() const
         Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
     }
 
-    return m_cover;
+    if ( !m_cover )
+        m_cover = new QPixmap();
+
+    if ( m_cover->isNull() && !m_coverBuffer.isEmpty() )
+    {
+        m_cover->loadFromData( m_coverBuffer );
+    }
+
+    if ( !m_cover->isNull() && !size.isEmpty() )
+    {
+        if ( m_coverCache.contains( size.width() ) )
+        {
+            return m_coverCache.value( size.width() );
+        }
+        else
+        {
+            QPixmap scaledCover;
+            scaledCover = m_cover->scaled( size, Qt::KeepAspectRatio, Qt::SmoothTransformation );
+            m_coverCache.insert( size.width(), scaledCover );
+        }
+    }
+
+    return *m_cover;
 }
+#endif
 
 
 void
@@ -128,7 +156,7 @@ Artist::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVari
         const QByteArray ba = returnedData["imgbytes"].toByteArray();
         if ( ba.length() )
         {
-            m_cover = ba;
+            m_coverBuffer = ba;
         }
     }
 
diff --git a/src/libtomahawk/artist.h b/src/libtomahawk/artist.h
index 3e1442023..05efe48eb 100644
--- a/src/libtomahawk/artist.h
+++ b/src/libtomahawk/artist.h
@@ -19,8 +19,13 @@
 #ifndef TOMAHAWKARTIST_H
 #define TOMAHAWKARTIST_H
 
+#include "config.h"
+
 #include <QtCore/QObject>
 #include <QtCore/QSharedPointer>
+#ifndef ENABLE_HEADLESS
+    #include <QtGui/QPixmap>
+#endif
 
 #include "typedefs.h"
 #include "dllmacro.h"
@@ -43,7 +48,9 @@ public:
     unsigned int id() const { return m_id; }
     QString name() const { return m_name; }
     QString sortname() const { return m_sortname; }
-    QByteArray cover() const;
+#ifndef ENABLE_HEADLESS
+    QPixmap cover( const QSize& size, bool forceLoad = true ) const;
+#endif
     bool infoLoaded() const { return m_infoLoaded; }
 
     Tomahawk::playlistinterface_ptr playlistInterface();
@@ -63,10 +70,13 @@ private:
     unsigned int m_id;
     QString m_name;
     QString m_sortname;
-    QByteArray m_cover;
+    QByteArray m_coverBuffer;
+    mutable QPixmap* m_cover;
     bool m_infoLoaded;
     mutable QString m_uuid;
 
+    mutable QHash< int, QPixmap > m_coverCache;
+
     Tomahawk::playlistinterface_ptr m_playlistInterface;
 };
 
diff --git a/src/libtomahawk/audio/audioengine.cpp b/src/libtomahawk/audio/audioengine.cpp
index 226d99a3f..256b8603f 100644
--- a/src/libtomahawk/audio/audioengine.cpp
+++ b/src/libtomahawk/audio/audioengine.cpp
@@ -333,7 +333,7 @@ AudioEngine::sendNowPlayingNotification()
     else
     {
         connect( m_currentTrack->album().data(), SIGNAL( updated() ), SLOT( onNowPlayingInfoReady() ), Qt::UniqueConnection );
-        m_currentTrack->album()->cover();
+        m_currentTrack->album()->cover( QSize( 0, 0 ) );
     }
 }
 
@@ -358,7 +358,7 @@ AudioEngine::onNowPlayingInfoReady()
     if ( !m_currentTrack->album().isNull() )
     {
         QImage cover;
-        cover.loadFromData( m_currentTrack->album()->cover() );
+        cover = m_currentTrack->album()->cover( QSize( 0, 0 ) ).toImage();
         playInfo["image"] = QVariant( cover );
     }
 
diff --git a/src/libtomahawk/playlist/albumitemdelegate.cpp b/src/libtomahawk/playlist/albumitemdelegate.cpp
index e28208248..f6baeceba 100644
--- a/src/libtomahawk/playlist/albumitemdelegate.cpp
+++ b/src/libtomahawk/playlist/albumitemdelegate.cpp
@@ -89,21 +89,20 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
         painter->drawLine( shadowRect.bottomLeft() + QPoint( 0, 4 ), shadowRect.bottomRight() + QPoint( 0, 4 ) );
     }
 
+    QRect r = option.rect.adjusted( 6, 5, -6, -41 );
     QPixmap cover;
     if ( !item->album().isNull() )
     {
-        cover.loadFromData( item->album()->cover() );
+        cover = item->album()->cover( r.size() );
     }
     else if ( !item->artist().isNull() )
     {
-        cover.loadFromData( item->artist()->cover() );
+        cover = item->artist()->cover( r.size() );
     }
 
     if ( cover.isNull() )
         cover = m_defaultCover;
 
-    QRect r = option.rect.adjusted( 6, 5, -6, -41 );
-
     if ( option.state & QStyle::State_Selected )
     {
 #if defined(Q_WS_MAC) || defined(Q_WS_WIN)
@@ -123,17 +122,7 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
 #endif
     }
 
-    QPixmap scover;
-    if ( m_cache.contains( cover.cacheKey() ) )
-    {
-        scover = m_cache.value( cover.cacheKey() );
-    }
-    else
-    {
-        scover = cover.scaled( r.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation );
-        m_cache.insert( cover.cacheKey(), scover );
-    }
-    painter->drawPixmap( r, scover );
+    painter->drawPixmap( r, cover );
 
     painter->setPen( opt.palette.color( QPalette::Text ) );
     QTextOption to;
diff --git a/src/libtomahawk/playlist/albumitemdelegate.h b/src/libtomahawk/playlist/albumitemdelegate.h
index 86b31bdf5..43de6e0e8 100644
--- a/src/libtomahawk/playlist/albumitemdelegate.h
+++ b/src/libtomahawk/playlist/albumitemdelegate.h
@@ -49,7 +49,6 @@ private:
     QAbstractItemView* m_view;
     AlbumProxyModel* m_model;
 
-    mutable QHash< qint64, QPixmap > m_cache;
     mutable QHash< QPersistentModelIndex, QRect > m_artistNameRects;
     QPersistentModelIndex m_hoveringOver;
 
diff --git a/src/libtomahawk/playlist/artistview.cpp b/src/libtomahawk/playlist/artistview.cpp
index 808b6c8c5..ff83b2eeb 100644
--- a/src/libtomahawk/playlist/artistview.cpp
+++ b/src/libtomahawk/playlist/artistview.cpp
@@ -80,6 +80,11 @@ ArtistView::ArtistView( QWidget* parent )
     setFont( f );
     #endif
 
+    m_timer.setInterval( SCROLL_TIMEOUT );
+    connect( verticalScrollBar(), SIGNAL( rangeChanged( int, int ) ), SLOT( onViewChanged() ) );
+    connect( verticalScrollBar(), SIGNAL( valueChanged( int ) ), SLOT( onViewChanged() ) );
+    connect( &m_timer, SIGNAL( timeout() ), SLOT( onScrollTimeout() ) );
+
     connect( this, SIGNAL( doubleClicked( QModelIndex ) ), SLOT( onItemActivated( QModelIndex ) ) );
     connect( this, SIGNAL( customContextMenuRequested( const QPoint& ) ), SLOT( onCustomContextMenu( const QPoint& ) ) );
     connect( m_contextMenu, SIGNAL( triggered( int ) ), SLOT( onMenuTriggered( int ) ) );
@@ -129,6 +134,7 @@ ArtistView::setTreeModel( TreeModel* model )
 
     connect( m_model, SIGNAL( itemCountChanged( unsigned int ) ), SLOT( onItemCountChanged( unsigned int ) ) );
     connect( m_proxyModel, SIGNAL( filterChanged( QString ) ), SLOT( onFilterChanged( QString ) ) );
+    connect( m_proxyModel, SIGNAL( rowsInserted( QModelIndex, int, int ) ), SLOT( onViewChanged() ) );
 
     guid(); // this will set the guid on the header
 
@@ -145,6 +151,43 @@ ArtistView::setTreeModel( TreeModel* model )
 }
 
 
+void
+ArtistView::onViewChanged()
+{
+    if ( m_timer.isActive() )
+        m_timer.stop();
+
+    m_timer.start();
+}
+
+
+void
+ArtistView::onScrollTimeout()
+{
+    if ( m_timer.isActive() )
+        m_timer.stop();
+
+    QModelIndex left = indexAt( viewport()->rect().topLeft() );
+    while ( left.isValid() && left.parent().isValid() )
+        left = left.parent();
+
+    QModelIndex right = indexAt( viewport()->rect().bottomLeft() );
+    while ( right.isValid() && right.parent().isValid() )
+        right = right.parent();
+
+    int max = m_proxyModel->playlistInterface()->trackCount();
+    if ( right.isValid() )
+        max = right.row() + 1;
+
+    if ( !max )
+        return;
+
+    for ( int i = left.row(); i < max; i++ )
+    {
+        m_model->getCover( m_proxyModel->mapToSource( m_proxyModel->index( i, 0 ) ) );
+    }
+}
+
 void
 ArtistView::currentChanged( const QModelIndex& current, const QModelIndex& previous )
 {
diff --git a/src/libtomahawk/playlist/artistview.h b/src/libtomahawk/playlist/artistview.h
index 8167f9b73..061bca781 100644
--- a/src/libtomahawk/playlist/artistview.h
+++ b/src/libtomahawk/playlist/artistview.h
@@ -95,6 +95,8 @@ private slots:
     void onItemCountChanged( unsigned int items );
     void onFilterChanged( const QString& filter );
     void onFilteringStarted();
+    void onViewChanged();
+    void onScrollTimeout();
 
     void onCustomContextMenu( const QPoint& pos );
     void onMenuTriggered( int action );
@@ -113,6 +115,7 @@ private:
     Tomahawk::ContextMenu* m_contextMenu;
 
     bool m_showModes;
+    QTimer m_timer;
     mutable QString m_guid;
 };
 
diff --git a/src/libtomahawk/playlist/treeitemdelegate.cpp b/src/libtomahawk/playlist/treeitemdelegate.cpp
index 90fbe4aa7..22fc81247 100644
--- a/src/libtomahawk/playlist/treeitemdelegate.cpp
+++ b/src/libtomahawk/playlist/treeitemdelegate.cpp
@@ -155,14 +155,13 @@ TreeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
     QPixmap cover;
     if ( !item->album().isNull() )
     {
-        cover.loadFromData( item->album()->cover() );
+        cover = item->album()->cover( r.size(), false );
     }
     else if ( !item->artist().isNull() )
     {
-        cover.loadFromData( item->artist()->cover() );
+        cover = item->artist()->cover( r.size(), false );
     }
 
-    QPixmap scover;
     if ( cover.isNull() )
     {
         if ( !item->artist().isNull() )
@@ -171,16 +170,7 @@ TreeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
             cover = m_defaultAlbumCover;
     }
 
-    if ( m_cache.contains( cover.cacheKey() ) )
-    {
-        scover = m_cache.value( cover.cacheKey() );
-    }
-    else
-    {
-        scover = cover.scaled( r.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation );
-        m_cache.insert( cover.cacheKey(), scover );
-    }
-    painter->drawPixmap( r, scover );
+    painter->drawPixmap( r, cover );
 
     QTextOption to;
     to.setAlignment( Qt::AlignVCenter );
diff --git a/src/libtomahawk/playlist/treeitemdelegate.h b/src/libtomahawk/playlist/treeitemdelegate.h
index 07fff9423..da84fcec9 100644
--- a/src/libtomahawk/playlist/treeitemdelegate.h
+++ b/src/libtomahawk/playlist/treeitemdelegate.h
@@ -43,8 +43,6 @@ private:
     ArtistView* m_view;
     TreeProxyModel* m_model;
 
-    mutable QHash< qint64, QPixmap > m_cache;
-
     QPixmap m_nowPlayingIcon;
     QPixmap m_defaultAlbumCover;
     QPixmap m_defaultArtistImage;
diff --git a/src/libtomahawk/playlist/treemodel.cpp b/src/libtomahawk/playlist/treemodel.cpp
index 24e3073e0..63f40b3b2 100644
--- a/src/libtomahawk/playlist/treemodel.cpp
+++ b/src/libtomahawk/playlist/treemodel.cpp
@@ -87,6 +87,18 @@ TreeModel::collection() const
 }
 
 
+void
+TreeModel::getCover( const QModelIndex& index )
+{
+    TreeModelItem* item = itemFromIndex( index );
+
+    if ( !item->artist().isNull() && !item->artist()->infoLoaded() )
+        item->artist()->cover( QSize( 0, 0 ) );
+    else if ( !item->album().isNull() && !item->album()->infoLoaded() )
+        item->album()->cover( QSize( 0, 0 ) );
+}
+
+
 void
 TreeModel::setCurrentItem( const QModelIndex& index )
 {
diff --git a/src/libtomahawk/playlist/treemodel.h b/src/libtomahawk/playlist/treemodel.h
index 53578bc8b..fb645cf28 100644
--- a/src/libtomahawk/playlist/treemodel.h
+++ b/src/libtomahawk/playlist/treemodel.h
@@ -98,6 +98,8 @@ public:
     void addAlbums( const Tomahawk::artist_ptr& artist, const QModelIndex& parent, bool autoRefetch = false );
     void addTracks( const Tomahawk::album_ptr& album, const QModelIndex& parent, bool autoRefetch = false );
 
+    void getCover( const QModelIndex& index );
+
     ColumnStyle columnStyle() const { return m_columnStyle; }
     void setColumnStyle( ColumnStyle style );
 
diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp
index 62b048704..c91cb37ab 100644
--- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp
+++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp
@@ -243,10 +243,10 @@ AlbumInfoWidget::loadAlbums( bool autoRefetch )
 void
 AlbumInfoWidget::onAlbumCoverUpdated()
 {
-    if ( m_album->cover().isNull() )
+    if ( m_album->cover( QSize( 0, 0 ) ).isNull() )
         return;
 
-    m_pixmap.loadFromData( m_album->cover() );
+    m_pixmap = m_album->cover( QSize( 0, 0 ) );
     emit pixmapChanged( m_pixmap );
 }
 
diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp
index 178713c04..96e4dfc8b 100644
--- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp
+++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp
@@ -289,10 +289,10 @@ ArtistInfoWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestD
 void
 ArtistInfoWidget::onArtistImageUpdated()
 {
-    if ( m_artist->cover().isNull() )
+    if ( m_artist->cover( QSize( 0, 0 ) ).isNull() )
         return;
 
-    m_pixmap.loadFromData( m_artist->cover() );
+    m_pixmap = m_artist->cover( QSize( 0, 0 ) );
     emit pixmapChanged( m_pixmap );
 }
 
diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp
index 058b7bed1..5855a2b37 100644
--- a/src/tomahawkapp.cpp
+++ b/src/tomahawkapp.cpp
@@ -543,10 +543,7 @@ TomahawkApp::spotifyApiCheckFinished()
     QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() );
     Q_ASSERT( reply );
 
-    if ( reply->error() )
-        DropJob::setCanParseSpotifyPlaylists( false );
-    else
-        DropJob::setCanParseSpotifyPlaylists( true );
+    DropJob::setCanParseSpotifyPlaylists( !reply->error() );
 #endif
 }