1 /***************************************************************************
2 * copyright : (C) 2007 Seb Ruiz <ruiz@kde.org> *
3 * (C) 2007 Nikolaj Hald Nielsen <nhnFreespirit@gmail.com> *
4 * (C) 2007 Leonardo Franchi <lfranchi@gmail.com> *
5 * (C) 2007 Jeff Mitchell <kde-dev@emailgoeshere.com> *
6 **************************************************************************/
8 /***************************************************************************
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
15 ***************************************************************************/
17 #include "amarokconfig.h"
20 #include "GenericInfoBox.h"
21 #include "textfader.h"
22 #include "collectiondb.h"
23 #include "contextbox.h"
24 #include "contextview.h"
26 #include "enginecontroller.h"
27 #include "graphicsitemfader.h"
28 #include "graphicsitemscaler.h"
29 #include "introanimation.h"
30 #include "scriptmanager.h"
31 #include "statusbar.h"
32 #include "items/ContextItemManager.h"
34 #include <kstandarddirs.h>
36 #include <math.h> // scaleView()
40 #include <QGraphicsTextItem>
41 #include <QGraphicsScene>
42 #include <QMouseEvent>
43 #include <QWheelEvent>
46 #include <QSvgRenderer>
48 using namespace Context
;
50 static bool enablePUD
= true;
51 static bool eyeCandyFlag
= false;
53 ContextView
*ContextView::s_instance
= 0;
55 ContextView::ContextView()
57 , EngineObserver( EngineController::instance() )
63 s_instance
= this; // we are a singleton class
65 // start context item manager
66 ContextItemManager::instance()->setStartBox( 2 ); // HACK, for now we are telling it
67 // that the contextview itself puts two boxes on the CV (true)
69 setRenderHints( QPainter::Antialiasing
);
72 setAlignment( Qt::AlignTop
);
73 setCacheMode( QGraphicsView::CacheBackground
); // this won't be changing regularly
75 setMouseTracking( true );
80 void ContextView::initiateScene()
82 m_contextScene
= new QGraphicsScene( this );
83 m_contextScene
->setItemIndexMethod( QGraphicsScene::BspTreeIndex
);
84 m_contextScene
->setBackgroundBrush( palette().highlight() );
85 setScene( m_contextScene
);
88 void ContextView::engineStateChanged( Engine::State state
, Engine::State oldState
)
108 void ContextView::engineNewMetaData( const MetaBundle
&, bool )
112 void ContextView::showHome()
117 FadingImageItem * fadeingImage = new FadingImageItem (QPixmap( KStandardDirs::locate("data", "amarok/images/splash_screen.jpg" ) ) );
118 QColor color = palette().highlight();
119 fadeingImage->setFadeColor( color );
120 fadeingImage->setTargetAlpha( 200 );
124 QGraphicsPixmapItem *logoItem = new QGraphicsPixmapItem ( QPixmap( KStandardDirs::locate("data", "amarok/images/splash_screen.jpg" ) ) );
126 GraphicsItemFader *logoFader = new GraphicsItemFader( logoItem, 0 );
127 // logoFader->setTargetAlpha( 200 );
128 logoFader->setFadeColor( palette().highlight() );
129 logoFader->setDuration( 5000 );
130 logoFader->setFPS( 30 );
131 logoFader->setStartAlpha( 0 );
132 logoFader->setTargetAlpha( 200 );
134 addContextBox( logoFader );
135 logoFader->startFading();
140 /* TextFader *textFader = new TextFader("Hello, World", 0);
141 QFont font = textFader->font();
142 font.setPointSize( 20 );
143 textFader->setFont( font );
145 textFader->setDuration( 5000 );
146 textFader->setFPS( 30 );
147 textFader->setStartAlpha( 0 );
148 textFader->setTargetAlpha( 255 );
150 addContextBox( textFader );
151 textFader->startFading();*/
154 IntroAnimation *introAnim = new IntroAnimation();
156 connect( introAnim, SIGNAL( animationComplete() ), this, SLOT( introAnimationComplete() ) );
158 debug() << "starting intro anim" << endl;
160 introAnim->setFadeColor( palette().highlight() );
161 addContextBox( introAnim );
162 introAnim->startAnimation();
164 introAnimationComplete();
165 messageNotify( QString( "showHome" ) );
170 void ContextView::introAnimationComplete()
173 debug() << "introAnimationComplete!" << endl
;
175 ContextBox
*welcomeBox
= new ContextBox();
176 welcomeBox
->setTitle( "Hooray, welcome to Amarok::ContextView!" );
177 addContextBox( welcomeBox
);
180 AlbumBox
*albumBox
= new AlbumBox();
181 albumBox
->setTitle( "Your Newest Albums" );
183 // because i don't know how to use the new QueryMaker class...
184 QString query
= "SELECT distinct(AL.name), AR.name, YR.name "
186 "INNER JOIN album AL ON T.album = AL.id "
187 "INNER JOIN artist AR ON T.artist = AR.id "
188 "INNER JOIN year YR ON T.year = YR.id "
190 "ORDER BY T.createdate DESC "
192 QStringList values
= CollectionDB::instance()->query( query
, false );
194 for( QStringList::ConstIterator it
= values
.begin(), end
= values
.end(); it
!= end
; ++it
)
196 const QString album
= *it
;
197 const QString artist
= *++it
;
198 const QString year
= *++it
;
199 const QString
&cover
= CollectionDB::instance()->albumImage( artist
, album
, false, 50 );
200 albumBox
->addAlbumInfo( cover
, QString( "%1 - %2\n%3" ).arg( artist
, album
, year
) );
203 addContextBox( albumBox
);
206 // QTimer::singleShot( 5000, this, SLOT( testBoxLayout() ) );
209 void ContextView::testBoxLayout()
211 static bool s_add
= true;
215 m_testItem
= new ContextBox();
216 m_testItem
->setTitle( "Test Item" );
217 // QGraphicsSvgItem *svg = new QGraphicsSvgItem( KStandardDirs::locate("data", "amarok/images/amarok_icon.svg" ), m_testItem );
218 // svg->scale(0.5, 0.5 );
219 // svg->moveBy( 0, 30 );
225 addContextBox( m_testItem
, 1 );
227 removeContextBox( m_testItem
);
231 QTimer::singleShot( 5000, this, SLOT( testBoxLayout() ) );
235 void ContextView::scaleView( qreal factor
)
237 qreal scaleF
= matrix().scale( factor
, factor
).mapRect(QRectF(0, 0, 1, 1)).width();
238 if( scaleF
< 0.07 || scaleF
> 100 )
241 scale( factor
, factor
);
244 void ContextView::wheelEvent( QWheelEvent
*event
)
246 if( event
->modifiers() & Qt::ControlModifier
)
247 scaleView( pow( (double)2, -event
->delta() / 240.0) );
249 QGraphicsView::wheelEvent( event
);
252 void ContextView::resizeEvent( QResizeEvent
*event
)
254 QSize newSize
= event
->size();
255 QList
<QGraphicsItem
*> items
= m_contextScene
->items();
257 foreach( QGraphicsItem
*item
, items
)
259 ContextBox
*box
= dynamic_cast<ContextBox
*>( item
);
261 box
->ensureWidthFits( (qreal
)newSize
.width() );
265 void ContextView::clear()
268 // tell member items that their boxes are being removed
269 messageNotify( QString( "boxesRemoved" ) );
271 m_contextBoxes
.clear();
272 delete m_contextScene
;
278 bool ContextView::boxHigherThan( const ContextBox
*c1
, const ContextBox
*c2
)
280 // "Higher" means closer towards the top of the screen, but it means closer to position 0 (so really it's lower)
281 return c1
->sceneBoundingRect().bottom() < c2
->sceneBoundingRect().bottom();
284 void ContextView::boxHeightChanged( qreal change
)
286 ContextBox
*box
= dynamic_cast<ContextBox
*>( sender() );
290 QList
<QGraphicsItem
*> shuffle
;
292 int index
= m_contextBoxes
.indexOf( box
);
293 for( ++index
; index
< m_contextBoxes
.size(); ++index
)
294 shuffle
<< m_contextBoxes
.at(index
);
296 // 'change' will be negative if it got smaller
297 shuffleItems( shuffle
, change
);
300 void ContextView::showPopupDropper()
308 while( !m_pudScalers
.isEmpty() )
309 delete m_pudScalers
.takeFirst();
311 while( !m_pudFaders
.isEmpty() )
312 delete m_pudFaders
.takeFirst();
314 foreach( ContextBox
* box
, m_contextBoxes
)
319 GraphicsItemFader
*fader
= new GraphicsItemFader( box
);
320 fader
->setDuration( 1 );
321 fader
->setDelay( 1000 );
322 fader
->setStartAlpha( 255 );
323 fader
->setTargetAlpha( 0 );
324 m_pudFaders
.append( fader
);
327 GraphicsItemScaler
*scaler
= new GraphicsItemScaler( box
);
328 scaler
->setDuration( 1000 );
329 scaler
->setTargetSize( 1, 1);
330 m_pudScalers
.append( scaler
);
333 foreach( GraphicsItemScaler
* scaler
, m_pudScalers
)
334 scaler
->startScaling();
335 foreach( GraphicsItemFader
* fader
, m_pudFaders
)
336 fader
->startFading();
341 void ContextView::hidePopupDropper()
349 foreach( GraphicsItemFader
* fader
, m_pudFaders
)
351 fader
->setStartAlpha( 0 );
352 fader
->setTargetAlpha( 255 );
353 fader
->setDuration( 1000 );
354 fader
->setDelay( 100 );
357 foreach( GraphicsItemScaler
* scaler
, m_pudScalers
)
359 scaler
->setDuration( 1 );
360 scaler
->setTargetSize( scaler
->originalWidth(), scaler
->originalHeight() );
363 foreach( GraphicsItemFader
* fader
, m_pudFaders
)
364 fader
->startFading();
366 foreach( GraphicsItemScaler
* scaler
, m_pudScalers
)
367 scaler
->startScaling();
372 void ContextView::mouseMoveEvent( QMouseEvent
*e
)
376 QGraphicsView::mouseMoveEvent( e
);
379 void ContextView::removeContextBox( ContextBox
*oldBox
, bool fadeOut
)
383 if( !oldBox
|| !m_contextScene
)
388 // GraphicsItemFader *fader = new GraphicsItemFader( oldBox );
389 // fader->setDuration( 2500 );
390 // fader->setStartAlpha( 0 );
391 // fader->setTargetAlpha( 255 );
392 // fader->setFadeColor( palette().highlight().color() );
393 // fader->startFading();
396 if( !m_contextBoxes
.isEmpty() )
398 QList
<QGraphicsItem
*> shuffleUp
;
400 // need to shuffle up all the boxes below
401 int index
= m_contextBoxes
.indexOf( oldBox
);
402 for( ++index
; index
< m_contextBoxes
.size(); ++index
)
404 ContextBox
*box
= m_contextBoxes
.at(index
);
405 debug() << "shuffling up: " << box
->title() << endl
;
409 qreal distance
= oldBox
->boundingRect().height() + BOX_PADDING
;
410 debug() << "shuffling " << shuffleUp
.size() << " items up, a total of " << distance
<< endl
;
412 shuffleItems( shuffleUp
, distance
, ShuffleUp
);
415 // no need to resort when removing
416 m_contextBoxes
.removeAll( oldBox
);
417 m_contextScene
->removeItem( oldBox
);
418 disconnect( oldBox
, SIGNAL( heightChanged(qreal
) ), this, SLOT( boxHeightChanged(qreal
) ) );
422 // Places a context box at the location specified by @param index. -1 -> at the bottom
423 void ContextView::addContextBox( ContextBox
*newBox
, int index
, bool fadeIn
)
427 if( !newBox
|| !m_contextScene
)
430 debug() << "Adding box: " << newBox
->title() << endl
;
434 // GraphicsItemFader *fader = new GraphicsItemFader( newBox );
435 // connect( fader, SIGNAL( animationComplete() ), this, SLOT( faderItemFinished() ) );
436 // fader->setDuration( 2500 );
437 // fader->setStartAlpha( 255 );
438 // fader->setTargetAlpha( 0 );
439 // fader->setFadeColor( palette().highlight().color() );
440 // fader->startFading();
444 qreal yposition
= BOX_PADDING
;
446 if( !m_contextBoxes
.isEmpty() )
448 if( index
>= m_contextBoxes
.count() )
451 debug() << "box count: " << m_contextBoxes
.size() << endl
;
453 // special case 'add-to-end' index, -1.
455 yposition
= m_contextBoxes
.last()->sceneBoundingRect().bottom() + BOX_PADDING
;
458 if( index
> 0 ) // we need the bottom value for the box above it.
459 yposition
= m_contextBoxes
.at( index
- 1 )->sceneBoundingRect().bottom() + BOX_PADDING
;
461 debug() << "y-position: " << yposition
<< endl
;
463 QList
<QGraphicsItem
*> shuffleDown
;
465 // need to shuffle down all the boxes below
466 for( int i
= index
; i
< m_contextBoxes
.size(); ++i
)
467 shuffleDown
<< m_contextBoxes
.at( i
);
469 qreal distance
= newBox
->boundingRect().height() + BOX_PADDING
;
470 debug() << "shuffling " << shuffleDown
.size() << " items down, a total of " << distance
<< endl
;
472 shuffleItems( shuffleDown
, distance
, ShuffleDown
);
476 debug() << "placing box at position: " << index
<< ", y position of box: " << yposition
<< endl
;
478 m_contextScene
->addItem( newBox
);
479 newBox
->setPos( BOX_PADDING
, yposition
);
480 m_contextBoxes
.append( newBox
);
482 // sort them based on bottom position
483 qSort( m_contextBoxes
.begin(), m_contextBoxes
.end(), boxHigherThan
);
485 connect( newBox
, SIGNAL( heightChanged(qreal
) ), this, SLOT( boxHeightChanged(qreal
) ) );
489 void ContextView::shuffleItems( QList
<QGraphicsItem
*> items
, qreal distance
, int direction
)
491 if( direction
== ShuffleUp
)
492 distance
= -distance
;
494 QList
<QGraphicsItem
*>::iterator i
;
495 for( i
= items
.begin(); i
!= items
.end(); ++i
)
497 (*i
)->moveBy( 0, distance
);
501 void ContextView::showCurrentTrack()
505 MetaBundle bundle
= EngineController::instance()->bundle();
507 ContextBox
*infoBox
= new ContextBox();
508 infoBox
->setTitle( i18n("%1 - %2", bundle
.title(), bundle
.artist() ) );
509 addContextBox( infoBox
);
511 AlbumBox
*albumBox
= new AlbumBox();
512 albumBox
->setTitle( i18n("Albums By %1", bundle
.artist() ) );
514 int artistId
= CollectionDB::instance()->artistID( bundle
.artist() );
515 // because i don't know how to use the new QueryMaker class...
516 QString query
= QString("SELECT distinct(AL.name), AR.name, YR.name "
518 "INNER JOIN album AL ON T.album = AL.id "
519 "INNER JOIN artist AR ON T.artist = AR.id "
520 "INNER JOIN year YR ON T.year = YR.id "
522 "ORDER BY YR.name DESC").arg( artistId
);
524 QStringList values
= CollectionDB::instance()->query( query
, false );
525 debug() << "Result count: " << values
.count() << endl
;
527 for( QStringList::ConstIterator it
= values
.begin(), end
= values
.end(); it
!= end
; ++it
)
530 if( album
.isEmpty() )
531 album
= i18n( "Unknown" );
533 const QString artist
= *++it
;
534 const QString year
= *++it
;
535 const QString
&cover
= CollectionDB::instance()->albumImage( artist
, album
, false, 50 );
536 debug() << "artist: " << artist
<< " album: " << album
<< " cover: " << cover
<< endl
;
537 albumBox
->addAlbumInfo( cover
, QString( "%1 - %2\n%3" ).arg( artist
, album
, year
) );
540 addContextBox( albumBox
);
542 messageNotify( QString( "showCurrentTrack" ) );
545 #include "contextview.moc"