First steps towards making the playlist behave nicely with respect to grouped tracks...
[amarok.git] / src / playlist / PlaylistGraphicsView.cpp
blobac5225c4b60ae711a52b3b81c9bfe961aebde92b
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() );
41 void
42 Playlist::GraphicsView::setModel( Playlist::Model *model )
44 DEBUG_BLOCK
46 m_model = model;
48 rowsInserted( QModelIndex(), 0, m_model->rowCount() - 1);
50 connect( m_model, SIGNAL( modelReset() ), this, SLOT( modelReset() ) );
51 connect( m_model, SIGNAL( rowsInserted( const QModelIndex&, int, int ) ), this, SLOT( rowsInserted( const QModelIndex &, int, int ) ) );
52 connect( m_model, SIGNAL( rowsRemoved( const QModelIndex&, int, int ) ), this, SLOT( rowsRemoved( const QModelIndex&, int, int ) ) );
53 connect( m_model, SIGNAL( dataChanged( const QModelIndex&, const QModelIndex& ) ), this, SLOT( dataChanged( const QModelIndex& ) ) );
54 show();
57 void
58 Playlist::GraphicsView::contextMenuEvent( QContextMenuEvent *event )
60 QPointF sceneClickPos = mapToScene( event->pos() );
61 QGraphicsItem *topItem = scene()->itemAt( sceneClickPos );
62 if( !topItem )
63 return;
66 Playlist::GraphicsItem *item = dynamic_cast<Playlist::GraphicsItem*>( topItem );
67 if( !item )
68 item = dynamic_cast<Playlist::GraphicsItem*>( topItem->parentItem() );
69 if( !item ) // we've clicked on empty space
70 return;
72 item->setSelected( true );
73 event->accept();
75 KAction *playAction = new KAction( KIcon( Amarok::icon( "play" ) ), i18n( "&Play" ), this );
76 playAction->setData( QVariant( sceneClickPos ) );
77 connect( playAction, SIGNAL( triggered() ), this, SLOT( playTrack() ) );
79 KMenu *menu = new KMenu( this );
80 menu->addAction( playAction );
81 menu->addSeparator();
82 menu->addAction( i18n( "Remove From Playlist" ), this, SLOT( removeSelection() ) );
83 menu->exec( event->globalPos() );
86 void
87 Playlist::GraphicsView::keyPressEvent( QKeyEvent* event )
89 DEBUG_BLOCK
90 debug() << "Pressed: " << event;
91 if( event->matches( QKeySequence::Delete ) )
93 if( !scene()->selectedItems().isEmpty() )
95 event->accept();
96 removeSelection();
97 return;
100 QGraphicsView::keyPressEvent( event );
103 void
104 Playlist::GraphicsView::playTrack()
106 QAction *playAction = dynamic_cast<QAction*>( sender() );
107 if( !playAction )
108 return;
110 QPointF sceneClickPos = playAction->data().toPointF();
111 Playlist::GraphicsItem *item = dynamic_cast<Playlist::GraphicsItem*>( scene()->itemAt( sceneClickPos )->parentItem() );
112 if( !item )
113 return;
114 item->play();
117 void
118 Playlist::GraphicsView::removeSelection()
120 QList<QGraphicsItem*> selection = scene()->selectedItems();
121 foreach( QGraphicsItem *i, selection )
123 int index = m_tracks.indexOf( static_cast<Playlist::GraphicsItem*>(i) );
124 m_model->removeRows( index, 1 );
128 void
129 Playlist::GraphicsView::rowsInserted( const QModelIndex& parent, int start, int end )
131 Q_UNUSED( parent );
133 //call setRow on track imidiately preceding the insertion as this might have to change its
134 // look and height if it has been grouped by the model.
135 if ( start > 0 )
136 m_tracks[ start-1]->setRow( start-1 );
138 int cumulativeHeight = 0;
139 for ( int j = 0; j < start; j++ )
140 cumulativeHeight += m_tracks.at( j )->boundingRect().height();
142 debug() << "start: " << start << " ,end: " << end;
143 for( int i = start; i <= end; i++ )
146 Playlist::GraphicsItem* item = new Playlist::GraphicsItem();
147 item->setRow( i );
148 item->setPos( 0.0, cumulativeHeight );
149 cumulativeHeight += item->boundingRect().height();
150 scene()->addItem( item );
151 m_tracks.insert( i, item );
154 // make sure all following tracks has their colors updated correctly
155 for ( int i = end + 1 ; i < m_tracks.count(); i++ )
156 m_tracks.at( i )->setRow( i );
158 shuffleTracks( end + 1 );
161 void
162 Playlist::GraphicsView::rowsRemoved(const QModelIndex& parent, int start, int end )
164 DEBUG_BLOCK
165 Q_UNUSED( parent );
166 for( int i = end; i >= start; i-- )
167 delete m_tracks.takeAt( i );
169 // make sure all following tracks has their colors updated correctly
170 for ( int i = start; i < m_tracks.count(); i++ )
171 m_tracks.at( i )->setRow( i );
174 shuffleTracks( start );
177 void
178 Playlist::GraphicsView::moveItem( Playlist::GraphicsItem *moveMe, Playlist::GraphicsItem *above )
180 int moveMeIndex = m_tracks.indexOf( moveMe );
181 int aboveIndex = m_tracks.indexOf( above );
183 //call set row on all items below the first one potentially modified to
184 //make sure that all items have correct background color and group info
187 if( moveMeIndex < aboveIndex )
189 m_model->moveRow( moveMeIndex, aboveIndex -1 );
190 m_tracks.move( moveMeIndex, aboveIndex - 1 );
193 int i;
194 for ( i = moveMeIndex; i < m_tracks.count(); i++ )
195 m_tracks.at( i )->setRow( i );
198 shuffleTracks( moveMeIndex, aboveIndex );
200 else
202 m_model->moveRow( moveMeIndex, aboveIndex );
203 m_tracks.move( moveMeIndex, aboveIndex );
205 int i;
206 for ( i = aboveIndex; i < m_tracks.count(); i++ )
207 m_tracks.at( i )->setRow( i );
209 shuffleTracks( aboveIndex, moveMeIndex + 1);
213 void
214 Playlist::GraphicsView::shuffleTracks( int startPosition, int stopPosition )
216 if( startPosition < 0 )
217 return;
219 if( stopPosition < 0 || stopPosition > m_tracks.size() )
220 stopPosition = m_tracks.size();
222 QTimeLine *timer = new QTimeLine( 300 ); // 0.3 second duration
223 timer->setCurveShape( QTimeLine::EaseInCurve );
226 int cumulativeHeight = 0;
228 for ( int j = 0; j < startPosition; j++ )
229 cumulativeHeight += m_tracks.at( j )->boundingRect().height();
231 for( int i = startPosition; i < stopPosition; ++i )
233 Playlist::GraphicsItem *item = m_tracks.at( i );
234 qreal currentY = item->pos().y();
237 qreal desiredY = cumulativeHeight;
238 cumulativeHeight += item->boundingRect().height();
240 bool moveUp = false;
241 if( desiredY > currentY )
242 moveUp = true;
244 qreal distanceMoved = moveUp ? ( desiredY - currentY ) : ( currentY - desiredY );
246 QGraphicsItemAnimation *animator = new QGraphicsItemAnimation;
247 animator->setItem( item );
248 animator->setTimeLine( timer );
250 // if distanceMoved is negative, then we are moving the object towards the bottom of the screen
251 for( qreal i = 0; i < distanceMoved; ++i )
253 qreal newY = moveUp ? ( currentY + i ) : ( currentY - i );
254 animator->setPosAt( i / distanceMoved, QPointF( 0.0, newY ) );
257 timer->start();
260 void
261 Playlist::GraphicsView::modelReset()
263 foreach( Playlist::GraphicsItem* it, m_tracks )
265 delete it;
267 m_tracks.clear();
270 void
271 Playlist::GraphicsView::dataChanged(const QModelIndex & index)
273 DEBUG_BLOCK
274 if ( !index.isValid() )
275 return;
277 if ( m_tracks.count() > index.row() )
278 m_tracks.at( index.row() )->refresh();
281 namespace The {
282 Playlist::GraphicsView* playlistView() { return Playlist::GraphicsView::instance(); }
286 #include "PlaylistGraphicsView.moc"