1 /***************************************************************************
2 * copyright : (C) 2007 Ian Monroe <ian@monroe.nu> *
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 ***************************************************************************/
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"
21 #include <QFontMetricsF>
22 #include <QGraphicsScene>
23 #include <QGraphicsTextItem>
24 #include <QGraphicsPixmapItem>
25 #include <QGraphicsRectItem>
26 #include <QGraphicsSceneMouseEvent>
27 #include <QGraphicsView>
30 #include <QRadialGradient>
32 #include <QStyleOptionGraphicsItem>
34 struct Playlist::GraphicsItem::ActiveItems
41 , bottomRightText( 0 )
50 delete bottomLeftText
;
51 delete bottomRightText
;
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
;
65 QRectF preDragLocation
;
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()
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()
96 Playlist::GraphicsItem::paint( QPainter
* painter
, const QStyleOptionGraphicsItem
* option
, QWidget
* widget
)
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
) )
113 const Meta::TrackPtr track
= index
.data( ItemRole
).value
< Playlist::Item
* >()->track();
114 m_items
= new Playlist::GraphicsItem::ActiveItems();
115 m_items
->track
= 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() );
139 SetBackgroundBrush( option
->palette
.base() );
143 SetBackgroundBrush( option
->palette
.alternateBase() );
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();
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();
174 Playlist::GraphicsItem::init( Meta::TrackPtr track
)
178 albumPixmap
= track
->album()->image( int( ALBUM_WIDTH
) );
180 m_items
->albumArt
= new QGraphicsPixmapItem( albumPixmap
, this );
181 m_items
->albumArt
->setPos( 0.0, MARGIN
);
185 font
.setPointSize( font
.pointSize() - 1 );
186 #define NewText( X ) \
187 X = new Playlist::TextItem( this ); \
188 X->setTextInteractionFlags( Qt::TextEditorInteraction ); \
190 NewText( m_items
->topLeftText
)
191 NewText( m_items
->bottomLeftText
)
192 NewText( m_items
->topRightText
)
193 NewText( m_items
->bottomRightText
)
199 Playlist::GraphicsItem::resize( Meta::TrackPtr track
, int totalWidth
)
201 if( totalWidth
== -1 || totalWidth
== m_items
->lastWidth
) //no change needed
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() );
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
;
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
;
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() )
238 m_items
->bottomLeftText
->setPos( leftAlignX
, lineTwoY
);
240 m_items
->lastWidth
= totalWidth
;
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
);
251 Playlist::GraphicsItem::play()
253 The::playlistModel()->play( getRow() );
257 Playlist::GraphicsItem::mouseDoubleClickEvent( QGraphicsSceneMouseEvent
*event
)
265 QGraphicsItem::mouseDoubleClickEvent( event
);
269 Playlist::GraphicsItem::mousePressEvent( QGraphicsSceneMouseEvent
*event
)
271 if( event
->buttons() & Qt::RightButton
|| !m_items
)
276 m_items
->preDragLocation
= mapToScene( boundingRect() ).boundingRect();
277 QGraphicsItem::mousePressEvent( event
);
280 // With help from QGraphicsView::mouseMoveEvent()
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 )
291 bool dragOverOriginalPosition
= m_items
->preDragLocation
.contains( scenePosition
);
293 //make sure item is drawn on top of other items
296 // Determine the list of selected items
297 QList
<QGraphicsItem
*> selectedItems
= scene()->selectedItems();
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;
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
);
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() );
334 Playlist::DropVis::instance()->show( above
);
340 QGraphicsItem::mouseMoveEvent( event
);
345 Playlist::GraphicsItem::dragEnterEvent( QGraphicsSceneDragDropEvent
*event
)
347 foreach( QString mime
, The::playlistModel()->mimeTypes() )
349 if( event
->mimeData()->hasFormat( mime
) )
352 Playlist::DropVis::instance()->show( this );
359 Playlist::GraphicsItem::dropEvent( QGraphicsSceneDragDropEvent
* event
)
363 The::playlistModel()->dropMimeData( event
->mimeData(), Qt::CopyAction
, getRow(), 0, QModelIndex() );
364 Playlist::DropVis::instance()->hide();
368 Playlist::GraphicsItem::refresh()
371 if( !m_items
|| !m_items
->track
)
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();
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
);
404 // if we've dropped ourself ontop of another item, then we need to shuffle the tracks below down
407 setPos( above
->pos() );
408 The::playlistView()->moveItem( this, above
);
411 //make sure item resets its z value
413 Playlist::DropVis::instance()->hide();