1 /*****************************************************************************
2 * icon_view.cpp : Icon view for the Playlist
3 ****************************************************************************
4 * Copyright © 2010 the VideoLAN team
7 * Authors: Jean-Baptiste Kempf <jb@videolan.org>
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.
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.
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 *****************************************************************************/
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"
29 #include <QApplication>
32 #include <QStyleOptionViewItem>
33 #include <QFontMetrics>
34 #include <QPixmapCache>
36 #include <QDragMoveEvent>
40 #define ART_SIZE_W 110
45 QString AbstractPlViewItemDelegate::getMeta( const QModelIndex & index, int meta ) const
47 return index.model()->index( index.row(),
48 PLModel::columnFromMeta( meta ),
53 void AbstractPlViewItemDelegate::paintBackground(
54 QPainter *painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
56 /* FIXME: This does not indicate item selection in all QStyles, so for the time being we
57 have to draw it ourselves, to ensure visible effect of selection on all platforms */
58 /* QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &option,
62 QRect r = option.rect.adjusted( 0, 0, -1, -1 );
63 if( option.state & QStyle::State_Selected )
65 painter->setBrush( option.palette.color( QPalette::Highlight ) );
66 painter->setPen( option.palette.color( QPalette::Highlight ).darker( 150 ) );
67 painter->drawRect( r );
69 else if( index.data( PLModel::IsCurrentRole ).toBool() )
71 painter->setBrush( QBrush( Qt::lightGray ) );
72 painter->setPen( QColor( Qt::darkGray ) );
73 painter->drawRect( r );
75 if( option.state & QStyle::State_MouseOver )
77 painter->setOpacity( 0.5 );
78 painter->setPen( Qt::NoPen );
79 painter->setBrush( option.palette.color( QPalette::Highlight ).lighter( 150 ) );
80 painter->drawRect( option.rect );
85 QPixmap AbstractPlViewItemDelegate::getArtPixmap( const QModelIndex & index, const QSize & size ) const
87 PLItem *item = static_cast<PLItem*>( index.internalPointer() );
90 QString artUrl = InputManager::decodeArtURL( item->inputItem() );
92 if( artUrl.isEmpty() )
94 for( int i = 0; i < item->childCount(); i++ )
96 artUrl = InputManager::decodeArtURL( item->child( i )->inputItem() );
97 if( !artUrl.isEmpty() )
104 QString key = artUrl + QString("%1%2").arg(size.width()).arg(size.height());
106 if( !QPixmapCache::find( key, artPix ))
108 if( artUrl.isEmpty() || !artPix.load( artUrl ) )
110 key = QString("noart%1%2").arg(size.width()).arg(size.height());
111 if( !QPixmapCache::find( key, artPix ) )
113 artPix = QPixmap( ":/noart" ).scaled( size,
115 Qt::SmoothTransformation );
116 QPixmapCache::insert( key, artPix );
121 artPix = artPix.scaled( size, Qt::KeepAspectRatio, Qt::SmoothTransformation );
122 QPixmapCache::insert( key, artPix );
129 void PlIconViewItemDelegate::paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
131 QString title = getMeta( index, COLUMN_TITLE );
132 QString artist = getMeta( index, COLUMN_ARTIST );
134 QPixmap artPix = getArtPixmap( index, QSize( ART_SIZE_W, ART_SIZE_H ) );
136 paintBackground( painter, option, index );
140 QRect artRect( option.rect.x() + 5 + ( ART_SIZE_W - artPix.width() ) / 2,
141 option.rect.y() + 5 + ( ART_SIZE_H - artPix.height() ) / 2,
142 artPix.width(), artPix.height() );
144 // Draw the drop shadow
146 painter->setOpacity( 0.7 );
147 painter->setBrush( QBrush( Qt::darkGray ) );
148 painter->setPen( Qt::NoPen );
149 painter->drawRoundedRect( artRect.adjusted( 0, 0, 2, 2 ), ART_RADIUS, ART_RADIUS );
152 // Draw the art pixmap
153 QPainterPath artRectPath;
154 artRectPath.addRoundedRect( artRect, ART_RADIUS, ART_RADIUS );
155 painter->setClipPath( artRectPath );
156 painter->drawPixmap( artRect, artPix );
157 painter->setClipping( false );
159 if( option.state & QStyle::State_Selected )
160 painter->setPen( option.palette.color( QPalette::HighlightedText ) );
162 QFont font( index.data( Qt::FontRole ).value<QFont>() );
164 //Draw children indicator
165 if( !index.data( PLModel::IsLeafNodeRole ).toBool() )
167 QRect r( option.rect );
168 r.setSize( QSize( 25, 25 ) );
170 if( index.data( PLModel::IsCurrentsParentNodeRole ).toBool() )
172 painter->setOpacity( 0.75 );
173 QPainterPath nodeRectPath;
174 nodeRectPath.addRoundedRect( r, 4, 4 );
175 painter->fillPath( nodeRectPath, option.palette.color( QPalette::Highlight ) );
176 painter->setOpacity( 1.0 );
178 QPixmap dirPix( ":/type/node" );
179 QRect r2( dirPix.rect() );
180 r2.moveCenter( r.center() );
181 painter->drawPixmap( r2, dirPix );
185 font.setItalic( true );
186 painter->setFont( font );
188 QFontMetrics fm = painter->fontMetrics();
189 QRect textRect = option.rect.adjusted( 1, ART_SIZE_H + 10, 0, -1 );
190 textRect.setHeight( fm.height() );
192 painter->drawText( textRect,
193 fm.elidedText( title, Qt::ElideRight, textRect.width() ),
194 QTextOption( Qt::AlignCenter ) );
197 painter->setPen( painter->pen().color().lighter( 150 ) );
198 font.setItalic( false );
199 painter->setFont( font );
200 fm = painter->fontMetrics();
202 textRect.moveTop( textRect.bottom() + 1 );
204 painter->drawText( textRect,
205 fm.elidedText( artist, Qt::ElideRight, textRect.width() ),
206 QTextOption( Qt::AlignCenter ) );
211 QSize PlIconViewItemDelegate::sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const
213 QFont f( index.data( Qt::FontRole ).value<QFont>() );
215 QFontMetrics fm( f );
216 int textHeight = fm.height();
217 QSize sz ( ART_SIZE_W + 2 * SPACER,
218 ART_SIZE_H + 3 * SPACER + 2 * textHeight + 1 );
223 #define LISTVIEW_ART_SIZE 45
225 void PlListViewItemDelegate::paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
227 QModelIndex parent = index.parent();
230 QString title = getMeta( index, COLUMN_TITLE );
231 QString duration = getMeta( index, COLUMN_DURATION );
232 if( !duration.isEmpty() ) title += QString(" [%1]").arg( duration );
234 QString artist = getMeta( index, COLUMN_ARTIST );
235 QString album = getMeta( index, COLUMN_ALBUM );
236 QString trackNum = getMeta( index, COLUMN_TRACK_NUMBER );
237 QString artistAlbum = artist;
238 if( !album.isEmpty() )
240 if( !artist.isEmpty() ) artistAlbum += ": ";
241 artistAlbum += album;
242 if( !trackNum.isEmpty() ) artistAlbum += QString( " [#%1]" ).arg( trackNum );
245 QPixmap artPix = getArtPixmap( index, QSize( LISTVIEW_ART_SIZE, LISTVIEW_ART_SIZE ) );
247 //Draw selection rectangle and current playing item indication
248 paintBackground( painter, option, index );
250 QRect artRect( artPix.rect() );
251 artRect.moveCenter( QPoint( artRect.center().x() + 3,
252 option.rect.center().y() ) );
254 painter->drawPixmap( artRect, artPix );
259 if( option.state & QStyle::State_Selected )
260 painter->setPen( option.palette.color( QPalette::HighlightedText ) );
262 QTextOption textOpt( Qt::AlignVCenter | Qt::AlignLeft );
263 textOpt.setWrapMode( QTextOption::NoWrap );
265 QFont f( index.data( Qt::FontRole ).value<QFont>() );
269 painter->setFont( f );
270 QFontMetrics fm( painter->fontMetrics() );
272 QRect textRect = option.rect.adjusted( LISTVIEW_ART_SIZE + 10, 0, -10, 0 );
273 if( !artistAlbum.isEmpty() )
275 textRect.setHeight( fm.height() );
276 textRect.moveBottom( option.rect.center().y() - 2 );
279 //Draw children indicator
280 if( !index.data( PLModel::IsLeafNodeRole ).toBool() )
282 QPixmap dirPix = QPixmap( ":/type/node" );
283 painter->drawPixmap( QPoint( textRect.x(), textRect.center().y() - dirPix.height() / 2 ),
285 textRect.setLeft( textRect.x() + dirPix.width() + 5 );
288 painter->drawText( textRect,
289 fm.elidedText( title, Qt::ElideRight, textRect.width() ),
292 // Draw artist and album info
293 if( !artistAlbum.isEmpty() )
295 f.setItalic( false );
296 painter->setFont( f );
297 fm = painter->fontMetrics();
299 textRect.moveTop( textRect.bottom() + 4 );
300 textRect.setLeft( textRect.x() + 20 );
302 painter->drawText( textRect,
303 fm.elidedText( artistAlbum, Qt::ElideRight, textRect.width() ),
310 QSize PlListViewItemDelegate::sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const
314 QFontMetrics fm( f );
315 int height = qMax( LISTVIEW_ART_SIZE, 2 * fm.height() + 4 ) + 6;
316 return QSize( 0, height );
319 static void plViewStartDrag( QAbstractItemView *view, const Qt::DropActions & supportedActions )
321 QDrag *drag = new QDrag( view );
322 drag->setPixmap( QPixmap( ":/noart64" ) );
323 drag->setMimeData( view->model()->mimeData(
324 view->selectionModel()->selectedIndexes() ) );
325 drag->exec( supportedActions );
328 static void plViewDragMoveEvent( QAbstractItemView *view, QDragMoveEvent * event )
330 if( event->keyboardModifiers() & Qt::ControlModifier &&
331 event->possibleActions() & Qt::CopyAction )
332 event->setDropAction( Qt::CopyAction );
333 else event->acceptProposedAction();
336 PlIconView::PlIconView( PLModel *model, QWidget *parent ) : QListView( parent )
338 PlIconViewItemDelegate *delegate = new PlIconViewItemDelegate( this );
341 setViewMode( QListView::IconMode );
342 setMovement( QListView::Static );
343 setResizeMode( QListView::Adjust );
344 setGridSize( delegate->sizeHint() );
346 setUniformItemSizes( true );
347 setSelectionMode( QAbstractItemView::ExtendedSelection );
348 setDragEnabled(true);
349 /* dropping in QListView::IconMode does not seem to work */
350 //setAcceptDrops( true );
351 //setDropIndicatorShown(true);
353 setItemDelegate( delegate );
356 void PlIconView::startDrag ( Qt::DropActions supportedActions )
358 plViewStartDrag( this, supportedActions );
361 void PlIconView::dragMoveEvent ( QDragMoveEvent * event )
363 plViewDragMoveEvent( this, event );
364 QAbstractItemView::dragMoveEvent( event );
367 PlListView::PlListView( PLModel *model, QWidget *parent ) : QListView( parent )
370 setViewMode( QListView::ListMode );
371 setUniformItemSizes( true );
372 setSelectionMode( QAbstractItemView::ExtendedSelection );
373 setAlternatingRowColors( true );
374 setDragEnabled(true);
375 setAcceptDrops( true );
376 setDropIndicatorShown(true);
378 PlListViewItemDelegate *delegate = new PlListViewItemDelegate( this );
379 setItemDelegate( delegate );
382 void PlListView::startDrag ( Qt::DropActions supportedActions )
384 plViewStartDrag( this, supportedActions );
387 void PlListView::dragMoveEvent ( QDragMoveEvent * event )
389 plViewDragMoveEvent( this, event );
390 QAbstractItemView::dragMoveEvent( event );
393 void PlListView::keyPressEvent( QKeyEvent *event )
395 //If the space key is pressed, override the standard list behaviour to allow pausing
397 if ( event->modifiers() == Qt::NoModifier && event->key() == Qt::Key_Space )
398 QWidget::keyPressEvent( event );
399 //Otherwise, just do as usual.
401 QListView::keyPressEvent( event );
404 void PlTreeView::startDrag ( Qt::DropActions supportedActions )
406 plViewStartDrag( this, supportedActions );
409 void PlTreeView::dragMoveEvent ( QDragMoveEvent * event )
411 plViewDragMoveEvent( this, event );
412 QAbstractItemView::dragMoveEvent( event );
415 void PlTreeView::keyPressEvent( QKeyEvent *event )
417 //If the space key is pressed, override the standard list behaviour to allow pausing
419 if ( event->modifiers() == Qt::NoModifier && event->key() == Qt::Key_Space )
420 QWidget::keyPressEvent( event );
421 //Otherwise, just do as usual.
423 QTreeView::keyPressEvent( event );