If you've got a modern machine, turn on the eyecandy bool in contextview.cpp for...
[amarok.git] / src / contextview / contextview.cpp
blobda5c9d7544c3fe78bc1367b72712d712d0b4e5d3
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 /***************************************************************************
9 * *
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. *
14 * *
15 ***************************************************************************/
17 #include "amarokconfig.h"
18 #include "debug.h"
19 #include "albumbox.h"
20 #include "GenericInfoBox.h"
21 #include "textfader.h"
22 #include "collectiondb.h"
23 #include "contextbox.h"
24 #include "contextview.h"
25 #include "debug.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()
37 #include <QBrush>
38 #include <QColor>
39 #include <QGLWidget>
40 #include <QGraphicsTextItem>
41 #include <QGraphicsScene>
42 #include <QMouseEvent>
43 #include <QWheelEvent>
45 //just for testing
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()
56 : QGraphicsView()
57 , EngineObserver( EngineController::instance() )
58 , m_testItem( 0 )
59 , m_pudShown( false )
61 DEBUG_BLOCK
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 );
71 initiateScene();
72 setAlignment( Qt::AlignTop );
73 setCacheMode( QGraphicsView::CacheBackground ); // this won't be changing regularly
75 setMouseTracking( true );
77 showHome();
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 )
90 DEBUG_BLOCK
91 Q_UNUSED( oldState );
93 switch( state )
95 case Engine::Playing:
96 showCurrentTrack();
97 break;
99 case Engine::Empty:
100 showHome();
101 break;
103 default:
108 void ContextView::engineNewMetaData( const MetaBundle&, bool )
112 void ContextView::showHome()
114 clear();
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();
138 //test TextFader
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()
172 clear();
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 "
185 "FROM tags T "
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 "
189 "WHERE T.sampler=0 "
190 "ORDER BY T.createdate DESC "
191 "LIMIT 5 OFFSET 0";
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 );
205 // testing
206 // QTimer::singleShot( 5000, this, SLOT( testBoxLayout() ) );
209 void ContextView::testBoxLayout()
211 static bool s_add = true;
213 if( !m_testItem )
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 );
222 if( m_testItem )
224 if( s_add )
225 addContextBox( m_testItem, 1 );
226 else
227 removeContextBox( m_testItem );
230 s_add = !s_add;
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 )
239 return;
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) );
248 else
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 );
260 if( box )
261 box->ensureWidthFits( (qreal)newSize.width() );
265 void ContextView::clear()
267 DEBUG_BLOCK
268 // tell member items that their boxes are being removed
269 messageNotify( QString( "boxesRemoved" ) );
271 m_contextBoxes.clear();
272 delete m_contextScene;
274 initiateScene();
275 update();
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() );
287 if( !box )
288 return;
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()
302 if( !enablePUD )
303 return;
304 DEBUG_BLOCK
305 if( m_pudShown )
306 return;
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 )
317 if( eyeCandyFlag )
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();
338 m_pudShown = true;
341 void ContextView::hidePopupDropper()
343 if( !enablePUD )
344 return;
345 DEBUG_BLOCK
346 if( !m_pudShown )
347 return;
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();
369 m_pudShown = false;
372 void ContextView::mouseMoveEvent( QMouseEvent *e )
374 if( m_pudShown )
375 hidePopupDropper();
376 QGraphicsView::mouseMoveEvent( e );
379 void ContextView::removeContextBox( ContextBox *oldBox, bool fadeOut )
381 DEBUG_BLOCK
383 if( !oldBox || !m_contextScene )
384 return;
386 if( fadeOut )
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;
406 shuffleUp << box;
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 )
425 DEBUG_BLOCK
427 if( !newBox || !m_contextScene )
428 return;
430 debug() << "Adding box: " << newBox->title() << endl;
432 if( fadeIn )
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();
441 // newBox = fader;
444 qreal yposition = BOX_PADDING;
446 if( !m_contextBoxes.isEmpty() )
448 if( index >= m_contextBoxes.count() )
449 index = -1;
451 debug() << "box count: " << m_contextBoxes.size() << endl;
453 // special case 'add-to-end' index, -1.
454 if( index < 0 )
455 yposition = m_contextBoxes.last()->sceneBoundingRect().bottom() + BOX_PADDING;
456 else
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()
503 clear();
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 "
517 "FROM tags T "
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 "
521 "WHERE AR.id = %1 "
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 )
529 QString album = *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"