Just as a relaxing sunday evening project, try to create a simple, alternate playlist...
[amarok.git] / src / playlist / PlaylistGraphicsItem.cpp
blob8f7857f73a408d9af201db38b5cbd2c624ed9f9b
1 /***************************************************************************
2 * copyright : (C) 2007 Ian Monroe <ian@monroe.nu> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License version 2 *
6 * as published by the Free Software Foundation. *
7 ***************************************************************************/
9 #include "debug.h"
10 #include "meta/MetaUtility.h"
11 #include "AmarokMimeData.h"
12 #include "PlaylistGraphicsItem.h"
13 #include "PlaylistGraphicsView.h"
14 #include "PlaylistDropVis.h"
15 #include "PlaylistModel.h"
16 #include "PlaylistTextItem.h"
17 #include "TheInstances.h"
19 #include <QBrush>
20 #include <QDrag>
21 #include <QFontMetricsF>
22 #include <QGraphicsScene>
23 #include <QGraphicsTextItem>
24 #include <QGraphicsPixmapItem>
25 #include <QGraphicsRectItem>
26 #include <QGraphicsSceneMouseEvent>
27 #include <QGraphicsView>
28 #include <QMimeData>
29 #include <QPen>
30 #include <QRadialGradient>
31 #include <QScrollBar>
32 #include <QStyleOptionGraphicsItem>
34 struct Playlist::GraphicsItem::ActiveItems
36 ActiveItems()
37 : albumArt( 0 )
38 , background( 0 )
39 , foreground( 0 )
40 , bottomLeftText( 0 )
41 , bottomRightText( 0 )
42 , topLeftText( 0 )
43 , topRightText( 0 )
44 , lastWidth( -5 )
45 { }
46 ~ActiveItems()
48 delete albumArt;
49 delete background;
50 delete bottomLeftText;
51 delete bottomRightText;
52 delete foreground;
53 delete topLeftText;
54 delete topRightText;
56 QGraphicsPixmapItem* albumArt;
57 QGraphicsRectItem* background;
58 QGraphicsRectItem* foreground;
59 Playlist::TextItem* bottomLeftText;
60 Playlist::TextItem* bottomRightText;
61 Playlist::TextItem* topLeftText;
62 Playlist::TextItem* topRightText;
63 int lastWidth;
65 QRectF preDragLocation;
66 Meta::TrackPtr track;
69 const qreal Playlist::GraphicsItem::ALBUM_WIDTH = 50.0;
70 const qreal Playlist::GraphicsItem::MARGIN = 2.0;
71 qreal Playlist::GraphicsItem::s_height = -1.0;
72 QFontMetricsF* Playlist::GraphicsItem::s_fm = 0;
74 Playlist::GraphicsItem::GraphicsItem()
75 : QGraphicsItem()
76 , m_items( 0 )
78 setZValue( 1.0 );
79 if( !s_fm )
81 s_fm = new QFontMetricsF( QFont() );
82 s_height = qMax( ALBUM_WIDTH, s_fm->height() * 2 ) + 2 * MARGIN;
84 setFlag( QGraphicsItem::ItemIsSelectable );
85 setFlag( QGraphicsItem::ItemIsMovable );
86 setAcceptDrops( true );
87 // setHandlesChildEvents( true ); // don't let drops etc hit the text items, doing stupid things
90 Playlist::GraphicsItem::~GraphicsItem()
92 delete m_items;
95 void
96 Playlist::GraphicsItem::paint( QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget )
98 // ::paint RULES:
99 // 1) You do not talk about ::paint method
100 // 2) You DO NOT talk about ::paint method
101 // 3) Do not show or hide item that are already shown or hidden, respectively
102 // 4) Do not setBrush without making sure its hasn't already been set to that brush().
103 // 5) If this is your first night at ::paint method, you HAVE to paint.
104 Q_UNUSED( painter ); Q_UNUSED( widget );
105 const int row = getRow();
106 const QModelIndex index = The::playlistModel()->index( row, 0 );
108 if( !m_items || ( option->rect.width() != m_items->lastWidth ) )
111 if( !m_items )
113 const Meta::TrackPtr track = index.data( ItemRole ).value< Playlist::Item* >()->track();
114 m_items = new Playlist::GraphicsItem::ActiveItems();
115 m_items->track = track;
116 init( track );
118 resize( m_items->track, option->rect.width() );
121 { //background mania. blue if selected, alternating white/gray otherwise.
122 #define SetBackgroundBrush( B ) \
123 if( m_items->background->brush() != B ) \
124 m_items->background->setBrush( B );
125 if( !m_items->background )
127 m_items->background = new QGraphicsRectItem( option->rect, this );
128 m_items->background->setPos( 0.0, 0.0 );
129 m_items->background->setPen( QPen( Qt::NoPen ) );
130 m_items->background->setZValue( -5.0 );
131 m_items->background->show();
133 if( option->state & QStyle::State_Selected )
135 SetBackgroundBrush( option->palette.highlight() );
137 else if( row % 2 )
139 SetBackgroundBrush( option->palette.base() );
141 else
143 SetBackgroundBrush( option->palette.alternateBase() );
145 #undef SetBrush
148 if( index.data( ActiveTrackRole ).toBool() )
150 if( !m_items->foreground )
152 m_items->foreground = new QGraphicsRectItem( option->rect, this );
153 m_items->foreground->setPos( 0.0, MARGIN );
154 m_items->foreground->setZValue( 5.0 );
155 QRadialGradient gradient(option->rect.width() / 2.0, option->rect.height() / 2.0, option->rect.width() / 2.0, 20 + option->rect.width() / 2.0, option->rect.height() / 2.0 );
156 QColor start = option->palette.highlight().color().light();
157 start.setAlpha( 51 );
158 QColor end = option->palette.highlight().color().dark();
159 end.setAlpha( 51 );
160 gradient.setColorAt( 0.0, start );
161 gradient.setColorAt( 1.0, end );
162 QBrush brush( gradient );
163 m_items->foreground->setBrush( brush );
164 m_items->foreground->setPen( QPen( Qt::NoPen ) );
166 if( !m_items->foreground->isVisible() )
167 m_items->foreground->show();
169 else if( m_items->foreground && m_items->foreground->isVisible() )
170 m_items->foreground->hide();
173 void
174 Playlist::GraphicsItem::init( Meta::TrackPtr track )
176 QPixmap albumPixmap;
177 if( track->album() )
178 albumPixmap = track->album()->image( int( ALBUM_WIDTH ) );
180 m_items->albumArt = new QGraphicsPixmapItem( albumPixmap, this );
181 m_items->albumArt->setPos( 0.0, MARGIN );
184 QFont font;
185 font.setPointSize( font.pointSize() - 1 );
186 #define NewText( X ) \
187 X = new Playlist::TextItem( this ); \
188 X->setTextInteractionFlags( Qt::TextEditorInteraction ); \
189 X->setFont( font );
190 NewText( m_items->topLeftText )
191 NewText( m_items->bottomLeftText )
192 NewText( m_items->topRightText )
193 NewText( m_items->bottomRightText )
194 #undef NewText
198 void
199 Playlist::GraphicsItem::resize( Meta::TrackPtr track, int totalWidth )
201 if( totalWidth == -1 || totalWidth == m_items->lastWidth ) //no change needed
202 return;
203 if( m_items->lastWidth != -5 ) //this isn't the first "resize"
204 prepareGeometryChange();
205 m_items->lastWidth = totalWidth;
206 QString prettyLength = Meta::secToPrettyTime( track->length() );
207 QString album;
208 if( track->album() )
209 album = track->album()->name();
211 const qreal lineTwoY = s_height / 2 + MARGIN;
212 const qreal textWidth = ( ( qreal( totalWidth ) - ALBUM_WIDTH ) / 2.0 );
213 const qreal leftAlignX = ALBUM_WIDTH + MARGIN;
214 qreal rightAlignX;
216 qreal middle = textWidth + ALBUM_WIDTH + ( MARGIN * 2.0 );
217 qreal rightWidth = totalWidth - qMax( s_fm->width( album )
218 , s_fm->width( prettyLength ) );
219 rightAlignX = qMax( middle, rightWidth );
221 m_items->topRightText->setPos( rightAlignX, MARGIN );
222 m_items->bottomRightText->setPos( rightAlignX, lineTwoY );
223 m_items->topRightText->setEditableText( album, totalWidth - rightAlignX );
224 m_items->bottomRightText->setEditableText( prettyLength, totalWidth - rightAlignX );
227 qreal spaceForLeft = totalWidth - ( totalWidth - rightAlignX ) - leftAlignX;
229 QString artist;
230 if( track->artist() )
231 artist = track->artist()->name();
232 m_items->topLeftText->setEditableText( artist, spaceForLeft );
233 m_items->topLeftText->setPos( leftAlignX, MARGIN );
236 m_items->bottomLeftText->setEditableText( QString("%1 - %2").arg( QString::number( track->trackNumber() ), track->name() )
237 , spaceForLeft );
238 m_items->bottomLeftText->setPos( leftAlignX, lineTwoY );
240 m_items->lastWidth = totalWidth;
243 QRectF
244 Playlist::GraphicsItem::boundingRect() const
246 // the viewport()->size() takes scrollbars into account
247 return QRectF( 0.0, 0.0, The::playlistView()->viewport()->size().width(), s_height );
250 void
251 Playlist::GraphicsItem::play()
253 The::playlistModel()->play( getRow() );
256 void
257 Playlist::GraphicsItem::mouseDoubleClickEvent( QGraphicsSceneMouseEvent *event )
259 if( m_items )
261 event->accept();
262 play();
263 return;
265 QGraphicsItem::mouseDoubleClickEvent( event );
268 void
269 Playlist::GraphicsItem::mousePressEvent( QGraphicsSceneMouseEvent *event )
271 if( event->buttons() & Qt::RightButton || !m_items )
273 event->ignore();
274 return;
276 m_items->preDragLocation = mapToScene( boundingRect() ).boundingRect();
277 QGraphicsItem::mousePressEvent( event );
280 // With help from QGraphicsView::mouseMoveEvent()
281 void
282 Playlist::GraphicsItem::mouseMoveEvent( QGraphicsSceneMouseEvent *event )
284 if( (event->buttons() & Qt::LeftButton) && ( flags() & QGraphicsItem::ItemIsMovable) && m_items )
286 QPointF scenePosition = event->scenePos();
288 if( scenePosition.y() < 0 )
289 return;
291 bool dragOverOriginalPosition = m_items->preDragLocation.contains( scenePosition );
293 //make sure item is drawn on top of other items
294 setZValue( 2.0 );
296 // Determine the list of selected items
297 QList<QGraphicsItem *> selectedItems = scene()->selectedItems();
298 if( !isSelected() )
299 selectedItems << this;
300 // Move all selected items
301 foreach( QGraphicsItem *item, selectedItems )
303 if( (item->flags() & QGraphicsItem::ItemIsMovable) && (!item->parentItem() || !item->parentItem()->isSelected()) )
305 Playlist::GraphicsItem *above = 0;
306 QPointF diff;
307 if( item == this && !dragOverOriginalPosition )
309 diff = event->scenePos() - event->lastScenePos();
310 QList<QGraphicsItem*> collisions = scene()->items( event->scenePos() );
311 foreach( QGraphicsItem *i, collisions )
313 Playlist::GraphicsItem *c = dynamic_cast<Playlist::GraphicsItem *>( i );
314 if( c && c != this )
316 above = c;
317 break;
321 else
323 diff = item->mapToParent( item->mapFromScene(event->scenePos()))
324 - item->mapToParent(item->mapFromScene(event->lastScenePos()));
327 item->moveBy( 0, diff.y() );
328 if( item->flags() & ItemIsSelectable )
329 item->setSelected( true );
331 if( dragOverOriginalPosition )
332 Playlist::DropVis::instance()->show( m_items->preDragLocation.y() );
333 else
334 Playlist::DropVis::instance()->show( above );
338 else
340 QGraphicsItem::mouseMoveEvent( event );
344 void
345 Playlist::GraphicsItem::dragEnterEvent( QGraphicsSceneDragDropEvent *event )
347 foreach( QString mime, The::playlistModel()->mimeTypes() )
349 if( event->mimeData()->hasFormat( mime ) )
351 event->accept();
352 Playlist::DropVis::instance()->show( this );
353 break;
358 void
359 Playlist::GraphicsItem::dropEvent( QGraphicsSceneDragDropEvent * event )
361 event->accept();
362 setZValue( 1.0 );
363 The::playlistModel()->dropMimeData( event->mimeData(), Qt::CopyAction, getRow(), 0, QModelIndex() );
364 Playlist::DropVis::instance()->hide();
367 void
368 Playlist::GraphicsItem::refresh()
370 QPixmap albumPixmap;
371 if( !m_items || !m_items->track )
372 return;
374 if( m_items->track->album() )
375 albumPixmap = m_items->track->album()->image( int( ALBUM_WIDTH ) );
377 m_items->albumArt->hide();
378 delete ( m_items->albumArt );
379 m_items->albumArt = new QGraphicsPixmapItem( albumPixmap, this );
380 m_items->albumArt->setPos( 0.0, MARGIN );
383 void Playlist::GraphicsItem::mouseReleaseEvent( QGraphicsSceneMouseEvent *event )
385 bool dragOverOriginalPosition = m_items->preDragLocation.contains( event->scenePos() );
386 if( dragOverOriginalPosition )
388 setPos( m_items->preDragLocation.topLeft() );
389 Playlist::DropVis::instance()->hide();
390 return;
393 Playlist::GraphicsItem *above = 0;
394 QList<QGraphicsItem*> collisions = scene()->items( event->scenePos() );
395 foreach( QGraphicsItem *i, collisions )
397 Playlist::GraphicsItem *c = dynamic_cast<Playlist::GraphicsItem *>( i );
398 if( c && c != this )
400 above = c;
401 break;
404 // if we've dropped ourself ontop of another item, then we need to shuffle the tracks below down
405 if( above )
407 setPos( above->pos() );
408 The::playlistView()->moveItem( this, above );
411 //make sure item resets its z value
412 setZValue( 1.0 );
413 Playlist::DropVis::instance()->hide();