fix some painting bugs when moving things around in album groups. Left in a bit of...
[amarok.git] / src / playlist / PlaylistGraphicsView.cpp
blob8e5eb340d587be49c8df7b45a18ed7564b080f69
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 "amarok.h"
10 #include "debug.h"
11 #include "PlaylistModel.h"
12 #include "PlaylistGraphicsItem.h"
13 #include "PlaylistGraphicsView.h"
14 #include "PlaylistGraphicsScene.h"
15 #include "PlaylistDropVis.h"
16 #include "TheInstances.h"
18 #include <KAction>
19 #include <KMenu>
21 #include <QGraphicsItemAnimation>
22 #include <QModelIndex>
23 #include <QKeyEvent>
24 #include <QTimeLine>
25 #include <QVariant>
27 Playlist::GraphicsView *Playlist::GraphicsView::s_instance = 0;
29 Playlist::GraphicsView::GraphicsView( QWidget *parent )
30 : QGraphicsView( parent )
31 , m_model( 0 )
33 setAlignment( Qt::AlignLeft | Qt::AlignTop );
34 setTransformationAnchor( QGraphicsView::AnchorUnderMouse );
36 setScene( new Playlist::GraphicsScene() );
37 scene()->addItem( Playlist::DropVis::instance() );
39 setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
43 void
44 Playlist::GraphicsView::setModel( Playlist::Model *model )
46 DEBUG_BLOCK
48 m_model = model;
50 rowsInserted( QModelIndex(), 0, m_model->rowCount() - 1);
52 connect( m_model, SIGNAL( modelReset() ), this, SLOT( modelReset() ) );
53 connect( m_model, SIGNAL( rowsInserted( const QModelIndex&, int, int ) ), this, SLOT( rowsInserted( const QModelIndex &, int, int ) ) );
54 connect( m_model, SIGNAL( rowsRemoved( const QModelIndex&, int, int ) ), this, SLOT( rowsRemoved( const QModelIndex&, int, int ) ) );
55 connect( m_model, SIGNAL( dataChanged( const QModelIndex&, const QModelIndex& ) ), this, SLOT( dataChanged( const QModelIndex& ) ) );
56 connect( m_model, SIGNAL( playlistGroupingChanged( ) ), this, SLOT( groupingChanged() ) );
59 show();
62 void
63 Playlist::GraphicsView::contextMenuEvent( QContextMenuEvent *event )
65 QPointF sceneClickPos = mapToScene( event->pos() );
66 QGraphicsItem *topItem = scene()->itemAt( sceneClickPos );
67 if( !topItem )
68 return;
71 Playlist::GraphicsItem *item = dynamic_cast<Playlist::GraphicsItem*>( topItem );
72 if( !item )
73 item = dynamic_cast<Playlist::GraphicsItem*>( topItem->parentItem() );
74 if( !item ) // we've clicked on empty space
75 return;
77 item->setSelected( true );
78 event->accept();
80 KAction *playAction = new KAction( KIcon( Amarok::icon( "play" ) ), i18n( "&Play" ), this );
81 playAction->setData( QVariant( sceneClickPos ) );
82 connect( playAction, SIGNAL( triggered() ), this, SLOT( playTrack() ) );
84 KMenu *menu = new KMenu( this );
85 menu->addAction( playAction );
86 menu->addSeparator();
87 menu->addAction( i18n( "Remove From Playlist" ), this, SLOT( removeSelection() ) );
88 menu->exec( event->globalPos() );
91 void
92 Playlist::GraphicsView::keyPressEvent( QKeyEvent* event )
94 DEBUG_BLOCK
95 debug() << "Pressed: " << event;
96 if( event->matches( QKeySequence::Delete ) )
98 if( !scene()->selectedItems().isEmpty() )
100 event->accept();
101 removeSelection();
102 return;
105 QGraphicsView::keyPressEvent( event );
108 void
109 Playlist::GraphicsView::playTrack()
111 QAction *playAction = dynamic_cast<QAction*>( sender() );
112 if( !playAction )
113 return;
115 QPointF sceneClickPos = playAction->data().toPointF();
116 Playlist::GraphicsItem *item = dynamic_cast<Playlist::GraphicsItem*>( scene()->itemAt( sceneClickPos )->parentItem() );
117 if( !item )
118 return;
119 item->play();
122 void
123 Playlist::GraphicsView::removeSelection()
125 QList<QGraphicsItem*> selection = scene()->selectedItems();
127 int firstIndex = m_tracks.indexOf( static_cast<Playlist::GraphicsItem*>( selection.first() ) );
128 if ( firstIndex > 0) firstIndex -= 1;
130 foreach( QGraphicsItem *i, selection )
132 int count = 1;
133 int index = m_tracks.indexOf( static_cast<Playlist::GraphicsItem*>(i) );
134 QModelIndex modelIndex = The::playlistModel()->index( index, 0 );
135 QModelIndex nextIndex = The::playlistModel()->index( index+1 , 0 );
136 if( modelIndex.data( GroupRole ).toInt() == Head && nextIndex.data( GroupRole ).toInt() != Head )
138 QModelIndex in = modelIndex;
139 int i = index;
140 while( in.data( GroupRole ).toInt() != End )
142 ++count;
143 in = The::playlistModel()->index( i++, 0 );
146 count = modelIndex.data( GroupRole ).toInt() == Head ? count - 1 : count;
147 m_model->removeRows( index, count );
152 for ( int i = firstIndex ; i < m_tracks.count(); i++ )
153 m_tracks.at( i )->setRow( i );
156 void
157 Playlist::GraphicsView::rowsInserted( const QModelIndex& parent, int start, int end )
159 Q_UNUSED( parent );
161 //call setRow on track imidiately preceding the insertion as this might have to change its
162 // look and height if it has been grouped by the model.
163 if ( start > 0 )
164 m_tracks[ start-1]->setRow( start-1 );
166 int cumulativeHeight = 0;
167 for ( int j = 0; j < start; j++ )
168 cumulativeHeight += m_tracks.at( j )->boundingRect().height();
170 debug() << "start: " << start << " ,end: " << end;
171 for( int i = start; i <= end; i++ )
174 Playlist::GraphicsItem* item = new Playlist::GraphicsItem();
175 item->setRow( i );
176 item->setPos( 0.0, cumulativeHeight );
177 cumulativeHeight += item->boundingRect().height();
178 scene()->addItem( item );
179 m_tracks.insert( i, item );
182 // make sure all following tracks has their colors updated correctly
183 for ( int i = end + 1 ; i < m_tracks.count(); i++ )
184 m_tracks.at( i )->setRow( i );
186 shuffleTracks( end + 1 );
189 void
190 Playlist::GraphicsView::rowsRemoved(const QModelIndex& parent, int start, int end )
192 DEBUG_BLOCK
193 Q_UNUSED( parent );
194 for( int i = end; i >= start; i-- )
195 delete m_tracks.takeAt( i );
197 // make sure all following tracks has their colors updated correctly
198 for ( int i = start; i < m_tracks.count(); i++ )
199 m_tracks.at( i )->setRow( i );
202 shuffleTracks( start );
205 void
206 Playlist::GraphicsView::moveItem( Playlist::GraphicsItem *moveMe, Playlist::GraphicsItem *above )
209 int moveMeIndex = m_tracks.indexOf( moveMe );
210 int aboveIndex;
211 if ( above )
212 aboveIndex = m_tracks.indexOf( above );
213 else
214 aboveIndex = m_tracks.count();
217 //call set row on all items below the first one potentially modified to
218 //make sure that all items have correct background color and group info
220 int firstIndex = QMIN ( aboveIndex, moveMeIndex ) -1;
221 if ( firstIndex < 0 ) firstIndex = 0;
223 if( moveMeIndex < aboveIndex )
225 m_model->moveRow( moveMeIndex, aboveIndex -1 );
226 m_tracks.move( moveMeIndex, aboveIndex - 1 );
229 int i;
230 for ( i = firstIndex; i < m_tracks.count(); i++ )
231 m_tracks.at( i )->setRow( i );
234 //shuffleTracks( moveMeIndex, aboveIndex );
235 shuffleTracks( 0 );
237 else
239 m_model->moveRow( moveMeIndex, aboveIndex );
240 m_tracks.move( moveMeIndex, aboveIndex );
244 debug() << "First index: " << firstIndex;
246 for ( int i = firstIndex; i < m_tracks.count(); i++ )
247 m_tracks.at( i )->setRow( i );
249 //shuffleTracks( aboveIndex, moveMeIndex + 1);
250 shuffleTracks( 0 );
256 void
257 Playlist::GraphicsView::shuffleTracks( int startPosition, int stopPosition )
259 DEBUG_BLOCK
260 if( startPosition < 0 )
261 return;
263 if( stopPosition < 0 || stopPosition > m_tracks.size() )
264 stopPosition = m_tracks.size();
266 QTimeLine *timer = new QTimeLine( 300 ); // 0.3 second duration
267 timer->setCurveShape( QTimeLine::EaseInCurve );
268 timer->setUpdateInterval( 30 ); // make sure that there is no leftover time
269 //that results in items not moving all the way
272 int cumulativeHeight = 0;
274 for ( int j = 0; j < startPosition; j++ )
275 cumulativeHeight += m_tracks.at( j )->boundingRect().height();
277 for( int i = startPosition; i < stopPosition; ++i )
279 Playlist::GraphicsItem *item = m_tracks.at( i );
280 qreal currentY = item->pos().y();
283 qreal desiredY = cumulativeHeight;
285 int itemHeight = item->boundingRect().height();
286 cumulativeHeight += itemHeight;
289 int visibleTop = mapToScene( 0,0 ).y();
290 int visibleBottom = mapToScene( 0, height() ).y();
292 /*if ( ( ( ( currentY >= visibleTop ) && ( currentY <= visibleBottom ) ) ||
293 ( ( desiredY >= visibleTop ) && ( desiredY <= visibleBottom ) ) ) &&
294 ( itemHeight != 0 ) ) {*/
296 if ( !( ( desiredY < visibleTop ) || ( desiredY > visibleBottom ) ) &&
297 ( ( currentY >= visibleTop ) && ( currentY <= visibleBottom ) ) &&
298 ( itemHeight != 0 ) ) {
300 bool moveUp = false;
301 if( desiredY > currentY )
302 moveUp = true;
304 qreal distanceMoved = moveUp ? ( desiredY - currentY ) : ( currentY - desiredY );
306 QGraphicsItemAnimation *animator = new QGraphicsItemAnimation;
307 animator->setItem( item );
308 animator->setTimeLine( timer );
310 // if distanceMoved is negative, then we are moving the object towards the bottom of the screen
311 for( qreal i = 0; i < distanceMoved; ++i )
313 qreal newY = moveUp ? ( currentY + i ) : ( currentY - i );
314 animator->setPosAt( i / distanceMoved, QPointF( 0.0, newY ) );
316 animator->setPosAt( 1, QPointF( 0.0, desiredY ) );
318 } else {
319 //don't animate items if both currentY and desiredY are outside the visible area!
320 //We still do need to update their position though
321 m_tracks.at( i )->setPos( 0.0, desiredY );
325 timer->start();
328 void
329 Playlist::GraphicsView::modelReset()
331 foreach( Playlist::GraphicsItem* it, m_tracks )
333 delete it;
335 m_tracks.clear();
338 void
339 Playlist::GraphicsView::dataChanged(const QModelIndex & index)
341 DEBUG_BLOCK
342 if ( !index.isValid() )
343 return;
345 if ( m_tracks.count() > index.row() ) {
346 debug() << "Refreshing item...";
347 m_tracks.at( index.row() )->update();
351 namespace The {
352 Playlist::GraphicsView* playlistView() { return Playlist::GraphicsView::instance(); }
355 void Playlist::GraphicsView::groupingChanged()
357 // ouch!!! this is expesive!!
358 //DEBUG_BLOCK
360 int i;
361 for ( i = 0; i < m_tracks.count(); i++ )
362 m_tracks.at( i )->setRow( i );
365 shuffleTracks( 0, -1 );
366 // update();
370 #include "PlaylistGraphicsView.moc"