From 1953f6030ea5e44ca347e22d1ae09609d779bd31 Mon Sep 17 00:00:00 2001
From: Jakob Leben <jleben@videolan.org>
Date: Thu, 11 Feb 2010 03:37:58 +0100
Subject: [PATCH] Qt: add a list view

---
 modules/gui/qt4/components/playlist/icon_view.cpp  |  253 +++++++++++++-------
 modules/gui/qt4/components/playlist/icon_view.hpp  |   32 +++-
 .../gui/qt4/components/playlist/playlist_model.cpp |    9 +-
 .../gui/qt4/components/playlist/playlist_model.hpp |    4 +-
 modules/gui/qt4/components/playlist/sorting.h      |    1 +
 .../gui/qt4/components/playlist/standardpanel.cpp  |   80 +++++--
 .../gui/qt4/components/playlist/standardpanel.hpp  |   18 +-
 7 files changed, 284 insertions(+), 113 deletions(-)

diff --git a/modules/gui/qt4/components/playlist/icon_view.cpp b/modules/gui/qt4/components/playlist/icon_view.cpp
index 99ca253..702288e 100644
--- a/modules/gui/qt4/components/playlist/icon_view.cpp
+++ b/modules/gui/qt4/components/playlist/icon_view.cpp
@@ -23,6 +23,7 @@
 
 #include "components/playlist/icon_view.hpp"
 #include "components/playlist/playlist_model.hpp"
+#include "components/playlist/sorting.h"
 #include "input_manager.hpp"
 
 #include <QApplication>
@@ -41,141 +42,213 @@
 #define ITEMS_SPACING       10
 #define ART_RADIUS          5
 
-
-static const QRect drawRect = QRect( 0, 0, RECT_SIZE_W, RECT_SIZE_H );
-static const QRect artRect = drawRect.adjusted( OFFSET - 1, 2, - OFFSET, - OFFSET *2 );
-
-void PlListViewItemDelegate::paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
+QString AbstractPlViewItemDelegate::getMeta( const QModelIndex & index, int meta ) const
 {
-    PLItem *currentItem = static_cast<PLItem*>( index.internalPointer() );
-    assert( currentItem );
+    return index.model()->index( index.row(),
+                                  PLModel::columnFromMeta( meta ),
+                                  index.parent() )
+                                .data().toString();
+}
 
-    char *meta = input_item_GetTitleFbName( currentItem->inputItem() );
-    QString title = qfu( meta );
-    free( meta );
+void AbstractPlViewItemDelegate::paintPlayingItemBg( QPainter *painter, const QStyleOptionViewItem & option ) const
+{
+    painter->save();
+    painter->setOpacity( 0.5 );
+    painter->setBrush( QBrush( Qt::gray ) );
+    painter->fillRect( option.rect, option.palette.color( QPalette::Dark ) );
+    painter->restore();
+}
 
-    meta = input_item_GetArtist( currentItem->inputItem() );
-    QString artist = qfu( meta );
-    free( meta );
+QPixmap AbstractPlViewItemDelegate::getArtPixmap( const QModelIndex & index, const QSize & size ) const
+{
+    PLItem *item = static_cast<PLItem*>( index.internalPointer() );
+    assert( item );
 
-    /* Primary ArtUrl */
-    QString artUrl = InputManager::decodeArtURL( currentItem->inputItem() );
+    QString artUrl = InputManager::decodeArtURL( item->inputItem() );
 
-    /* else look up through all children and use the first picture found */
     if( artUrl.isEmpty() )
     {
-        for( int i = 0; i < currentItem->childCount(); i++ )
+        for( int i = 0; i < item->childCount(); i++ )
         {
-            artUrl = InputManager::decodeArtURL( currentItem->child( i )->inputItem() );
+            artUrl = InputManager::decodeArtURL( item->child( i )->inputItem() );
             if( !artUrl.isEmpty() )
                 break;
         }
     }
 
-    /*if( option.state & QStyle::State_Selected )
-         painter->fillRect(option.rect, option.palette.highlight());*/
-    QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &option,
-                                          painter );
+    QPixmap artPix( size );
 
