dont animate items bound for beyond the visible area... This greatly speeds up expand...
[amarok.git] / src / playlist / PlaylistGraphicsView.cpp
blob51a0a66c31c131057b5ffe854ce48be6fb458bfb
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 if( moveMeIndex < aboveIndex )
222 m_model->moveRow( moveMeIndex, aboveIndex -1 );
223 m_tracks.move( moveMeIndex, aboveIndex - 1 );
226 int i;
227 for ( i = moveMeIndex; i < m_tracks.count(); i++ )
228 m_tracks.at( i )->setRow( i );
231 //shuffleTracks( moveMeIndex, aboveIndex );
232 shuffleTracks( 0 );
234 else
236 m_model->moveRow( moveMeIndex, aboveIndex );
237 m_tracks.move( moveMeIndex, aboveIndex );
239 int i;
240 for ( i = aboveIndex; i < m_tracks.count(); i++ )
241 m_tracks.at( i )->setRow( i );
243 //shuffleTracks( aboveIndex, moveMeIndex + 1);
244 shuffleTracks( 0 );
250 void
251 Playlist::GraphicsView::shuffleTracks( int startPosition, int stopPosition )
253 DEBUG_BLOCK
254 if( startPosition < 0 )
255 return;
257 if( stopPosition < 0 || stopPosition > m_tracks.size() )
258 stopPosition = m_tracks.size();
260 QTimeLine *timer = new QTimeLine( 300 ); // 0.3 second duration
261 timer->setCurveShape( QTimeLine::EaseInCurve );
262 timer->setUpdateInterval( 30 ); // make sure that there is no leftover time
263 //that results in items not moving all the way
266 int cumulativeHeight = 0;
268 for ( int j = 0; j < startPosition; j++ )
269 cumulativeHeight += m_tracks.at( j )->boundingRect().height();
271 for( int i = startPosition; i < stopPosition; ++i )
273 Playlist::GraphicsItem *item = m_tracks.at( i );
274 qreal currentY = item->pos().y();
277 qreal desiredY = cumulativeHeight;
279 int itemHeight = item->boundingRect().height();
280 cumulativeHeight += itemHeight;
283 int visibleTop = mapToScene( 0,0 ).y();
284 int visibleBottom = mapToScene( 0, height() ).y();
286 /*if ( ( ( ( currentY >= visibleTop ) && ( currentY <= visibleBottom ) ) ||
287 ( ( desiredY >= visibleTop ) && ( desiredY <= visibleBottom ) ) ) &&
288 ( itemHeight != 0 ) ) {*/
290 if ( !( ( desiredY < visibleTop ) || ( desiredY > visibleBottom ) ) &&
291 ( ( currentY >= visibleTop ) && ( currentY <= visibleBottom ) ) &&
292 ( itemHeight != 0 ) ) {
294 bool moveUp = false;
295 if( desiredY > currentY )
296 moveUp = true;
298 qreal distanceMoved = moveUp ? ( desiredY - currentY ) : ( currentY - desiredY );
300 QGraphicsItemAnimation *animator = new QGraphicsItemAnimation;
301 animator->setItem( item );
302 animator->setTimeLine( timer );
304 // if distanceMoved is negative, then we are moving the object towards the bottom of the screen
305 for( qreal i = 0; i < distanceMoved; ++i )
307 qreal newY = moveUp ? ( currentY + i ) : ( currentY - i );
308 animator->setPosAt( i / distanceMoved, QPointF( 0.0, newY ) );
310 animator->setPosAt( 1, QPointF( 0.0, desiredY ) );
312 } else {
313 //don't animate items if both currentY and desiredY are outside the visible area!
314 //We still do need to update their position though
315 m_tracks.at( i )->setPos( 0.0, desiredY );
319 timer->start();
322 void
323 Playlist::GraphicsView::modelReset()
325 foreach( Playlist::GraphicsItem* it, m_tracks )
327 delete it;
329 m_tracks.clear();
332 void
333 Playlist::GraphicsView::dataChanged(const QModelIndex & index)
335 DEBUG_BLOCK
336 if ( !index.isValid() )
337 return;
339 if ( m_tracks.count() > index.row() )
340 m_tracks.at( index.row() )->refresh();
343 namespace The {
344 Playlist::GraphicsView* playlistView() { return Playlist::GraphicsView::instance(); }
347 void Playlist::GraphicsView::groupingChanged()
349 // ouch!!! this is expesive!!
350 //DEBUG_BLOCK
352 int i;
353 for ( i = 0; i < m_tracks.count(); i++ )
354 m_tracks.at( i )->setRow( i );
357 shuffleTracks( 0, -1 );
358 // update();
362 #include "PlaylistGraphicsView.moc"