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 ***************************************************************************/
11 #include "PlaylistModel.h"
12 #include "PlaylistGraphicsItem.h"
13 #include "PlaylistGraphicsView.h"
14 #include "PlaylistGraphicsScene.h"
15 #include "PlaylistDropVis.h"
16 #include "TheInstances.h"
21 #include <QGraphicsItemAnimation>
22 #include <QModelIndex>
27 Playlist::GraphicsView
*Playlist::GraphicsView::s_instance
= 0;
29 Playlist::GraphicsView::GraphicsView( QWidget
*parent
)
30 : QGraphicsView( parent
)
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
);
44 Playlist::GraphicsView::setModel( Playlist::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() ) );
63 Playlist::GraphicsView::contextMenuEvent( QContextMenuEvent
*event
)
65 QPointF sceneClickPos
= mapToScene( event
->pos() );
66 QGraphicsItem
*topItem
= scene()->itemAt( sceneClickPos
);
71 Playlist::GraphicsItem
*item
= dynamic_cast<Playlist::GraphicsItem
*>( topItem
);
73 item
= dynamic_cast<Playlist::GraphicsItem
*>( topItem
->parentItem() );
74 if( !item
) // we've clicked on empty space
77 item
->setSelected( true );
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
);
87 menu
->addAction( i18n( "Remove From Playlist" ), this, SLOT( removeSelection() ) );
88 menu
->exec( event
->globalPos() );
92 Playlist::GraphicsView::keyPressEvent( QKeyEvent
* event
)
95 debug() << "Pressed: " << event
;
96 if( event
->matches( QKeySequence::Delete
) )
98 if( !scene()->selectedItems().isEmpty() )
105 QGraphicsView::keyPressEvent( event
);
109 Playlist::GraphicsView::playTrack()
111 QAction
*playAction
= dynamic_cast<QAction
*>( sender() );
115 QPointF sceneClickPos
= playAction
->data().toPointF();
116 Playlist::GraphicsItem
*item
= dynamic_cast<Playlist::GraphicsItem
*>( scene()->itemAt( sceneClickPos
)->parentItem() );
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
)
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
;
140 while( in
.data( GroupRole
).toInt() != End
)
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
);
157 Playlist::GraphicsView::rowsInserted( const QModelIndex
& parent
, int start
, int end
)
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.
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();
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 );
190 Playlist::GraphicsView::rowsRemoved(const QModelIndex
& parent
, int start
, int end
)
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
);
206 Playlist::GraphicsView::moveItem( Playlist::GraphicsItem
*moveMe
, Playlist::GraphicsItem
*above
)
209 int moveMeIndex
= m_tracks
.indexOf( moveMe
);
212 aboveIndex
= m_tracks
.indexOf( above
);
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 );
227 for ( i
= moveMeIndex
; i
< m_tracks
.count(); i
++ )
228 m_tracks
.at( i
)->setRow( i
);
231 //shuffleTracks( moveMeIndex, aboveIndex );
236 m_model
->moveRow( moveMeIndex
, aboveIndex
);
237 m_tracks
.move( moveMeIndex
, aboveIndex
);
240 for ( i
= aboveIndex
; i
< m_tracks
.count(); i
++ )
241 m_tracks
.at( i
)->setRow( i
);
243 //shuffleTracks( aboveIndex, moveMeIndex + 1);
251 Playlist::GraphicsView::shuffleTracks( int startPosition
, int stopPosition
)
254 if( startPosition
< 0 )
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 ) ) {
295 if( desiredY
> currentY
)
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
) );
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
);
323 Playlist::GraphicsView::modelReset()
325 foreach( Playlist::GraphicsItem
* it
, m_tracks
)
333 Playlist::GraphicsView::dataChanged(const QModelIndex
& index
)
336 if ( !index
.isValid() )
339 if ( m_tracks
.count() > index
.row() )
340 m_tracks
.at( index
.row() )->refresh();
344 Playlist::GraphicsView
* playlistView() { return Playlist::GraphicsView::instance(); }
347 void Playlist::GraphicsView::groupingChanged()
349 // ouch!!! this is expesive!!
353 for ( i
= 0; i
< m_tracks
.count(); i
++ )
354 m_tracks
.at( i
)->setRow( i
);
357 shuffleTracks( 0, -1 );
362 #include "PlaylistGraphicsView.moc"