-    /* Pixmap where all the rendering will happen and that will be cached */
-    QPixmap pix;
+    QString key = artUrl + QString("%1%2").arg(size.width()).arg(size.height());
 
-    QString key = title + artist + artUrl
-                  + QString( index.data( PLModel::IsCurrentRole ).toBool() );
-    if(QPixmapCache::find( key, pix ))
+    if( !QPixmapCache::find( key, artPix ))
     {
-        // cool, we found it in the cache
-        painter->drawPixmap( option.rect, pix );
-        return;
+        QPixmap tmp;
+        bool cache = false;
+        if( artUrl.isEmpty() || !tmp.load( artUrl ) )
+        {
+            tmp = QPixmap( ":/noart64" ).scaled( size, Qt::KeepAspectRatio, Qt::SmoothTransformation );
+        }
+        else
+        {
+            tmp = tmp.scaled( size, Qt::KeepAspectRatio, Qt::SmoothTransformation );
+            cache = true;
+        }
+        artPix.fill( Qt::black );
+        QPainter p( &artPix );
+        p.drawPixmap( (size.width() - tmp.width()) / 2,
+                      (size.height() - tmp.height()) / 2,
+                      tmp );
+        if( cache ) QPixmapCache::insert( key, artPix );
     }
 
-    /* Background decorations */
-    pix = QPixmap( RECT_SIZE_W, RECT_SIZE_H );
-    pix.fill( Qt::transparent );
+    return artPix;
+}
 
-    QPainter *pixpainter = new QPainter( &pix );
+void PlIconViewItemDelegate::paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
+{
+    QString title = getMeta( index, COLUMN_TITLE );
+    QString artist = getMeta( index, COLUMN_ARTIST );
 
-    pixpainter->setRenderHints(
-            QPainter::Antialiasing | QPainter::SmoothPixmapTransform |
-            QPainter::TextAntialiasing );
+    QPixmap artPix = getArtPixmap( index, QSize( ART_SIZE, ART_SIZE ) );
+
+    QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &option,
+                                          painter );
+
+    painter->save();
 
     if( index.data( PLModel::IsCurrentRole ).toBool() )
     {
-       pixpainter->save();
-       pixpainter->setOpacity( 0.2 );
-       pixpainter->setBrush( QBrush( Qt::gray ) );
-       pixpainter->drawRoundedRect( 0, 0, RECT_SIZE_W, RECT_SIZE_H, ART_RADIUS, ART_RADIUS );
-       pixpainter->restore();
+       painter->save();
+       painter->setOpacity( 0.2 );
+       painter->setBrush( QBrush( Qt::gray ) );
+       painter->drawRoundedRect( option.rect.adjusted( 0, 0, -1, -1 ), ART_RADIUS, ART_RADIUS );
+       painter->restore();
     }
 
-    // Draw the drop shadow
-    pixpainter->save();
-    pixpainter->setOpacity( 0.7 );
-    pixpainter->setBrush( QBrush( Qt::gray ) );
-    pixpainter->drawRoundedRect( artRect.adjusted( 2, 2, 2, 2 ), ART_RADIUS, ART_RADIUS );
-    pixpainter->restore();
-
+    QRect artRect = option.rect.adjusted( OFFSET - 1, 2, - OFFSET, - OFFSET *2 );
 
-    // load album art in the pixmap
-    QPixmap artPix;
-    if( artUrl.isEmpty() || !artPix.load( artUrl ) )
-        artPix = QPixmap( ":/noart64" );
-    else
-        artPix = artPix.scaled( ART_SIZE, ART_SIZE,
-                Qt::KeepAspectRatioByExpanding );
+    // Draw the drop shadow
+    painter->save();
+    painter->setOpacity( 0.7 );
+    painter->setBrush( QBrush( Qt::darkGray ) );
+    painter->setPen( Qt::NoPen );
+    painter->drawRoundedRect( artRect.adjusted( 0, 0, 2, 2 ), ART_RADIUS, ART_RADIUS );
+    painter->restore();
 
     // Draw the art pixmap
     QPainterPath artRectPath;
     artRectPath.addRoundedRect( artRect, ART_RADIUS, ART_RADIUS );
