From e7cd5bddc46c79e39e940cf46ab6c22e6b30a16e Mon Sep 17 00:00:00 2001
From: Michael Zanetti <mzanetti@kde.org>
Date: Mon, 22 Aug 2011 01:35:15 +0200
Subject: [PATCH] more work on the drag and drop menu

---
 src/CMakeLists.txt                 |   3 +
 src/sourcetree/animationhelper.cpp | 116 ++++++++
 src/sourcetree/animationhelper.h   |  58 ++++
 src/sourcetree/sourcedelegate.cpp  | 417 +++++++++++++++++++++++------
 src/sourcetree/sourcedelegate.h    |  13 +-
 src/sourcetree/sourcetreeview.cpp  |  27 +-
 src/sourcetree/sourcetreeview.h    |   4 +
 7 files changed, 544 insertions(+), 94 deletions(-)
 create mode 100644 src/sourcetree/animationhelper.cpp
 create mode 100644 src/sourcetree/animationhelper.h

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f6a413d21..d6c8d0f81 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -58,6 +58,7 @@ SET( tomahawkSourcesGui ${tomahawkSourcesGui}
      sourcetree/sourcesproxymodel.cpp
      sourcetree/sourcetreeview.cpp
      sourcetree/sourcedelegate.cpp
+     sourcetree/animationhelper.cpp
      sourcetree/items/sourcetreeitem.cpp
      sourcetree/items/collectionitem.cpp
      sourcetree/items/playlistitems.cpp
@@ -103,6 +104,8 @@ SET( tomahawkHeadersGui ${tomahawkHeadersGui}
      sourcetree/sourcesmodel.h
      sourcetree/sourcesproxymodel.h
      sourcetree/sourcetreeview.h
+     sourcetree/sourcedelegate.h
+     sourcetree/animationhelper.h
      sourcetree/items/sourcetreeitem.h
      sourcetree/items/collectionitem.h
      sourcetree/items/playlistitems.h
diff --git a/src/sourcetree/animationhelper.cpp b/src/sourcetree/animationhelper.cpp
new file mode 100644
index 000000000..3b15b3a44
--- /dev/null
+++ b/src/sourcetree/animationhelper.cpp
@@ -0,0 +1,116 @@
+#include "animationhelper.h"
+
+#include "QDebug"
+
+AnimationHelper::AnimationHelper( const QModelIndex& index, QObject *parent )
+    :QObject( parent )
+    , m_index( index )
+    , m_fullyExpanded( false )
+    , m_expandAnimation( 0 )
+    , m_forceClosing( false )
+{
+    m_expandTimer.setSingleShot( true );
+    m_expandTimer.setInterval( 1000 );
+    connect( &m_expandTimer, SIGNAL(timeout()), SLOT(expandTimeout()));
+
+    m_collapseTimer.setSingleShot( true );
+    m_collapseTimer.setInterval( 1000 );
+    connect( &m_collapseTimer, SIGNAL(timeout()), SLOT(collapseTimeout()));
+}
+
+bool AnimationHelper::initialized() const
+{
+    return m_expandAnimation != 0;
+}
+
+void AnimationHelper::initialize( const QSize& startValue, const QSize& endValue, int duration )
+{
+    m_size = startValue;
+    m_targetSize = endValue;
+    m_startSize = startValue;
+
+    m_expandAnimation = new QPropertyAnimation( this, "size", this );
+    m_expandAnimation->setStartValue( startValue );
+    m_expandAnimation->setEndValue( endValue );
+    m_expandAnimation->setDuration( duration );
+    m_expandAnimation->setEasingCurve( QEasingCurve::OutBounce );
+    qDebug() << "starting animation" << startValue << endValue << duration;
+    connect( m_expandAnimation, SIGNAL( finished() ), SLOT(expandAnimationFinished()));
+
+    m_collapseAnimation= new QPropertyAnimation( this, "size", this );
+    m_collapseAnimation->setStartValue( endValue );
+    m_collapseAnimation->setEndValue( startValue );
+    m_collapseAnimation->setDuration( duration );
+    m_collapseAnimation->setEasingCurve( QEasingCurve::OutBounce );
+    connect( m_collapseAnimation, SIGNAL( finished() ), SLOT(collapseAnimationFinished()));
+
+}
+
+void AnimationHelper::setSize( const QSize& size )
+{
+    m_size = size;
+    emit sizeChanged();
+    qDebug() << "animaton setting size to" << size;
+}
+
+void AnimationHelper::expand()
+{
+    m_collapseTimer.stop();
+    m_expandTimer.start();
+}
+
+void AnimationHelper::collapse( bool immediately )
+{
+    if ( immediately )
+    {
+        m_fullyExpanded = false;
+        m_forceClosing = true;
+        m_collapseAnimation->start();
+    }
+    else
+    {
+        //m_fullyExpanded = false;
+        m_expandTimer.stop();
+        m_collapseTimer.start();
+    }
+}
+
+bool AnimationHelper::partlyExpanded()
+{
+    if ( m_forceClosing )
+        return false;
+
+    return m_fullyExpanded
+            || ( m_expandAnimation->state() == QPropertyAnimation::Running && m_expandAnimation->currentTime() > 250 )
+            || ( m_collapseAnimation->state() == QPropertyAnimation::Running && m_collapseAnimation->currentTime() < 100 );
+}
+
+bool AnimationHelper::fullyExpanded()
+{
+    return m_fullyExpanded;
+}
+
+void AnimationHelper::expandTimeout()
+{
+    m_expandAnimation->start();
+//        m_fullyExpanded = true;
+}
+
+void AnimationHelper::collapseTimeout()
+{
+//        m_size = m_startSize;
+        m_fullyExpanded = false;
+//        emit finished( m_index );
+    m_collapseAnimation->start();
+}
+
+void AnimationHelper::expandAnimationFinished()
+{
+    m_fullyExpanded = true;
+}
+
+void AnimationHelper::collapseAnimationFinished()
+{
+    m_fullyExpanded = false;
+    emit finished( m_index );
+}
diff --git a/src/sourcetree/animationhelper.h b/src/sourcetree/animationhelper.h
new file mode 100644
index 000000000..cc46013df
--- /dev/null
+++ b/src/sourcetree/animationhelper.h
@@ -0,0 +1,58 @@
+#ifndef ANIMATIONHELPER_H
+#define ANIMATIONHELPER_H
+
+#include <QObject>
+#include <QModelIndex>
+#include <QSize>
+#include <QTimer>
+#include <QPropertyAnimation>
+
+class AnimationHelper: public QObject
+{
+    Q_OBJECT
+    Q_PROPERTY( QSize size READ size WRITE setSize NOTIFY sizeChanged )
+
+public:
+    AnimationHelper( const QModelIndex& index, QObject *parent = 0 );
+
+    QSize originalSize() const { return m_startSize; }
+    QSize size() const { return m_size; }
+
+    bool initialized() const;
+    void initialize( const QSize& startValue, const QSize& endValue, int duration );
+
+    void setSize( const QSize& size );
+
+    void expand();
+    void collapse( bool immediately = false );
+
+    bool partlyExpanded();
+    bool fullyExpanded();
+
+signals:
+    void sizeChanged();
+    void finished( const QModelIndex& index);
+
+private slots:
+    void expandTimeout();
+    void collapseTimeout();
+    void expandAnimationFinished();
+    void collapseAnimationFinished();
+
+private:
+    QModelIndex m_index;
+    QSize m_size;
+    QSize m_targetSize;
+    QSize m_startSize;
+
+    QTimer m_expandTimer;
+    QTimer m_collapseTimer;
+
+    bool m_fullyExpanded;
+    bool m_forceClosing;
+
+    QPropertyAnimation *m_expandAnimation;
+    QPropertyAnimation *m_collapseAnimation;
+};
+
+#endif // ANIMATIONHELPER_H
diff --git a/src/sourcetree/sourcedelegate.cpp b/src/sourcetree/sourcedelegate.cpp
index 17cbef2d3..a059a8504 100644
--- a/src/sourcetree/sourcedelegate.cpp
+++ b/src/sourcetree/sourcedelegate.cpp
@@ -7,6 +7,7 @@
 
 #include "utils/tomahawkutils.h"
 #include "items/temporarypageitem.h"
+#include "animationhelper.h"
 
 #include <QApplication>
 #include <QPainter>
@@ -14,6 +15,12 @@
 
 #define TREEVIEW_INDENT_ADD -7
 
+SourceDelegate::SourceDelegate( QAbstractItemView* parent )
+    : QStyledItemDelegate( parent )
+    , m_parent( parent )
+{
+}
+
 QSize
 SourceDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const
 {
@@ -21,11 +28,19 @@ SourceDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex&
 
     if ( index.data( SourcesModel::SourceTreeItemTypeRole ).toInt() == SourcesModel::Collection )
         return QSize( option.rect.width(), 44 );
-    else if ( index == m_dropHoverIndex )
+    else if ( m_expandedMap.contains( index ) )
     {
-        QSize originalSize = QStyledItemDelegate::sizeHint( option, index );
-        qDebug() << "droptypecount is" << dropTypeCount( item );
-        return originalSize + QSize( 0, originalSize.height() * dropTypeCount( item ) );
+        if ( !m_expandedMap.value( index )->initialized() )
+        {
+            qDebug() << "droptypecount is " << dropTypeCount( item );
+            QSize originalSize = QStyledItemDelegate::sizeHint( option, index );
+//            QSize targetSize = originalSize + QSize( 0, originalSize.height() * dropTypeCount( item ) ); // useful for vertical menu
+            QSize targetSize = originalSize + QSize( 0, 56 );
+            m_expandedMap.value( index )->initialize( originalSize, targetSize, 500 );
+            m_expandedMap.value( index )->expand();
+        }
+        QMetaObject::invokeMethod( m_parent, "update", Qt::QueuedConnection, Q_ARG( QModelIndex, index ) );
+        return m_expandedMap.value( index )->size();
     }
     else
         return QStyledItemDelegate::sizeHint( option, index );
@@ -141,105 +156,298 @@ SourceDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, co
     }
     else if ( type == SourcesModel::StaticPlaylist || type == SourcesModel::CategoryAdd )
     {
+        if ( !( m_expandedMap.contains( index) && m_expandedMap.value( index )->partlyExpanded() && dropTypeCount( item ) > 0 ) )
+        {
+            QStyledItemDelegate::paint( painter, option, index );
+            return;
+        }
+
+        // Let Qt paint the original item. We add our stuff after it
+        QStyleOptionViewItem o = option;
+        o.rect.adjust( 0, 0, 0, - option.rect.height() + m_expandedMap.value( index )->originalSize().height() + 2 );
+        QStyledItemDelegate::paint( painter, o, index );
+
         painter->save();
 
-        QFont bold = painter->font();
-        bold.setBold( true );
 
-        QString name = index.data().toString();
-        if ( type == SourcesModel::StaticPlaylist )
+        // Get whole rect for the menu
+        QRect itemsRect = option.rect.adjusted( -option.rect.x(), m_expandedMap.value( index )->originalSize().height(), 0, 0 );
+
+        // draw the background
+
+        QLinearGradient linearGradient( 0, 0, 0, itemsRect.height() );
+        linearGradient.setColorAt( 0.0, QColor( 0xdb, 0x1b, 0x06 ) );
+        linearGradient.setColorAt( 1.0, QColor( 0xf4, 0x17, 0x05 ) );
+        painter->setBrush( linearGradient );
+        painter->drawRect( itemsRect );
+
+        int totalCount = dropTypeCount( item );
+        int itemWidth = itemsRect.width() / totalCount;
+        int iconSpacing = ( itemWidth - 32 ) / 2;
+
+        // adjust to one single entry
+        itemsRect.adjust( 0, 0, -itemsRect.width() + itemWidth, 0 );
+
+        int count = 0;
+
+        QPoint cursorPos = m_parent->mapFromGlobal( QCursor::pos() );
+
+        QPen pen(Qt::white);
+        painter->setPen(pen);
+
+        QFont font = painter->font();
+        font.setPixelSize( 10 );
+        painter->setFont( font );
+        QFont fontBold = painter->font();
+        fontBold.setBold( true );
+
+        QString text;
+        QRect textRect;
+        QPixmap icon = QPixmap( ":/data/images/new-additions.png" ).scaledToHeight( 32, Qt::SmoothTransformation );
+
+        QMap< int, SourceTreeItem::DropType > dropTypeMap;
+        dropTypeMap.insert( 0, SourceTreeItem::DropTypeThisTrack );
+        dropTypeMap.insert( 1, SourceTreeItem::DropTypeThisAlbum );
+        dropTypeMap.insert( 2, SourceTreeItem::DropTypeAllFromArtist );
+        dropTypeMap.insert( 3, SourceTreeItem::DropTypeLocalItems );
+        dropTypeMap.insert( 4, SourceTreeItem::DropTypeTop50 );
+
+        QMap< int, QString > dropTypeTextMap;
+        dropTypeTextMap.insert( 0, "Track" );
+        dropTypeTextMap.insert( 1, "Album" );
+        dropTypeTextMap.insert( 2, "Artist" );
+        dropTypeTextMap.insert( 3, "Local" );
+        dropTypeTextMap.insert( 4, "Top 10" );
+
+        SourceTreeItem::DropTypes dropTypes = item->supportedDropTypes( m_dropMimeData );
+
+        for ( int i = 0; i < 5; ++i )
         {
-            PlaylistItem* plItem = qobject_cast< PlaylistItem* >( item );
-            Q_ASSERT( plItem );
+            if ( !dropTypes.testFlag( dropTypeMap.value( i ) ) )
+                continue;
 
 
-            if ( plItem && !plItem->playlist().isNull() )
+            text = dropTypeTextMap.value( i );
+
+            if ( count > 0 )
+                itemsRect.adjust( itemWidth, 0, itemWidth, 0 );
+
+            if ( itemsRect.contains( cursorPos ) )
             {
-                name = plItem->playlist()->title();
+                painter->setFont( fontBold );
+                m_hoveredDropType = dropTypeMap.value( i );
             }
-        }
-        else if ( type == SourcesModel::CategoryAdd )
-        {
-            CategoryAddItem* cItem = qobject_cast< CategoryAddItem* >( item );
-            Q_ASSERT( cItem );
+            else
+                painter->setFont( font );
 
-            name = cItem->text();
+            textRect = itemsRect.adjusted( 0, 4, 0, 0 );
+            painter->drawPixmap( textRect.x() + iconSpacing, textRect.y(), icon );
+
+            int textSpacing = ( itemWidth - painter->fontMetrics().width( text ) ) / 2;
+            textRect.adjust( textSpacing, 32 + 4, 0, 0 );
+            painter->drawText( textRect, text );
+            count++;
         }
 
-        int height = option.rect.height();
-        if ( index == m_dropHoverIndex )
-            height /= ( dropTypeCount( item ) + 1 );
 
-        QRect iconRect = option.rect.adjusted( 4, 1, -option.rect.width() + height - 2 + 4, -option.rect.height() + height -1 );
+//        if ( dropTypes.testFlag( SourceTreeItem::DropTypeThisTrack ) )
+//        {
+//            text = tr( "Track" );
 
-        QPixmap avatar = index.data( Qt::DecorationRole ).value< QIcon >().pixmap( iconRect.width(), iconRect.height() );
-        painter->drawPixmap( iconRect, avatar.scaledToHeight( iconRect.height(), Qt::SmoothTransformation ) );
+//            itemsRect.adjust( itemWidth * count, 0, itemWidth * count, 0 );
+//            if ( itemRect.contains( cursorPos ) )
+//            {
+//                painter->setFont( fontBold );
+//                m_hoveredDropType = SourceTreeItem::DropTypeThisTrack;
+//            }
+//            else
+//                painter->setFont( font );
 
-        if ( ( option.state & QStyle::State_Selected ) == QStyle::State_Selected )
-        {
-            painter->setPen( o.palette.color( QPalette::HighlightedText ) );
-        }
+//            textRect = itemsRect.adjusted( 0, 4, 0, 0 );
+//            painter->drawPixmap( textRect.x() + iconSpacing, textRect.y(), icon );
 
-        QRect textRect = option.rect.adjusted( iconRect.width() + 8, 2, /*-figWidth - 24*/ 0, 0 );
-        QString text = painter->fontMetrics().elidedText( name, Qt::ElideRight, textRect.width() );
-        painter->drawText( textRect, text );
+//            int textSpacing = ( itemWidth - painter->fontMetrics().width( text ) ) / 2;
+//            textRect.adjust( textSpacing, 32 + 4, 0, 0 );
+//            painter->drawText( textRect, text );
+//            count++;
+//        }
+//        if ( dropTypes.testFlag( SourceTreeItem::DropTypeThisAlbum ) )
+//        {
+//            text = tr( "Album" );
+//            itemsRect.adjust( itemWidth * count, 0, itemWidth * count, 0 );
+//            if ( itemRect.contains( cursorPos ) )
+//            {
+//                painter->setFont( fontBold );
+//                m_hoveredDropType = SourceTreeItem::DropTypeThisTrack;
+//            }
+//            else
+//                painter->setFont( font );
 
-        if ( index == m_dropHoverIndex )
-        {
-            QPoint cursorPos = m_parent->mapFromGlobal( QCursor::pos() );
-            int hoveredDropTypeIndex = ( cursorPos.y() - o.rect.y() ) / height;
-            int verticalOffset = height * hoveredDropTypeIndex;
-            QRect selectionRect = o.rect.adjusted( 0, verticalOffset, 0, -o.rect.height() + height + verticalOffset );
-            painter->drawRoundedRect( selectionRect, 5, 5 );
+//            textRect = itemsRect.adjusted( 0, 4, 0, 0 );
+//            painter->drawPixmap( textRect.x() + iconSpacing, textRect.y(), icon );
 
-            int count = 1;
-            SourceTreeItem::DropTypes dropTypes = item->supportedDropTypes( m_dropMimeData );
-            if ( dropTypes.testFlag( SourceTreeItem::DropTypeThisTrack ) )
-            {
-                text = tr( "This track" );
-                textRect = option.rect.adjusted( iconRect.width() + 8, 2 + ( count * height ), 0, 0 );
-                painter->drawText( textRect, text );
-                if ( count == hoveredDropTypeIndex )
-                    m_hoveredDropType = SourceTreeItem::DropTypeThisTrack;
-                count++;
-            }
-            if ( dropTypes.testFlag( SourceTreeItem::DropTypeThisAlbum ) )
-            {
-                text = tr( "This album" );
-                textRect = option.rect.adjusted( iconRect.width() + 8, 2 + ( count * height ), 0, 0 );
-                painter->drawText( textRect, text );
-                if ( count == hoveredDropTypeIndex )
-                    m_hoveredDropType = SourceTreeItem::DropTypeThisAlbum;
-                count++;
-            }
-            if ( dropTypes.testFlag( SourceTreeItem::DropTypeAllFromArtist ) )
-            {
-                text = tr( "All from artist" );
-                textRect = option.rect.adjusted( iconRect.width() + 8, 2 + ( count * height ), 0, 0 );
-                painter->drawText( textRect, text );
-                if ( count == hoveredDropTypeIndex )
-                    m_hoveredDropType = SourceTreeItem::DropTypeAllFromArtist;
-                count++;
-            }
-            if ( dropTypes.testFlag( SourceTreeItem::DropTypeLocalItems ) )
-            {
-                text = tr( "All local from Artist" );
-                textRect = option.rect.adjusted( iconRect.width() + 8, 2 + ( count * height ), 0, 0 );
-                painter->drawText( textRect, text );
-                if ( count == hoveredDropTypeIndex )
-                    m_hoveredDropType = SourceTreeItem::DropTypeLocalItems;
-                count++;
-            }
-            if ( dropTypes.testFlag( SourceTreeItem::DropTypeTop50 ) )
-            {
-                text = tr( "Top 50" );
-                textRect = option.rect.adjusted( iconRect.width() + 8, 2 + ( count * height ), 0, 0 );
-                painter->drawText( textRect, text );
-                if ( count == hoveredDropTypeIndex )
-                    m_hoveredDropType = SourceTreeItem::DropTypeTop50;
-                count++;
-            }
-        }
+//            int textSpacing = ( itemWidth - painter->fontMetrics().width( text ) ) / 2;
+//            textRect.adjust( textSpacing, 32 + 4, 0, 0 );
+//            painter->drawText( textRect, text );
+//            count++;
+//        }
+//        if ( dropTypes.testFlag( SourceTreeItem::DropTypeAllFromArtist ) )
+//        {
+//            text = tr( "Artist" );
+//            textRect = itemsRect.adjusted( itemWidth * count, 4, itemWidth * count, 0 );
+//            painter->drawPixmap( textRect.x() + iconSpacing, textRect.y(), icon );
+
+//            if ( itemRect.contains( cursorPos ) )
+//            {
+//                painter->setFont( fontBold );
+//                m_hoveredDropType = SourceTreeItem::DropTypeAllFromArtist;
+//            }
+//            else
+//                painter->setFont( font );
+
+//            int textSpacing = ( itemWidth - painter->fontMetrics().width( text ) ) / 2;
+//            textRect.adjust( textSpacing, 32 + 4, 0, 0 );
+//            painter->drawText( textRect, text );
+//            count++;
+//        }
+//        if ( dropTypes.testFlag( SourceTreeItem::DropTypeLocalItems ) )
+//        {
+//            text = tr( "Local" );
+//            textRect = itemsRect.adjusted( itemWidth * count, 4, itemWidth * count, 0 );
+//            painter->drawPixmap( textRect.x() + iconSpacing, textRect.y(), icon );
+
+//            if ( itemRect.contains( cursorPos ) )
+//            {
+//                painter->setFont( fontBold );
+//                m_hoveredDropType = SourceTreeItem::DropTypeLocalItems;
+//            }
+//            else
+//                painter->setFont( font );
+
+//            int textSpacing = ( itemWidth - painter->fontMetrics().width( text ) ) / 2;
+//            textRect.adjust( textSpacing, 32 + 4, 0, 0 );
+//            painter->drawText( textRect, text );
+//            count++;
+//        }
+//        if ( dropTypes.testFlag( SourceTreeItem::DropTypeTop50 ) )
+//        {
+//            text = tr( "Top 10" );
+//            textRect = itemsRect.adjusted( itemWidth * count, 4, itemWidth * count, 0 );
+//            painter->drawPixmap( textRect.x() + iconSpacing, textRect.y(), icon );
+
+//            if ( itemRect.contains( cursorPos ) )
+//            {
+//                painter->setFont( fontBold );
+//                m_hoveredDropType = SourceTreeItem::DropTypeTop50;
+//            }
+//            else
+//                painter->setFont( font );
+
+//            int textSpacing = ( itemWidth - painter->fontMetrics().width( text ) ) / 2;
+//            textRect.adjust( textSpacing, 32 + 4, 0, 0 );
+//            painter->drawText( textRect, text );
+//            count++;
+//        }
+
+
+
+//        QFont bold = painter->font();
+//        bold.setBold( true );
+
+//        QString name = index.data().toString();
+//        if ( type == SourcesModel::StaticPlaylist )
+//        {
+//            PlaylistItem* plItem = qobject_cast< PlaylistItem* >( item );
+//            Q_ASSERT( plItem );
+
+
+//            if ( plItem && !plItem->playlist().isNull() )
+//            {
+//                name = plItem->playlist()->title();
+//            }
+//        }
+//        else if ( type == SourcesModel::CategoryAdd )
+//        {
+//            CategoryAddItem* cItem = qobject_cast< CategoryAddItem* >( item );
+//            Q_ASSERT( cItem );
+
+//            name = cItem->text();
+//        }
+
+//        int height = option.rect.height();
+//        if ( m_expandedMap.contains( index ) && m_expandedMap.value( index )->partlyExpanded() )
+//            height /= ( dropTypeCount( item ) + 1 );
+
+//        QRect iconRect = option.rect.adjusted( 4, 1, -option.rect.width() + option.decorationSize.width() - 2 + 4, -option.rect.height() + option.decorationSize.height() -1 );
+
+//        QPixmap avatar = index.data( Qt::DecorationRole ).value< QIcon >().pixmap( iconRect.width(), iconRect.height() );
+//        painter->drawPixmap( iconRect, avatar.scaledToHeight( iconRect.height(), Qt::SmoothTransformation ) );
+
+//        if ( ( option.state & QStyle::State_Selected ) == QStyle::State_Selected )
+//        {
+//            painter->setPen( o.palette.color( QPalette::HighlightedText ) );
+//        }
+
+//        QRect textRect = option.rect.adjusted( iconRect.width() + 8, 2, /*-figWidth - 24*/ 0, 0 );
+//        QString text = painter->fontMetrics().elidedText( name, Qt::ElideRight, textRect.width() );
+//        painter->drawText( textRect, text );
+
+//        if ( m_expandedMap.contains( index ) && m_expandedMap.value( index )->partlyExpanded() )
+//        {
+//            QPoint cursorPos = m_parent->mapFromGlobal( QCursor::pos() );
+//            int hoveredDropTypeIndex = ( cursorPos.y() - o.rect.y() ) / height;
+//            int verticalOffset = height * hoveredDropTypeIndex;
+//            QRect selectionRect = o.rect.adjusted( 0, verticalOffset, 0, -o.rect.height() + height + verticalOffset );
+//            painter->drawRoundedRect( selectionRect, 5, 5 );
+
+//            int count = 1;
+//            SourceTreeItem::DropTypes dropTypes = item->supportedDropTypes( m_dropMimeData );
+//            if ( dropTypes.testFlag( SourceTreeItem::DropTypeThisTrack ) )
+//            {
+//                text = tr( "This track" );
+//                textRect = option.rect.adjusted( iconRect.width() + 8, 2 + ( count * height ), 0, 0 );
+//                painter->drawText( textRect, text );
+//                if ( count == hoveredDropTypeIndex )
+//                    m_hoveredDropType = SourceTreeItem::DropTypeThisTrack;
+//                count++;
+//            }
+//            if ( dropTypes.testFlag( SourceTreeItem::DropTypeThisAlbum ) )
+//            {
+//                text = tr( "This album" );
+//                textRect = option.rect.adjusted( iconRect.width() + 8, 2 + ( count * height ), 0, 0 );
+//                painter->drawText( textRect, text );
+//                if ( count == hoveredDropTypeIndex )
+//                    m_hoveredDropType = SourceTreeItem::DropTypeThisAlbum;
+//                count++;
+//            }
+//            if ( dropTypes.testFlag( SourceTreeItem::DropTypeAllFromArtist ) )
+//            {
+//                text = tr( "All from artist" );
+//                textRect = option.rect.adjusted( iconRect.width() + 8, 2 + ( count * height ), 0, 0 );
+//                painter->drawText( textRect, text );
+//                if ( count == hoveredDropTypeIndex )
+//                    m_hoveredDropType = SourceTreeItem::DropTypeAllFromArtist;
+//                count++;
+//            }
+//            if ( dropTypes.testFlag( SourceTreeItem::DropTypeLocalItems ) )
+//            {
+//                text = tr( "All local from Artist" );
+//                textRect = option.rect.adjusted( iconRect.width() + 8, 2 + ( count * height ), 0, 0 );
+//                painter->drawText( textRect, text );
+//                if ( count == hoveredDropTypeIndex )
+//                    m_hoveredDropType = SourceTreeItem::DropTypeLocalItems;
+//                count++;
+//            }
+//            if ( dropTypes.testFlag( SourceTreeItem::DropTypeTop50 ) )
+//            {
+//                text = tr( "Top 50" );
+//                textRect = option.rect.adjusted( iconRect.width() + 8, 2 + ( count * height ), 0, 0 );
+//                painter->drawText( textRect, text );
+//                if ( count == hoveredDropTypeIndex )
+//                    m_hoveredDropType = SourceTreeItem::DropTypeTop50;
+//                count++;
+//            }
+//        }
 
         painter->restore();
 
@@ -335,3 +543,40 @@ SourceDelegate::hoveredDropType() const
 {
     return m_hoveredDropType;
 }
+
+void
+SourceDelegate::hovered(const QModelIndex &index, const QMimeData *mimeData)
+{
+    if ( !index.isValid() )
+    {
+        return;
+    }
+    if ( !m_expandedMap.contains( index ) )
+    {
+        foreach ( AnimationHelper *helper, m_expandedMap )
+        {
+            helper->collapse();
+        }
+
+        m_newDropHoverIndex = index;
+        m_dropMimeData = const_cast< QMimeData* >( mimeData );
+        m_expandedMap.insert( m_newDropHoverIndex, new AnimationHelper( m_newDropHoverIndex ) );
+        connect( m_expandedMap.value( m_newDropHoverIndex ), SIGNAL( finished( QModelIndex ) ), SLOT( animationFinished( QModelIndex ) ) );
+
+    }
+}
+
+void
+SourceDelegate::dragLeaveEvent()
+{
+    foreach ( AnimationHelper *helper, m_expandedMap )
+    {
+        helper->collapse( true );
+    }
+}
+
+void
+SourceDelegate::animationFinished( const QModelIndex& index )
+{
+    delete m_expandedMap.take( index );
+}
diff --git a/src/sourcetree/sourcedelegate.h b/src/sourcetree/sourcedelegate.h
index 124b8c3c7..e764b9340 100644
--- a/src/sourcetree/sourcedelegate.h
+++ b/src/sourcetree/sourcedelegate.h
@@ -5,13 +5,18 @@
 #include "items/sourcetreeitem.h"
 
 #include <QStyledItemDelegate>
+#include <QPropertyAnimation>
+
+class AnimationHelper;
 
 class SourceDelegate : public QStyledItemDelegate
 {
+    Q_OBJECT
 public:
-    SourceDelegate( QAbstractItemView* parent = 0 ) : QStyledItemDelegate( parent ), m_parent( parent ) {}
+    SourceDelegate( QAbstractItemView* parent = 0 );
 
-    void setDropHoverIndex( const QModelIndex &index, const QMimeData *mimeData ) { m_dropHoverIndex = index; m_dropMimeData = const_cast< QMimeData* >( mimeData ); }
+    void hovered( const QModelIndex &index, const QMimeData *mimeData );
+    void dragLeaveEvent();
 
     SourceTreeItem::DropType hoveredDropType() const;
 
@@ -22,12 +27,16 @@ protected:
     virtual int dropTypeCount( SourceTreeItem* item ) const;
     virtual bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index );
 
+private slots:
+    void animationFinished( const QModelIndex& );
 private:
     QAbstractItemView* m_parent;
     mutable int m_iconHeight;
     QModelIndex m_dropHoverIndex;
+    QModelIndex m_newDropHoverIndex;
     QMimeData *m_dropMimeData;
     mutable SourceTreeItem::DropType m_hoveredDropType; // Hack to keep easily track of the current highlighted DropType in paint()
+    QMap< QModelIndex, AnimationHelper* > m_expandedMap;
 };
 
 #endif // SOURCEDELEGATE_H
diff --git a/src/sourcetree/sourcetreeview.cpp b/src/sourcetree/sourcetreeview.cpp
index bd40aa415..c7bc2c7c7 100644
--- a/src/sourcetree/sourcetreeview.cpp
+++ b/src/sourcetree/sourcetreeview.cpp
@@ -439,7 +439,7 @@ SourceTreeView::dragLeaveEvent( QDragLeaveEvent* event )
     m_dragging = false;
     setDirtyRegion( m_dropRect );
 
-    m_delegate->setDropHoverIndex( QModelIndex(), 0 );
+    m_delegate->dragLeaveEvent();
     dataChanged(m_dropIndex, m_dropIndex);
     m_dropIndex = QPersistentModelIndex();
 }
@@ -456,7 +456,6 @@ SourceTreeView::dragMoveEvent( QDragMoveEvent* event )
         setDirtyRegion( m_dropRect );
         const QPoint pos = event->pos();
         const QModelIndex index = indexAt( pos );
-        m_delegate->setDropHoverIndex( QModelIndex(), event->mimeData() );
         dataChanged(m_dropIndex, m_dropIndex);
         m_dropIndex = QPersistentModelIndex( index );
 
@@ -469,7 +468,7 @@ SourceTreeView::dragMoveEvent( QDragMoveEvent* event )
             if( item->willAcceptDrag( event->mimeData() ) )
             {
                 accept = true;
-                m_delegate->setDropHoverIndex( index, event->mimeData() );
+                m_delegate->hovered( index, event->mimeData() );
                 dataChanged(index, index);
             }
         }
