]> git.sesse.net Git - vlc/blob - modules/gui/qt4/components/playlist/icon_view.cpp
f60ce0e9c12d1e0abee4b06e85a3ff21cb9c96b0
[vlc] / modules / gui / qt4 / components / playlist / icon_view.cpp
1 /*****************************************************************************
2  * icon_view.cpp : Icon view for the Playlist
3  ****************************************************************************
4  * Copyright © 2010 the VideoLAN team
5  * $Id$
6  *
7  * Authors:         Jean-Baptiste Kempf <jb@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #include "components/playlist/icon_view.hpp"
25 #include "components/playlist/playlist_model.hpp"
26 #include "components/playlist/sorting.h"
27 #include "input_manager.hpp"
28
29 #include <QApplication>
30 #include <QPainter>
31 #include <QRect>
32 #include <QStyleOptionViewItem>
33 #include <QFontMetrics>
34 #include <QPixmapCache>
35
36 #include "assert.h"
37
38 #define ART_SIZE_W          110
39 #define ART_SIZE_H          80
40 #define ART_RADIUS          5
41 #define SPACER              5
42
43 QString AbstractPlViewItemDelegate::getMeta( const QModelIndex & index, int meta ) const
44 {
45     return index.model()->index( index.row(),
46                                   PLModel::columnFromMeta( meta ),
47                                   index.parent() )
48                                 .data().toString();
49 }
50
51 void AbstractPlViewItemDelegate::paintBackground(
52     QPainter *painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
53 {
54     /* FIXME: This does not indicate item selection in all QStyles, so for the time being we
55        have to draw it ourselves, to ensure visible effect of selection on all platforms */
56     /* QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &option,
57                                             painter ); */
58
59     painter->save();
60     QRect r = option.rect.adjusted( 0, 0, -1, -1 );
61     if( option.state & QStyle::State_Selected )
62     {
63         painter->setBrush( option.palette.color( QPalette::Highlight ) );
64         painter->setPen( option.palette.color( QPalette::Highlight ).darker( 150 ) );
65         painter->drawRect( r );
66     }
67     else if( index.data( PLModel::IsCurrentRole ).toBool() )
68     {
69         painter->setBrush( QBrush( Qt::lightGray ) );
70         painter->setPen( QColor( Qt::darkGray ) );
71         painter->drawRect( r );
72     }
73     if( option.state & QStyle::State_MouseOver )
74     {
75         painter->setOpacity( 0.5 );
76         painter->setPen( Qt::NoPen );
77         painter->setBrush( option.palette.color( QPalette::Highlight ).lighter( 150 ) );
78         painter->drawRect( option.rect );
79     }
80     painter->restore();
81 }
82
83 QPixmap AbstractPlViewItemDelegate::getArtPixmap( const QModelIndex & index, const QSize & size ) const
84 {
85     PLItem *item = static_cast<PLItem*>( index.internalPointer() );
86     assert( item );
87
88     QString artUrl = InputManager::decodeArtURL( item->inputItem() );
89
90     if( artUrl.isEmpty() )
91     {
92         for( int i = 0; i < item->childCount(); i++ )
93         {
94             artUrl = InputManager::decodeArtURL( item->child( i )->inputItem() );
95             if( !artUrl.isEmpty() )
96                 break;
97         }
98     }
99
100     QPixmap artPix;
101
102     QString key = artUrl + QString("%1%2").arg(size.width()).arg(size.height());
103
104     if( !QPixmapCache::find( key, artPix ))
105     {
106         if( artUrl.isEmpty() || !artPix.load( artUrl ) )
107         {
108             key = QString("noart%1%2").arg(size.width()).arg(size.height());
109             if( !QPixmapCache::find( key, artPix ) )
110             {
111                 artPix = QPixmap( ":/noart" ).scaled( size,
112                                                       Qt::KeepAspectRatio,
113                                                       Qt::SmoothTransformation );
114                 QPixmapCache::insert( key, artPix );
115             }
116         }
117         else
118         {
119             artPix = artPix.scaled( size, Qt::KeepAspectRatio, Qt::SmoothTransformation );
120             QPixmapCache::insert( key, artPix );
121         }
122     }
123
124     return artPix;
125 }
126
127 void PlIconViewItemDelegate::paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
128 {
129     QString title = getMeta( index, COLUMN_TITLE );
130     QString artist = getMeta( index, COLUMN_ARTIST );
131
132     QPixmap artPix = getArtPixmap( index, QSize( ART_SIZE_W, ART_SIZE_H ) );
133
134     paintBackground( painter, option, index );
135
136     painter->save();
137
138     QRect artRect( option.rect.x() + 5 + ( ART_SIZE_W - artPix.width() ) / 2,
139                    option.rect.y() + 5 + ( ART_SIZE_H - artPix.height() ) / 2,
140                    artPix.width(), artPix.height() );
141
142     // Draw the drop shadow
143     painter->save();
144     painter->setOpacity( 0.7 );
145     painter->setBrush( QBrush( Qt::darkGray ) );
146     painter->setPen( Qt::NoPen );
147     painter->drawRoundedRect( artRect.adjusted( 0, 0, 2, 2 ), ART_RADIUS, ART_RADIUS );
148     painter->restore();
149
150     // Draw the art pixmap
151     QPainterPath artRectPath;
152     artRectPath.addRoundedRect( artRect, ART_RADIUS, ART_RADIUS );
153     painter->setClipPath( artRectPath );
154     painter->drawPixmap( artRect, artPix );
155     painter->setClipping( false );
156
157     if( option.state & QStyle::State_Selected )
158         painter->setPen( option.palette.color( QPalette::HighlightedText ) );
159
160     QFont font( index.data( Qt::FontRole ).value<QFont>() );
161     font.setPointSize( 7 );
162
163     //Draw children indicator
164     if( !index.data( PLModel::IsLeafNodeRole ).toBool() )
165     {
166         painter->setOpacity( 0.75 );
167         QRect r( option.rect );
168         r.setSize( QSize( 25, 25 ) );
169         r.translate( 5, 5 );
170         painter->fillRect( r, option.palette.color( QPalette::Mid ) );
171         painter->setOpacity( 1.0 );
172         QPixmap dirPix( ":/type/node" );
173         QRect r2( dirPix.rect() );
174         r2.moveCenter( r.center() );
175         painter->drawPixmap( r2, dirPix );
176     }
177
178     // Draw title
179     font.setItalic( true );
180     painter->setFont( font );
181
182     QFontMetrics fm = painter->fontMetrics();
183     QRect textRect = option.rect.adjusted( 1, ART_SIZE_H + 10, 0, -1 );
184     textRect.setHeight( fm.height() );
185
186     painter->drawText( textRect,
187                       fm.elidedText( title, Qt::ElideRight, textRect.width() ),
188                       QTextOption( Qt::AlignCenter ) );
189
190     // Draw artist
191     painter->setPen( painter->pen().color().lighter( 150 ) );
192     font.setItalic( false );
193     painter->setFont( font );
194     fm = painter->fontMetrics();
195
196     textRect.moveTop( textRect.bottom() + 1 );
197
198     painter->drawText(  textRect,
199                         fm.elidedText( artist, Qt::ElideRight, textRect.width() ),
200                         QTextOption( Qt::AlignCenter ) );
201
202     painter->restore();
203 }
204
205 QSize PlIconViewItemDelegate::sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const
206 {
207     QFont f;
208     f.setPointSize( 7 );
209     f.setBold( true );
210     QFontMetrics fm( f );
211     int textHeight = fm.height();
212     QSize sz ( ART_SIZE_W + 2 * SPACER,
213                ART_SIZE_H + 3 * SPACER + 2 * textHeight + 1 );
214     return sz;
215 }
216
217
218 #define LISTVIEW_ART_SIZE 45
219
220 void PlListViewItemDelegate::paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
221 {
222     QModelIndex parent = index.parent();
223     QModelIndex i;
224
225     QString title = getMeta( index, COLUMN_TITLE );
226     QString duration = getMeta( index, COLUMN_DURATION );
227     if( !duration.isEmpty() ) title += QString(" [%1]").arg( duration );
228
229     QString artist = getMeta( index, COLUMN_ARTIST );
230     QString album = getMeta( index, COLUMN_ALBUM );
231     QString trackNum = getMeta( index, COLUMN_TRACK_NUMBER );
232     QString artistAlbum = artist
233                           + ( artist.isEmpty() ? QString() : QString( ": " ) )
234                           + album
235                           + ( album.isEmpty() || trackNum.isEmpty() ?
236                               QString() : QString( " [#%1]" ).arg( trackNum ) );
237
238     QPixmap artPix = getArtPixmap( index, QSize( LISTVIEW_ART_SIZE, LISTVIEW_ART_SIZE ) );
239
240     //Draw selection rectangle and current playing item indication
241     paintBackground( painter, option, index );
242
243     QRect artRect( artPix.rect() );
244     artRect.moveCenter( QPoint( artRect.center().x() + 3,
245                                 option.rect.center().y() ) );
246     //Draw album art
247     painter->drawPixmap( artRect, artPix );
248
249     //Start drawing text
250     painter->save();
251
252     if( option.state & QStyle::State_Selected )
253         painter->setPen( option.palette.color( QPalette::HighlightedText ) );
254
255     QTextOption textOpt( Qt::AlignVCenter | Qt::AlignLeft );
256     textOpt.setWrapMode( QTextOption::NoWrap );
257
258     QFont f( index.data( Qt::FontRole ).value<QFont>() );
259
260     //Draw title info
261     f.setItalic( true );
262     painter->setFont( f );
263     QFontMetrics fm( painter->fontMetrics() );
264
265     QRect textRect = option.rect.adjusted( LISTVIEW_ART_SIZE + 10, 0, -10, 0 );
266     if( !artistAlbum.isEmpty() )
267     {
268         textRect.setHeight( fm.height() );
269         textRect.moveBottom( option.rect.center().y() - 2 );
270     }
271
272     //Draw children indicator
273     if( !index.data( PLModel::IsLeafNodeRole ).toBool() )
274     {
275         QPixmap dirPix = QPixmap( ":/type/node" );
276         painter->drawPixmap( QPoint( textRect.x(), textRect.center().y() - dirPix.height() / 2 ),
277                              dirPix );
278         textRect.setLeft( textRect.x() + dirPix.width() + 5 );
279     }
280
281     painter->drawText( textRect,
282                        fm.elidedText( title, Qt::ElideRight, textRect.width() ),
283                        textOpt );
284
285     // Draw artist and album info
286     if( !artistAlbum.isEmpty() )
287     {
288         f.setItalic( false );
289         painter->setFont( f );
290         fm = painter->fontMetrics();
291
292         textRect.moveTop( textRect.bottom() + 4 );
293         textRect.setLeft( textRect.x() + 20 );
294
295         painter->drawText( textRect,
296                            fm.elidedText( artistAlbum, Qt::ElideRight, textRect.width() ),
297                            textOpt );
298     }
299
300     painter->restore();
301 }
302
303 QSize PlListViewItemDelegate::sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const
304 {
305   QFont f;
306   f.setBold( true );
307   QFontMetrics fm( f );
308   int height = qMax( LISTVIEW_ART_SIZE, 2 * fm.height() + 4 ) + 6;
309   return QSize( 0, height );
310 }
311
312 PlIconView::PlIconView( PLModel *model, QWidget *parent ) : QListView( parent )
313 {
314     PlIconViewItemDelegate *delegate = new PlIconViewItemDelegate( this );
315
316     setModel( model );
317     setViewMode( QListView::IconMode );
318     setMovement( QListView::Static );
319     setResizeMode( QListView::Adjust );
320     setGridSize( delegate->sizeHint() );
321     setWrapping( true );
322     setUniformItemSizes( true );
323     setSelectionMode( QAbstractItemView::ExtendedSelection );
324     setDragEnabled(true);
325     /* dropping in QListView::IconMode does not seem to work */
326     //setAcceptDrops( true );
327     //setDropIndicatorShown(true);
328
329     setItemDelegate( delegate );
330 }
331
332 PlListView::PlListView( PLModel *model, QWidget *parent ) : QListView( parent )
333 {
334     setModel( model );
335     setViewMode( QListView::ListMode );
336     setUniformItemSizes( true );
337     setSelectionMode( QAbstractItemView::ExtendedSelection );
338     setAlternatingRowColors( true );
339     setDragEnabled(true);
340     setAcceptDrops( true );
341     setDropIndicatorShown(true);
342
343     PlListViewItemDelegate *delegate = new PlListViewItemDelegate( this );
344     setItemDelegate( delegate );
345 }