-    pixpainter->setClipPath( artRectPath );
-    pixpainter->drawPixmap( artRect, artPix );
-    pixpainter->setClipping( false );
+    painter->setClipPath( artRectPath );
+    painter->drawPixmap( artRect, artPix );
+    painter->setClipping( false );
 
-    /* */
-    QColor text = qApp->palette().text().color();
+    if( option.state & QStyle::State_Selected )
+        painter->setPen( option.palette.color( QPalette::HighlightedText ) );
 
-    // Draw title
-    pixpainter->setPen( text );
     QFont font;
     font.setPointSize( 7 );
-    font.setItalic( true );
     font.setBold( index.data( Qt::FontRole ).value<QFont>().bold() );
-    pixpainter->setFont( font );
-    QFontMetrics fm = pixpainter->fontMetrics();
-    QRect textRect = drawRect.adjusted( 1, ART_SIZE + 4, 0, -1 );
+
+    // Draw title
+    font.setItalic( true );
+    painter->setFont( font );
+
+    QFontMetrics fm = painter->fontMetrics();
+    QRect textRect = option.rect.adjusted( 1, ART_SIZE + 8, 0, -1 );
     textRect.setHeight( fm.height() + 1 );
 
-    pixpainter->drawText( textRect,
+    painter->drawText( textRect,
                       fm.elidedText( title, Qt::ElideRight, textRect.width() ),
                       QTextOption( Qt::AlignCenter ) );
 
     // Draw artist
-    pixpainter->setPen( text.lighter( 220 ) );
+    painter->setPen( painter->pen().color().lighter( 150 ) );
     font.setItalic( false );
-    pixpainter->setFont( font );
-    fm = pixpainter->fontMetrics();
+    painter->setFont( font );
+    fm = painter->fontMetrics();
 
     textRect = textRect.adjusted( 0, textRect.height(),
                                     0, textRect.height() );
-    pixpainter->drawText(  textRect,
-                    fm.elidedText( artist, Qt::ElideRight, textRect.width() ),
-                    QTextOption( Qt::AlignCenter ) );
+    painter->drawText(  textRect,
+                        fm.elidedText( artist, Qt::ElideRight, textRect.width() ),
+                        QTextOption( Qt::AlignCenter ) );
+
+    painter->restore();
+}
 
-    delete pixpainter; // Ensure all paint operations have finished
+QSize PlIconViewItemDelegate::sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const
+{
+    return QSize( RECT_SIZE_W, RECT_SIZE_H );
+}
 
-    // Here real drawing happens
-    painter->drawPixmap( option.rect, pix );
+#define LISTVIEW_ART_SIZE 45
 