@@ -497,7 +496,7 @@ SourceTreeView::dropEvent( QDropEvent* event )
     const QPoint pos = event->pos();
     const QModelIndex index = indexAt( pos );
 
-    if ( model()->data( index, SourcesModel::SourceTreeItemTypeRole ).toInt() == SourcesModel::PlaylistsCategory )
+    if ( model()->data( index, SourcesModel::SourceTreeItemTypeRole ).toInt() == SourcesModel::StaticPlaylist )
     {
         PlaylistItem* item = itemFromIndex< PlaylistItem >( index );
         Q_ASSERT( item );
@@ -506,10 +505,21 @@ SourceTreeView::dropEvent( QDropEvent* event )
         qDebug() << "dropType is " << m_delegate->hoveredDropType();
     }
 
-    QTreeView::dropEvent( event );
+    // Need to fake the dropevent because the treeview would reject it if it is outside the item (on the tree)
+    if ( pos.x() < 100 )
+    {
+        QDropEvent* newEvent = new QDropEvent( pos + QPoint( 100, 0 ), event->possibleActions(), event->mimeData(), event->mouseButtons(), event->keyboardModifiers(), event->type() );
+        QTreeView::dropEvent( newEvent );
+        delete newEvent;
+    }
+    else
+    {
+        QTreeView::dropEvent( event );
+    }
+
     m_dragging = false;
     m_dropIndex = QPersistentModelIndex();
-    m_delegate->setDropHoverIndex( QModelIndex(), 0 );
+    m_delegate->dragLeaveEvent();
     dataChanged( index, index );
 }
 
@@ -573,3 +583,8 @@ SourceTreeView::itemFromIndex( const QModelIndex& index ) const
     return item;
 }
 
+void
+SourceTreeView::update( const QModelIndex &index )
+{
+    dataChanged( index, index );
+}
diff --git a/src/sourcetree/sourcetreeview.h b/src/sourcetree/sourcetreeview.h
index 8bba33076..6a60e07cf 100644
--- a/src/sourcetree/sourcetreeview.h
+++ b/src/sourcetree/sourcetreeview.h
@@ -24,6 +24,7 @@
 
 #include "source.h"
 #include "sourcetree/sourcesmodel.h"
+#include "sourcetree/sourcedelegate.h"
 
 class CollectionModel;
 class PlaylistModel;
@@ -42,6 +43,9 @@ public slots:
     void showOfflineSources( bool offlineSourcesShown );
 
     void renamePlaylist();
+
+    void update( const QModelIndex &index );
+
 signals:
     void onOnline( const QModelIndex& index );
     void onOffline( const QModelIndex& index );