-    // Cache the rendering
-    QPixmapCache::insert( key, pix );
+void PlListViewItemDelegate::paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
+{
+    QModelIndex parent = index.parent();
+    QModelIndex i;
+
+    QString title = getMeta( index, COLUMN_TITLE );
+    QString duration = getMeta( index, COLUMN_DURATION );
+    if( !duration.isEmpty() ) title += QString(" [%1]").arg( duration );
+
+    QString artist = getMeta( index, COLUMN_ARTIST );
+    QString album = getMeta( index, COLUMN_ALBUM );
+    QString trackNum = getMeta( index, COLUMN_TRACK_NUMBER );
+    QString artistAlbum = artist
+                          + ( artist.isEmpty() ? QString() : QString( ": " ) )
+                          + album
+                          + ( album.isEmpty() || trackNum.isEmpty() ?
+                              QString() : QString( " [#%1]" ).arg( trackNum ) );
+
+    QPixmap artPix = getArtPixmap( index, QSize( LISTVIEW_ART_SIZE, LISTVIEW_ART_SIZE ) );
+
+    QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &option, painter );
+
+    if( index.data( PLModel::IsCurrentRole ).toBool() )
+        paintPlayingItemBg( painter, option );
+
+    painter->drawPixmap( option.rect.topLeft() + QPoint(3,3), artPix );
+
+
+    int textH = option.fontMetrics.height() + 2;
+    int margin = ( option.rect.height() / 2 ) - textH;
+
+    QRect textRect = option.rect.adjusted( LISTVIEW_ART_SIZE + 10,
+                                           margin,
+                                           -10,
+                                           margin * -1 - ( artistAlbum.isEmpty() ? 0 : textH ) );
+
+    painter->save();
+
+    if( option.state & QStyle::State_Selected )
+        painter->setPen( option.palette.color( QPalette::HighlightedText ) );
+
+    QTextOption textOpt( Qt::AlignVCenter | Qt::AlignLeft );
+    textOpt.setWrapMode( QTextOption::NoWrap );
+
+    QFont f( option.font );
+    if( index.data( PLModel::IsCurrentRole ).toBool() ) f.setBold( true );
+
+    f.setItalic( true );
+    painter->setFont( f );
+
+    painter->drawText( textRect, title, textOpt );
+
+    f.setItalic( false );
+    painter->setFont( f );
+    textRect.moveTop( textRect.top() + textH );
+
+    painter->drawText( textRect, artistAlbum, textOpt );
+
+    painter->restore();
 }
 
 QSize PlListViewItemDelegate::sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const
 {
-    return QSize( RECT_SIZE_W, RECT_SIZE_H );
+  return QSize( LISTVIEW_ART_SIZE + 6, LISTVIEW_ART_SIZE + 6 );
 }
 
-
 PlIconView::PlIconView( PLModel *model, QWidget *parent ) : QListView( parent )
 {
     setModel( model );
@@ -183,12 +256,24 @@ PlIconView::PlIconView( PLModel *model, QWidget *parent ) : QListView( parent )
     setMovement( QListView::Static );
     setResizeMode( QListView::Adjust );
     setGridSize( QSize( RECT_SIZE_W, RECT_SIZE_H ) );
-    setUniformItemSizes( true );
     setWrapping( true );
+    setUniformItemSizes( true );
     setSelectionMode( QAbstractItemView::ExtendedSelection );
     setAcceptDrops( true );
 
-    PlListViewItemDelegate *pl = new PlListViewItemDelegate( this );
-    setItemDelegate( pl );
+    PlIconViewItemDelegate *delegate = new PlIconViewItemDelegate( this );
+    setItemDelegate( delegate );
 }
 
+PlListView::PlListView( PLModel *model, QWidget *parent ) : QListView( parent )
+{
+    setModel( model );
+    setViewMode( QListView::ListMode );
+    setUniformItemSizes( true );
+    setSelectionMode( QAbstractItemView::ExtendedSelection );
+    setAcceptDrops( true );
+    setAlternatingRowColors( true );
+
+    PlListViewItemDelegate *delegate = new PlListViewItemDelegate( this );
+    setItemDelegate( delegate );
+}
diff --git a/modules/gui/qt4/components/playlist/icon_view.hpp b/modules/gui/qt4/components/playlist/icon_view.hpp
index 1d73384..cd723d1 100644
--- a/modules/gui/qt4/components/playlist/icon_view.hpp
+++ b/modules/gui/qt4/components/playlist/icon_view.hpp
@@ -30,12 +30,32 @@
 class QPainter;
 class PLModel;
 
-class PlListViewItemDelegate : public QStyledItemDelegate
+class AbstractPlViewItemDelegate : public QStyledItemDelegate
+{
+public:
+    AbstractPlViewItemDelegate( QWidget * parent = 0 ) : QStyledItemDelegate(parent) {}
+    QString getMeta( const QModelIndex & index, int meta ) const;
+    void paintPlayingItemBg( QPainter *painter, const QStyleOptionViewItem & option ) const;
+    QPixmap getArtPixmap( const QModelIndex & index, const QSize & size ) const;
+};
+
+class PlIconViewItemDelegate : public AbstractPlViewItemDelegate
 {
     Q_OBJECT
 
 public:
-    PlListViewItemDelegate(QWidget *parent = 0) : QStyledItemDelegate(parent) {}
+    PlIconViewItemDelegate(QWidget *parent = 0) : AbstractPlViewItemDelegate(parent) {}
+
+    void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const;
+    QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const;
+};
+
+class PlListViewItemDelegate : public AbstractPlViewItemDelegate
+{
+    Q_OBJECT
+
+public:
+    PlListViewItemDelegate(QWidget *parent = 0) : AbstractPlViewItemDelegate(parent) {}
 
     void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const;
     QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const;
@@ -49,5 +69,13 @@ public:
     PlIconView( PLModel *model, QWidget *parent = 0 );
 };
 
+class PlListView : public QListView
+{
+    Q_OBJECT
+
+public:
+    PlListView( PLModel *model, QWidget *parent = 0 );
+};
+
 #endif
 
diff --git a/modules/gui/qt4/components/playlist/playlist_model.cpp b/modules/gui/qt4/components/playlist/playlist_model.cpp
index ec72f89..c481bd8 100644
--- a/modules/gui/qt4/components/playlist/playlist_model.cpp
+++ b/modules/gui/qt4/components/playlist/playlist_model.cpp
@@ -339,7 +339,12 @@ QVariant PLModel::data( const QModelIndex &index, int role ) const
             QFont f; f.setBold( true ); return QVariant( f );
         }
     }
+    else if( role == Qt::BackgroundRole && isCurrent( index ) )
+    {
+        return QVariant( QBrush( Qt::gray ) );
+    }
     else if( role == IsCurrentRole ) return QVariant( isCurrent( index ) );
+
     return QVariant();
 }
 
@@ -532,7 +537,7 @@ PLItem * PLModel::findInner( PLItem *root, int i_id, bool b_input )
 #undef CACHE
 #undef ICACHE
 
-int PLModel::columnToMeta( int _column ) const
+int PLModel::columnToMeta( int _column )
 {
     int meta = 1;
     int column = 0;
@@ -546,7 +551,7 @@ int PLModel::columnToMeta( int _column ) const
     return meta;
 }
 
-int PLModel::columnFromMeta( int meta_col ) const
+int PLModel::columnFromMeta( int meta_col )
 {
     int meta = 1;
     int column = 0;
diff --git a/modules/gui/qt4/components/playlist/playlist_model.hpp b/modules/gui/qt4/components/playlist/playlist_model.hpp
index 5cc5c12..ba112a0 100644
--- a/modules/gui/qt4/components/playlist/playlist_model.hpp
+++ b/modules/gui/qt4/components/playlist/playlist_model.hpp
@@ -90,6 +90,8 @@ public:
     QModelIndex currentIndex();
     bool isCurrent( const QModelIndex &index ) const;
     int itemId( const QModelIndex &index ) const;
+    static int columnFromMeta( int meta_column );
+    static int columnToMeta( int column );
 
     /* Actions */
     void popup( const QModelIndex & index, const QPoint &point, const QModelIndexList &list );
@@ -147,8 +149,6 @@ private:
     PLItem *findById( PLItem *, int );
     PLItem *findByInput( PLItem *, int );
     PLItem *findInner( PLItem *, int , bool );
-    int columnFromMeta( int meta_column ) const;
-    int columnToMeta( int column ) const;
     bool canEdit() const;
 
     PLItem *p_cached_item;
diff --git a/modules/gui/qt4/components/playlist/sorting.h b/modules/gui/qt4/components/playlist/sorting.h
index ca2f8f2..c324054 100644
--- a/modules/gui/qt4/components/playlist/sorting.h
+++ b/modules/gui/qt4/components/playlist/sorting.h
@@ -73,6 +73,7 @@ static inline char * psz_column_meta( input_item_t *p_item, uint32_t i_column )
         return input_item_GetTitleFbName( p_item );
     case COLUMN_DURATION:
         i_duration = input_item_GetDuration( p_item ) / 1000000;
+        if( i_duration == 0 ) return NULL;
         secstotimestr( psz_duration, i_duration );
         return strdup( psz_duration );
     case COLUMN_ARTIST:
diff --git a/modules/gui/qt4/components/playlist/standardpanel.cpp b/modules/gui/qt4/components/playlist/standardpanel.cpp
index 504b041..a9de812 100644
--- a/modules/gui/qt4/components/playlist/standardpanel.cpp
+++ b/modules/gui/qt4/components/playlist/standardpanel.cpp
@@ -52,6 +52,10 @@
 
 #include "sorting.h"
 
+static const QString viewNames[] = { qtr( "Detailed View" ),
+                                     qtr( "Icon View" ),
+                                     qtr( "List View" ) };
+
 StandardPLPanel::StandardPLPanel( PlaylistWidget *_parent,
                                   intf_thread_t *_p_intf,
                                   playlist_t *p_playlist,
@@ -64,6 +68,7 @@ StandardPLPanel::StandardPLPanel( PlaylistWidget *_parent,
 
     iconView = NULL;
     treeView = NULL;
+    listView = NULL;
 
     model = new PLModel( p_playlist, p_intf, p_root, this );
     currentRootId = -1;
@@ -101,15 +106,13 @@ StandardPLPanel::StandardPLPanel( PlaylistWidget *_parent,
 
     QActionGroup *actionGroup = new QActionGroup( this );
 
-    treeViewAction = actionGroup->addAction( "Detailed view" );
-    treeViewAction->setCheckable( true );
-    viewSelectionMapper->setMapping( treeViewAction, TREE_VIEW );
-    CONNECT( treeViewAction, triggered(), viewSelectionMapper, map() );
-
-    iconViewAction = actionGroup->addAction( "Icon view" );
-    iconViewAction->setCheckable( true );
-    viewSelectionMapper->setMapping( iconViewAction, ICON_VIEW );
-    CONNECT( iconViewAction, triggered(), viewSelectionMapper, map() );
+    for( int i = 0; i < VIEW_COUNT; i++ )
+    {
+        viewActions[i] = actionGroup->addAction( viewNames[i] );
+        viewActions[i]->setCheckable( true );
+        viewSelectionMapper->setMapping( viewActions[i], i );
+        CONNECT( viewActions[i], triggered(), viewSelectionMapper, map() );
+    }
 
     BUTTONACT( viewButton, cycleViews() );
     QMenu *viewMenu = new QMenu( this );
@@ -138,7 +141,12 @@ StandardPLPanel::~StandardPLPanel()
     getSettings()->beginGroup("Playlist");
     if( treeView )
         getSettings()->setValue( "headerStateV2", treeView->header()->saveState() );
-    getSettings()->setValue( "view-mode", ( currentView == iconView ) ? ICON_VIEW : TREE_VIEW );
+    if( currentView == treeView )
+        getSettings()->setValue( "view-mode", TREE_VIEW );
+    else if( currentView == listView )
+        getSettings()->setValue( "view-mode", LIST_VIEW );
+    else if( currentView == iconView )
+        getSettings()->setValue( "view-mode", ICON_VIEW );
     getSettings()->endGroup();
 }
 
@@ -287,6 +295,21 @@ void StandardPLPanel::createIconView()
     layout->addWidget( iconView, 1, 0, 1, -1 );
 }
 
+void StandardPLPanel::createListView()
+{
+    listView = new PlListView( model, this );
+    listView->setContextMenuPolicy( Qt::CustomContextMenu );
+    CONNECT( listView, customContextMenuRequested( const QPoint & ),
+             this, popupPlView( const QPoint & ) );
+    CONNECT( listView, activated( const QModelIndex & ),
+             this, activate( const QModelIndex & ) );
+    CONNECT( locationBar, invoked( const QModelIndex & ),
+             listView, setRootIndex( const QModelIndex & ) );
+
+    layout->addWidget( listView, 1, 0, 1, -1 );
+}
+
+
 void StandardPLPanel::createTreeView()
 {
     /* Create and configure the QTreeView */
@@ -354,9 +377,10 @@ void StandardPLPanel::showView( int i_view )
             createTreeView();
         locationBar->setIndex( treeView->rootIndex() );
         if( iconView ) iconView->hide();
+        if( listView ) listView->hide();
         treeView->show();
         currentView = treeView;
-        treeViewAction->setChecked( true );
+        viewActions[i_view]->setChecked( true );
         break;
     }
     case ICON_VIEW:
@@ -364,11 +388,31 @@ void StandardPLPanel::showView( int i_view )
         if( iconView == NULL )
             createIconView();
 
-        locationBar->setIndex( iconView->rootIndex() );
         if( treeView ) treeView->hide();
+        if( listView ) {
+            listView->hide();
+            iconView->setRootIndex( listView->rootIndex() );
+        }
+        locationBar->setIndex( iconView->rootIndex() );
         iconView->show();
         currentView = iconView;
-        iconViewAction->setChecked( true );
+        viewActions[i_view]->setChecked( true );
+        break;
+    }
+    case LIST_VIEW:
+    {
+        if( listView == NULL )
+            createListView();
+
+        if( treeView ) treeView->hide();
+        if( iconView ) {
+            iconView->hide();
+            listView->setRootIndex( iconView->rootIndex() );
+        }
+        locationBar->setIndex( listView->rootIndex() );
+        listView->show();
+        currentView = listView;
+        viewActions[i_view]->setChecked( true );
         break;
     }
     default:;
@@ -380,6 +424,8 @@ void StandardPLPanel::cycleViews()
     if( currentView == iconView )
         showView( TREE_VIEW );
     else if( currentView == treeView )
+        showView( LIST_VIEW );
+    else if( currentView == listView )
         showView( ICON_VIEW );
     else
         assert( 0 );
@@ -395,8 +441,8 @@ void StandardPLPanel::activate( const QModelIndex &index )
 {
     if( model->hasChildren( index ) )
     {
-        if( currentView == iconView ) {
-            iconView->setRootIndex( index );
+        if( currentView == iconView || currentView == listView ) {
+            currentView->setRootIndex( index );
             locationBar->setIndex( index );
         }
     }
@@ -429,8 +475,8 @@ void StandardPLPanel::browseInto( input_item_t *p_input )
 
     playlist_Unlock( THEPL );
 
-    if( currentView == iconView ) {
-        iconView->setRootIndex( index );
+    if( currentView == iconView || currentView == listView ) {
+        currentView->setRootIndex( index );
         locationBar->setIndex( index );
     }
     else
diff --git a/modules/gui/qt4/components/playlist/standardpanel.hpp b/modules/gui/qt4/components/playlist/standardpanel.hpp
index ed1ed2f..586cc64 100644
--- a/modules/gui/qt4/components/playlist/standardpanel.hpp
+++ b/modules/gui/qt4/components/playlist/standardpanel.hpp
@@ -46,6 +46,7 @@ class QPushButton;
 class QKeyEvent;
 class QWheelEvent;
 class PlIconView;
+class PlListView;
 class LocationBar;
 
 class StandardPLPanel: public QWidget
@@ -64,6 +65,14 @@ protected:
 
     PLModel *model;
 private:
+    enum {
+      TREE_VIEW = 0,
+      ICON_VIEW,
+      LIST_VIEW,
+
+      VIEW_COUNT
+    };
+
     intf_thread_t *p_intf;
 
     QWidget     *parent;
@@ -74,8 +83,10 @@ private:
 
     QTreeView   *treeView;
     PlIconView  *iconView;
+    PlListView  *listView;
     QAbstractItemView *currentView;
 
+    QAction *viewActions[ VIEW_COUNT ];
     QAction *iconViewAction, *treeViewAction;
     int currentRootId;
     QSignalMapper *selectColumnsSigMapper;
@@ -83,14 +94,9 @@ private:
 
     int last_activated_id;
 
-    enum {
-      TREE_VIEW = 0,
-      ICON_VIEW,
-      COVER_VIEW,
-    };
-
     void createTreeView();
     void createIconView();
+    void createListView();
 
 public slots:
     virtual void setRoot( playlist_item_t * );
-- 
1.6.3.3

