Amarok – versatile and easy to use audio player
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

966 lines
31 KiB

  1. /***************************************************************************
  2. playerwidget.cpp - description
  3. -------------------
  4. begin : Mit Nov 20 2002
  5. copyright : (C) 2002 by Mark Kretschmann
  6. email : markey@web.de
  7. ***************************************************************************/
  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. ***************************************************************************/
  16. #include "actionclasses.h"
  17. #include "amarok.h"
  18. #include "amarokconfig.h"
  19. #include "analyzerbase.h"
  20. #include "debug.h"
  21. #include "enginecontroller.h"
  22. #include "metabundle.h" //setScroll()
  23. #include "playerwindow.h"
  24. #include "sliderwidget.h"
  25. #include "tracktooltip.h" //setScroll()
  26. #include <tqaccel.h> //our quit shortcut in the ctor
  27. #include <tqevent.h> //various events
  28. #include <tqfont.h>
  29. #include <tqhbox.h>
  30. #include <tqlabel.h>
  31. #include <tqpainter.h>
  32. #include <tqpixmap.h>
  33. #include <tqstringlist.h>
  34. #include <tqtimer.h>
  35. #include <tqtooltip.h> //analyzer tooltip
  36. #include <tdeapplication.h>
  37. #include <tdelocale.h>
  38. #include <tdemessagebox.h>
  39. #include <kpushbutton.h>
  40. #include <kstandarddirs.h>
  41. #include <kurldrag.h>
  42. #include <twin.h> //eventFilter()
  43. #include <kimageeffect.h>
  44. //simple function for fetching amarok images
  45. namespace Amarok
  46. {
  47. //TODO remove these, they suck, do a generic getImage
  48. TQPixmap getPNG( const TQString &filename )
  49. {
  50. TQString file = !filename.endsWith( ".png", false ) ? "amarok/images/%1.png" : "amarok/images/%1";
  51. TQPixmap pix;
  52. TQImage img( locate( "data", file.arg( filename ) ), "PNG" );
  53. if (TQPaintDevice::x11AppDepth() == 32) pix.convertFromImage(KImageEffect::convertToPremultipliedAlpha( img ));
  54. else pix.convertFromImage( img );
  55. return pix;
  56. }
  57. TQPixmap getJPG( const TQString &filename )
  58. {
  59. TQString file = !filename.endsWith( ".jpg", false ) ? "amarok/images/%1.jpg" : "amarok/images/%1";
  60. TQPixmap pix;
  61. TQImage img( locate( "data", TQString( "amarok/images/%1.jpg" ).arg( filename ) ), "JPEG" );
  62. if (TQPaintDevice::x11AppDepth() == 32) pix.convertFromImage(KImageEffect::convertToPremultipliedAlpha( img ));
  63. else pix.convertFromImage( img );
  64. return pix;
  65. }
  66. }
  67. using Amarok::getPNG;
  68. //fairly pointless template which was designed to make the ctor clearer,
  69. //but probably achieves the opposite. Still, the code is neater..
  70. template<class W> static inline W*
  71. createWidget( const TQRect &r, TQWidget *parent, const char *name = 0, TQt::WFlags f = 0 )
  72. {
  73. W *w = new W( parent, name, f );
  74. w->setGeometry( r );
  75. return w;
  76. }
  77. PlayerWidget::PlayerWidget( TQWidget *parent, const char *name, bool enablePlaylist )
  78. : TQWidget( parent, name, TQt::WType_TopLevel )
  79. , EngineObserver( EngineController::instance() )
  80. , m_minimalView( false )
  81. , m_pAnimTimer( new TQTimer( this ) )
  82. , m_scrollBuffer( 291, 16 )
  83. , m_plusPixmap( getPNG( "time_plus" ) )
  84. , m_minusPixmap( getPNG( "time_minus" ) )
  85. , m_pAnalyzer( 0 )
  86. {
  87. //the createWidget template function is used here
  88. //createWidget just creates a widget which has it's geometry set too
  89. // Sets caption and icon correctly (needed e.g. for GNOME)
  90. kapp->setTopWidget( this );
  91. parent->installEventFilter( this ); //for hidePLaylistWithMainWindow mode
  92. //if this is the first time we have ever been run we let KWin place us
  93. if ( AmarokConfig::playerPos() != TQPoint(-1,-1) )
  94. move( AmarokConfig::playerPos() );
  95. setModifiedPalette();
  96. setFixedSize( 311, 140 );
  97. setCaption( "Amarok" );
  98. setAcceptDrops( true );
  99. //another quit shortcut because the other window has all the accels
  100. TQAccel *accel = new TQAccel( this );
  101. accel->insertItem( CTRL + Key_Q );
  102. connect( accel, TQT_SIGNAL( activated( int ) ), kapp, TQT_SLOT( quit() ) );
  103. TQFont font;
  104. font.setBold( true );
  105. font.setPixelSize( 10 );
  106. setFont( font );
  107. { //<NavButtons>
  108. //NOTE we use a layout for the buttons so resizing will be possible
  109. m_pFrameButtons = createWidget<TQHBox>( TQRect(0, 118, 311, 22), this );
  110. TDEActionCollection *ac =Amarok::actionCollection();
  111. //FIXME change the names of the icons to reflect kde names so we can fall back to them if necessary
  112. new NavButton( m_pFrameButtons, "prev", ac->action( "prev" ) );
  113. m_pButtonPlay = new NavButton( m_pFrameButtons, "play", ac->action( "play" ) );
  114. m_pButtonPause = new NavButton( m_pFrameButtons, "pause", ac->action( "pause" ) );
  115. new NavButton( m_pFrameButtons, "stop", ac->action( "stop" ) );
  116. new NavButton( m_pFrameButtons, "next", ac->action( "next" ) );
  117. KPushButton *switchView = new KPushButton( KGuiItem( "", "mini_dock" ), m_pFrameButtons );
  118. switchView->setSizePolicy( TQSizePolicy::Maximum, TQSizePolicy::Preferred ); // too big!
  119. switchView->setFocusPolicy( TQ_NoFocus );
  120. connect( switchView, TQT_SIGNAL( clicked() ), TQT_SLOT( toggleView() ) );
  121. m_pButtonPlay->setToggleButton( true );
  122. m_pButtonPause->setToggleButton( true );
  123. } //</NavButtons>
  124. { //<Sliders>
  125. m_pSlider = new Amarok::PrettySlider( Qt::Horizontal,
  126. Amarok::PrettySlider::Pretty, this );
  127. m_pVolSlider = new Amarok::PrettySlider( Qt::Vertical,
  128. Amarok::PrettySlider::Pretty, this,
  129. Amarok::VOLUME_MAX );
  130. m_pSlider->setGeometry( 4,103, 303, 12 );
  131. m_pVolSlider->setGeometry( 294,18, 12,79 );
  132. m_pVolSlider->setValue( AmarokConfig::masterVolume() );
  133. EngineController* const ec = EngineController::instance();
  134. connect( m_pSlider, TQT_SIGNAL(sliderReleased( int )), ec, TQT_SLOT(seek( int )) );
  135. connect( m_pSlider, TQT_SIGNAL(valueChanged( int )), TQT_SLOT(timeDisplay( int )) );
  136. connect( m_pVolSlider, TQT_SIGNAL(sliderMoved( int )), ec, TQT_SLOT(setVolume( int )) );
  137. connect( m_pVolSlider, TQT_SIGNAL(sliderReleased( int )), ec, TQT_SLOT(setVolume( int )) );
  138. } //<Sliders>
  139. { //<Scroller>
  140. font.setPixelSize( 11 );
  141. const int fontHeight = TQFontMetrics( font ).height(); //the real height is more like 13px
  142. m_pScrollFrame = createWidget<TQFrame>( TQRect(6,18, 285,fontHeight), this );
  143. m_pScrollFrame->setFont( font );
  144. { //</Scroller>
  145. } //<TimeLabel>
  146. font.setPixelSize( 18 );
  147. m_pTimeLabel = createWidget<TQLabel>( TQRect(16,36, 9*12+2,18), this, 0, TQt::WNoAutoErase );
  148. m_pTimeLabel->setFont( font );
  149. m_timeBuffer.resize( m_pTimeLabel->size() );
  150. m_timeBuffer.fill( backgroundColor() );
  151. } //<TimeLabel>
  152. m_pButtonEq = new IconButton( TQT_TQWIDGET(this), "eq", TQT_TQOBJECT(this), TQT_SLOT(slotShowEqualizer( bool )) );
  153. m_pButtonEq->setGeometry( 34,85, 28,13 );
  154. //TODO set isOn()
  155. m_pPlaylistButton = new IconButton( this, "pl", TQT_SIGNAL(playlistToggled( bool )) );
  156. m_pPlaylistButton->setGeometry( 5,85, 28,13 );
  157. m_pPlaylistButton->setOn( parent->isShown() || enablePlaylist );
  158. m_pDescription = createWidget<TQLabel>( TQRect(4,6, 250,10), this );
  159. m_pTimeSign = createWidget<TQLabel>( TQRect(6,40, 10,10), this, 0, TQt::WRepaintNoErase );
  160. m_pVolSign = createWidget<TQLabel>( TQRect(295,7, 9,8), this );
  161. m_pDescription->setText( i18n( "Artist-Title|Album|Length" ) );
  162. m_pVolSign ->setPixmap( getPNG( "vol_speaker" ) );
  163. //do before we set the widget's state
  164. applySettings();
  165. //set interface to correct state
  166. engineStateChanged( EngineController::engine()->state() );
  167. createAnalyzer( 0 );
  168. //so we get circulation events to x11Event()
  169. //XSelectInput( x11Display(), winId(), StructureNotifyMask );
  170. //Yagami mode!
  171. //KWin::setState( winId(), NET::KeepBelow | NET::SkipTaskbar | NET::SkipPager );
  172. //KWin::setType( winId(), NET::Override );
  173. //KWin::setOnAllDesktops( winId(), true );
  174. connect( m_pAnimTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( drawScroll() ) );
  175. TrackToolTip::instance()->addToWidget( m_pScrollFrame );
  176. }
  177. PlayerWidget::~PlayerWidget()
  178. {
  179. AmarokConfig::setPlayerPos( pos() );
  180. AmarokConfig::setPlaylistWindowEnabled( m_pPlaylistButton->isOn() );
  181. TrackToolTip::instance()->removeFromWidget( m_pScrollFrame );
  182. }
  183. // METHODS ----------------------------------------------------------------
  184. void PlayerWidget::setScroll( const TQStringList &list )
  185. {
  186. TQString text;
  187. TQStringList list2( list );
  188. TQStringList::Iterator end( list2.end() );
  189. for( TQStringList::Iterator it = list2.begin(); it != end; )
  190. {
  191. if( !(*it).isEmpty() )
  192. {
  193. text.append( *it );
  194. ++it;
  195. }
  196. else it = list2.remove( it );
  197. }
  198. //FIXME empty TQString would crash due to NULL Pixmaps
  199. if( text.isEmpty() ) text = i18n( "Please report this message to trinity-devel@lists.pearsoncomputing.net, thanks!" );
  200. TQFont font( m_pScrollFrame->font() );
  201. TQFontMetrics fm( font );
  202. const uint separatorWidth = 21;
  203. const uint baseline = font.pixelSize(); //the font actually extends below its pixelHeight
  204. const uint separatorYPos = baseline - fm.boundingRect( "x" ).height() + 1;
  205. m_scrollTextPixmap.resize( fm.width( text ) + list2.count() * separatorWidth, m_pScrollFrame->height() );
  206. m_scrollTextPixmap.fill( backgroundColor() );
  207. TQPainter p( &m_scrollTextPixmap );
  208. p.setPen( foregroundColor() );
  209. p.setFont( font );
  210. uint x = 0;
  211. for( TQStringList::ConstIterator it = list2.constBegin();
  212. it != list2.constEnd();
  213. ++it )
  214. {
  215. p.drawText( x, baseline, *it );
  216. x += fm.width( *it );
  217. p.fillRect( x + 8, separatorYPos, 4, 4, Amarok::ColorScheme::Foreground );
  218. x += separatorWidth;
  219. }
  220. drawScroll();
  221. }
  222. void PlayerWidget::drawScroll()
  223. {
  224. static uint phase = 0;
  225. TQPixmap* const buffer = &m_scrollBuffer;
  226. TQPixmap* const scroll = &m_scrollTextPixmap;
  227. const uint topMargin = 0; //moved margins into widget placement
  228. const uint leftMargin = 0; //as this makes it easier to fiddle
  229. const uint w = m_scrollTextPixmap.width();
  230. const uint h = m_scrollTextPixmap.height();
  231. phase += SCROLL_RATE;
  232. if( phase >= w ) phase = 0;
  233. int subs = 0;
  234. int dx = leftMargin;
  235. uint phase2 = phase;
  236. while( dx < m_pScrollFrame->width() )
  237. {
  238. subs = -m_pScrollFrame->width() + topMargin;
  239. subs += dx + ( w - phase2 );
  240. if( subs < 0 ) subs = 0;
  241. bitBlt( buffer, dx, topMargin, scroll, phase2, 0, w - phase2 - subs, h, TQt::CopyROP );
  242. dx += w - phase2;
  243. phase2 += w - phase2;
  244. if( phase2 >= w ) phase2 = 0;
  245. }
  246. bitBlt( m_pScrollFrame, 0, 0, buffer );
  247. }
  248. void PlayerWidget::engineStateChanged( Engine::State state, Engine::State /*oldState*/ )
  249. {
  250. DEBUG_BLOCK
  251. switch( state )
  252. {
  253. case Engine::Empty:
  254. m_pButtonPlay->setOn( false );
  255. m_pButtonPause->setOn( false );
  256. m_pSlider->setValue( 0 );
  257. m_pSlider->setMinValue( 0 );
  258. m_pSlider->setMaxValue( 0 );
  259. m_pSlider->newBundle( MetaBundle() ); // Set an empty bundle for no moodbar
  260. m_pTimeLabel->hide();
  261. m_pTimeSign->hide();
  262. m_rateString = TQString();
  263. m_pSlider->setEnabled( false );
  264. setScroll( i18n( "Welcome to Amarok" ) );
  265. update();
  266. break;
  267. case Engine::Playing:
  268. if( !m_minimalView )
  269. {
  270. m_pTimeLabel->show();
  271. m_pTimeSign->show();
  272. }
  273. m_pButtonPlay->setOn( true );
  274. m_pButtonPause->setOn( false );
  275. break;
  276. case Engine::Paused:
  277. m_pButtonPause->setOn( true );
  278. break;
  279. case Engine::Idle: //don't really want to do anything when idle
  280. break;
  281. }
  282. }
  283. void PlayerWidget::engineVolumeChanged( int percent )
  284. {
  285. m_pVolSlider->setValue( percent );
  286. }
  287. void PlayerWidget::engineNewMetaData( const MetaBundle &bundle, bool )
  288. {
  289. m_currentURL == bundle.url().path();
  290. m_pSlider->setMinValue( 0 ); // Important. minValue could have been changed by bogus maxValues
  291. m_pSlider->setMaxValue( bundle.length() * 1000 );
  292. m_pSlider->setEnabled( bundle.length() > 0 );
  293. m_pSlider->newBundle( bundle );
  294. m_rateString = bundle.prettyBitrate();
  295. TQString Hz = bundle.prettySampleRate( true );
  296. if( !Hz.isEmpty() )
  297. {
  298. if( m_rateString.isEmpty() )
  299. m_rateString = Hz;
  300. else
  301. m_rateString = i18n("%1 kBit - %2").arg( m_rateString, Hz );
  302. }
  303. TQStringList list( bundle.prettyTitle() );
  304. list << bundle.album();
  305. if( bundle.length() ) list << bundle.prettyLength();
  306. setScroll( list );
  307. update(); //we need to update rateString
  308. }
  309. void PlayerWidget::engineTrackPositionChanged( long position, bool /*userSeek*/ )
  310. {
  311. m_pSlider->setValue( position );
  312. if( !m_pSlider->isEnabled() ) timeDisplay( position );
  313. }
  314. void PlayerWidget::engineTrackLengthChanged( long length )
  315. {
  316. m_pSlider->setMaxValue( length * 1000 );
  317. }
  318. void PlayerWidget::timeDisplay( int ms )
  319. {
  320. int seconds = ms / 1000;
  321. const int songLength = EngineController::instance()->bundle().length();
  322. const bool showRemaining = AmarokConfig::leftTimeDisplayRemaining() && songLength > 0;
  323. if( showRemaining ) seconds = songLength - seconds;
  324. m_timeBuffer.fill( backgroundColor() );
  325. TQPainter p( &m_timeBuffer );
  326. p.setPen( foregroundColor() );
  327. p.setFont( m_pTimeLabel->font() );
  328. p.drawText( 0, 16, MetaBundle::prettyTime( seconds ) ); //FIXME remove padding, instead move()!
  329. bitBlt( m_pTimeLabel, 0, 0, &m_timeBuffer );
  330. m_pTimeSign->setPixmap( showRemaining ? m_minusPixmap : m_plusPixmap );
  331. }
  332. static inline TQColor comodulate( int hue, TQColor target )
  333. {
  334. ///this function is only used by determineAmarokColors()
  335. int ignore, s, v;
  336. target.getHsv( &ignore, &s, &v );
  337. return TQColor( hue, s, v, TQColor::Hsv );
  338. }
  339. void PlayerWidget::determineAmarokColors() //static
  340. {
  341. int hue, s, v;
  342. (!AmarokConfig::schemeKDE()
  343. ? AmarokConfig::playlistWindowBgColor()
  344. : TDEGlobalSettings::highlightColor()
  345. ).getHsv( &hue, &s, &v );
  346. using namespace Amarok::ColorScheme;
  347. Text = TQt::white;
  348. Background = comodulate( hue, 0x002090 );
  349. Foreground = comodulate( hue, 0x80A0FF );
  350. //ensure the base colour does not conflict with the window decoration colour
  351. //however generally it is nice to leave the other colours with the highlight hue
  352. //because the scheme is then "complimentary"
  353. //TODO schemes that have totally different active/inactive decoration colours need to be catered for too!
  354. if ( AmarokConfig::schemeKDE() ) {
  355. int h;
  356. TDEGlobalSettings::activeTitleColor().getHsv( &h, &s, &v );
  357. if( TQABS( hue - h ) > 120 )
  358. hue = h;
  359. }
  360. Base = comodulate( hue, Amarok::blue );
  361. }
  362. void PlayerWidget::setModifiedPalette()
  363. {
  364. TQPalette p = TQApplication::palette();
  365. TQColorGroup cg = p.active();
  366. cg.setColor( TQColorGroup::Background, Amarok::ColorScheme::Base );
  367. cg.setColor( TQColorGroup::Foreground, Amarok::ColorScheme::Text );
  368. setPalette( TQPalette(cg, p.disabled(), cg) );
  369. }
  370. void PlayerWidget::applySettings()
  371. {
  372. //NOTE DON'T use unsetFont(), we use custom font sizes (for now)
  373. TQFont phont = font();
  374. phont.setFamily( AmarokConfig::useCustomFonts()
  375. ? AmarokConfig::playerWidgetFont().family()
  376. : TQApplication::font().family() );
  377. setFont( phont );
  378. setModifiedPalette();
  379. //update the scroller
  380. switch( EngineController::engine()->state() ) {
  381. case Engine::Empty:
  382. m_scrollTextPixmap.fill( Amarok::ColorScheme::Base );
  383. update();
  384. break;
  385. default:
  386. engineNewMetaData( EngineController::instance()->bundle(), false );
  387. }
  388. if(m_pAnalyzer)
  389. setMinimalView(m_minimalView);
  390. }
  391. void PlayerWidget::setMinimalView( bool enable )
  392. {
  393. m_pAnalyzer->setHidden( enable );
  394. m_pTimeLabel->setHidden( enable );
  395. m_pTimeSign->setHidden( enable );
  396. m_pDescription->setHidden( enable );
  397. m_pButtonEq->setHidden( enable );
  398. m_pPlaylistButton->setHidden( enable );
  399. m_pVolSlider->setHidden( enable );
  400. if( enable )
  401. {
  402. uint space = 2;
  403. m_pScrollFrame->setGeometry ( 6,space, m_pScrollFrame->width(), m_pScrollFrame->height() );
  404. m_pSlider->setGeometry ( 4,space + m_pScrollFrame->height(), 303, 12 );
  405. m_pFrameButtons->setGeometry( 0,space + m_pScrollFrame->height() + m_pSlider->height(), 311,22 );
  406. uint height = m_pFrameButtons->height() + m_pScrollFrame->height() + m_pSlider->height() + space;
  407. setFixedSize( 311, height );
  408. AmarokConfig::setPlayerWindowMinimalView( true );
  409. }
  410. else
  411. {
  412. m_pScrollFrame->setGeometry( 6,18, m_pScrollFrame->width(),m_pScrollFrame->height() );
  413. m_pSlider->setGeometry( 4,103, 303,12 );
  414. m_pFrameButtons->setGeometry(0,118, 311,22);
  415. setFixedSize( 311, 140 );
  416. AmarokConfig::setPlayerWindowMinimalView( false );
  417. }
  418. m_minimalView = enable;
  419. update();
  420. }
  421. // EVENTS -----------------------------------------------------------------
  422. static bool dontChangeButtonState = false; //FIXME I hate this hack
  423. bool PlayerWidget::event( TQEvent *e )
  424. {
  425. switch( e->type() )
  426. {
  427. case TQEvent::Wheel:
  428. case TQEvent::DragEnter:
  429. case TQEvent::Drop:
  430. case TQEvent::Close:
  431. Amarok::genericEventHandler( this, e );
  432. return true; //we handled it
  433. case TQEvent::ApplicationPaletteChange:
  434. if( AmarokConfig::schemeKDE() )
  435. {
  436. determineAmarokColors();
  437. applySettings();
  438. }
  439. return true;
  440. case 6/*TQEvent::KeyPress*/:
  441. if (TQT_TQKEYEVENT(e)->key() == TQt::Key_D/* && (m_pAnalyzer->inherits(TQGLWIDGET_OBJECT_NAME_STRING)*/)
  442. {
  443. if( m_pAnalyzer->parent() )
  444. {
  445. m_pAnalyzer->reparent( 0, TQPoint(50,50), true );
  446. m_pAnalyzer->setCaption( kapp->makeStdCaption( i18n("Analyzer") ) );
  447. m_pAnalyzer->installEventFilter( this );
  448. m_pAnalyzer->setPaletteBackgroundColor( paletteBackgroundColor() );
  449. TQToolTip::remove( m_pAnalyzer );
  450. }
  451. else
  452. createAnalyzer( 0 );
  453. return true; //eat event
  454. }
  455. return false; //don't eat event
  456. case TQEvent::Show:
  457. m_pAnimTimer->start( ANIM_TIMER );
  458. if( m_pPlaylistButton->isOn() )
  459. {
  460. //IMPORTANT! If the PlaylistButton is on then we MUST be shown
  461. //we leave the PlaylistButton "on" to signify that we should restore it here
  462. //we leave it on when we do a hidePlaylistWithPlayerWindow type action
  463. //IMPORTANT - I beg of you! Please leave all this alone, it was hell to
  464. //create! If you have an issue with the behaviour bring it up on the mailing
  465. //list before you even think about committing. Thanks! (includes case Hide)
  466. const WId id = parentWidget()->winId();
  467. const uint desktop = KWin::windowInfo( winId() ).desktop();
  468. const KWin::WindowInfo info = KWin::windowInfo( id );
  469. //check the Playlist Window is on the correct desktop
  470. if( !info.isOnDesktop( desktop ) ) KWin::setOnDesktop( id, desktop );
  471. if( info.mappingState() == NET::Withdrawn )
  472. {
  473. //extern Atom tqt_wm_state; //XAtom defined by TQt
  474. //TODO prevent the active Window flicker from playlist to player window please!
  475. //TODO look at code for TQWidget::show();
  476. //XDeleteProperty( tqt_xdisplay(), id, tqt_wm_state );
  477. //parentWidget()->show();
  478. //if( !parentWidget()->isShown() ) XMapWindow( tqt_xdisplay(), id );
  479. // unsigned long data[2];
  480. // data[0] = (unsigned long) NormalState;
  481. // data[1] = (unsigned long) None;
  482. //
  483. // XChangeProperty( tqt_xdisplay(), id, tqt_wm_state, tqt_wm_state, 32,
  484. // PropModeReplace, (unsigned char *)data, 2);
  485. //
  486. // KWin::clearState( id, NET::Hidden );
  487. //
  488. // XMapWindow( tqt_xdisplay(), id );
  489. //
  490. //KWin::deIconifyWindow( id, false );
  491. parentWidget()->show();
  492. }
  493. if( info.isMinimized() )
  494. {
  495. //then the user will expect us to deiconify the Playlist Window
  496. //the PlaylistButton would be off otherwise (honest!)
  497. KWin::deIconifyWindow( id, false );
  498. }
  499. }
  500. return false;
  501. case TQEvent::Hide:
  502. m_pAnimTimer->stop();
  503. {
  504. //this prevents the PlaylistButton being set to off (see the eventFilter)
  505. //by leaving it on we ensure that we show the Playlist Window again when
  506. //we are next shown (see Show event handler above)
  507. if( parentWidget()->isShown() ) dontChangeButtonState = true;
  508. if( e->spontaneous() ) //the window system caused the event
  509. {
  510. //if we have been iconified, iconify the Playlist Window too
  511. //if we have been shaded, hide the PlaylistWindow
  512. //if the user is on another desktop to Amarok, do nothing
  513. const KWin::WindowInfo info = KWin::windowInfo( winId() );
  514. if( info.isMinimized() ) KWin::iconifyWindow( parentWidget()->winId(), false );
  515. else
  516. //this may seem strange, but it is correct
  517. //we have a handler in eventFilter for all other eventualities
  518. dontChangeButtonState = false;
  519. }
  520. else
  521. //we caused Amarok to hide, so we should hide the Playlist Window
  522. //NOTE we "override" closeEvents and thus they count as non-spontaneous
  523. //hideEvents; which frankly is a huge relief!
  524. parentWidget()->hide();
  525. }
  526. return false;
  527. default:
  528. return TQWidget::event( e );
  529. }
  530. }
  531. // bool
  532. // PlayerWidget::x11Event( XEvent *e )
  533. // {
  534. // if( e->type == ConfigureNotify )
  535. // {
  536. // kdDebug() << "CirculateNotify\n";
  537. // XRaiseWindow( x11Display(), playlistWindow()->winId() );
  538. // }
  539. //
  540. // return false;
  541. // }
  542. bool
  543. PlayerWidget::eventFilter( TQObject *o, TQEvent *e )
  544. {
  545. //NOTE we only monitor for parent() - which is the PlaylistWindow
  546. if( TQT_BASE_OBJECT(o) == TQT_BASE_OBJECT(m_pAnalyzer) )
  547. {
  548. //delete analyzer, create same one back in Player Window
  549. if( e->type() == TQEvent::Close )
  550. {
  551. createAnalyzer( 0 );
  552. return true;
  553. }
  554. return false;
  555. }
  556. switch( e->type() )
  557. {
  558. case TQEvent::Close:
  559. TQT_TQCLOSEEVENT(e)->accept(); //close the window!
  560. return true; //don't let PlaylistWindow have the event - see PlaylistWindow::closeEvent()
  561. case TQEvent::Hide:
  562. if( dontChangeButtonState )
  563. {
  564. //we keep the PlaylistButton set to "on" - see event() for more details
  565. //NOTE the Playlist Window will still be hidden
  566. dontChangeButtonState = false;
  567. break;
  568. }
  569. if( e->spontaneous() )
  570. {
  571. //we want to avoid setting the button for most spontaneous events
  572. //since they are not user driven, two are however:
  573. KWin::WindowInfo info = KWin::windowInfo( parentWidget()->winId() );
  574. if( !info.isMinimized() ) break;
  575. }
  576. //FALL THROUGH
  577. case TQEvent::Show:
  578. if( isShown() )
  579. {
  580. //only when shown means thaman:mkreiserfst using the global Show/Hide Playlist shortcut
  581. //when in the tray doesn't effect the state of the PlaylistButton
  582. //this is a good thing, but we have to set the state correctly when we are shown
  583. m_pPlaylistButton->blockSignals( true );
  584. m_pPlaylistButton->setOn( e->type() == TQEvent::Show );
  585. m_pPlaylistButton->blockSignals( false );
  586. }
  587. break;
  588. default:
  589. break;
  590. }
  591. return false;
  592. }
  593. void PlayerWidget::paintEvent( TQPaintEvent* )
  594. {
  595. //uses widget's font and foregroundColor() - see ctor
  596. TQPainter p( this );
  597. if( !m_minimalView )
  598. p.drawText( 6, 68, m_rateString );
  599. bitBlt( m_pScrollFrame, 0, 0, &m_scrollBuffer );
  600. bitBlt( m_pTimeLabel, 0, 0, &m_timeBuffer );
  601. }
  602. void PlayerWidget::contextMenuEvent( TQMouseEvent *e )
  603. {
  604. Amarok::Menu::instance()->exec( e->globalPos() );
  605. }
  606. void PlayerWidget::mousePressEvent( TQMouseEvent *e )
  607. {
  608. if ( e->button() == Qt::RightButton )
  609. {
  610. //Amarok::Menu::instance()->exec( e->globalPos() );
  611. }
  612. else if ( m_pAnalyzer->geometry().contains( e->pos() ) )
  613. {
  614. createAnalyzer( e->state() & TQt::ControlButton ? -1 : +1 );
  615. }
  616. else
  617. {
  618. TQRect
  619. rect = m_pTimeLabel->geometry();
  620. rect |= m_pTimeSign->geometry();
  621. if ( rect.contains( e->pos() ) )
  622. {
  623. AmarokConfig::setLeftTimeDisplayRemaining( !AmarokConfig::leftTimeDisplayRemaining() );
  624. timeDisplay( EngineController::engine()->position() );
  625. }
  626. else m_startDragPos = e->pos();
  627. }
  628. }
  629. void PlayerWidget::mouseMoveEvent( TQMouseEvent *e )
  630. {
  631. if( e->state() & Qt::LeftButton )
  632. {
  633. const int distance = (e->pos() - m_startDragPos).manhattanLength();
  634. if( distance > TQApplication::startDragDistance() ) startDrag();
  635. }
  636. }
  637. // SLOTS ---------------------------------------------------------------------
  638. void PlayerWidget::createAnalyzer( int increment )
  639. {
  640. AmarokConfig::setCurrentAnalyzer( AmarokConfig::currentAnalyzer() + increment );
  641. delete m_pAnalyzer;
  642. m_pAnalyzer = Analyzer::Factory::createAnalyzer( this );
  643. m_pAnalyzer->setGeometry( 120,40, 168,56 );
  644. TQToolTip::add( m_pAnalyzer, i18n( "Click for more analyzers, press 'd' to detach." ) );
  645. m_pAnalyzer->show();
  646. }
  647. void PlayerWidget::startDrag()
  648. {
  649. TQDragObject *d = new TQTextDrag( EngineController::instance()->bundle().prettyTitle(), this );
  650. d->dragCopy();
  651. // TQt will delete d for us.
  652. }
  653. void PlayerWidget::slotShowEqualizer( bool show ) //SLOT
  654. {
  655. if( show )
  656. {
  657. m_pButtonEq->setOff();
  658. if ( !EngineController::hasEngineProperty( "HasEqualizer" ) )
  659. KMessageBox::sorry( 0, i18n( "Equalizer is not available with this engine." ) );
  660. else
  661. TQTimer::singleShot( 0, kapp, TQT_SLOT( slotConfigEqualizer() ) );
  662. }
  663. }
  664. //////////////////////////////////////////////////////////////////////////////////////////
  665. // CLASS NavButton
  666. //////////////////////////////////////////////////////////////////////////////////////////
  667. #include <kiconeffect.h>
  668. #include <kimageeffect.h>
  669. NavButton::NavButton( TQWidget *parent, const TQString &icon, TDEAction *action )
  670. : TQToolButton( parent )
  671. , m_glowIndex( 0 )
  672. {
  673. // Prevent flicker
  674. setWFlags( TQt::WNoAutoErase );
  675. TQPixmap pixmap( getPNG( "b_" + icon ) );
  676. TDEIconEffect ie;
  677. // Tint icon blueish for "off" state
  678. m_pixmapOff = ie.apply( pixmap, TDEIconEffect::Colorize, 0.5, TQColor( 0x30, 0x10, 0xff ), false );
  679. // Tint gray and make pseudo-transparent for "disabled" state
  680. m_pixmapDisabled = ie.apply( pixmap, TDEIconEffect::ToGray, 0.7, TQColor(), true );
  681. int r = 0x20, g = 0x10, b = 0xff;
  682. float percentRed = 0.0;
  683. TQPixmap temp;
  684. // Precalculate pixmaps for "on" icon state
  685. for ( int i = 0; i < NUMPIXMAPS; i++ ) {
  686. TQImage img = pixmap.convertToImage();
  687. temp = KImageEffect::channelIntensity( img, percentRed, KImageEffect::Red );
  688. temp = ie.apply( temp, TDEIconEffect::Colorize, 1.0, TQColor( r, 0x10, 0x30 ), false );
  689. temp = ie.apply( temp, TDEIconEffect::Colorize, 1.0, TQColor( r, g, b ), false );
  690. // Create new pixmap on the heap and add pointer to list
  691. m_glowPixmaps.append( temp );
  692. percentRed = percentRed + 1.0 / NUMPIXMAPS;
  693. r += 14;
  694. g += 2;
  695. b -= 0;
  696. }
  697. // And the the same reversed
  698. for ( int i = NUMPIXMAPS - 1; i > 0; i-- )
  699. {
  700. TQPixmap temp = m_glowPixmaps[i];
  701. m_glowPixmaps.append(temp);
  702. }
  703. // This is just for initialization
  704. TQIconSet iconSet;
  705. iconSet.setPixmap( pixmap, TQIconSet::Automatic, TQIconSet::Normal, TQIconSet::Off );
  706. iconSet.setPixmap( pixmap, TQIconSet::Automatic, TQIconSet::Normal, TQIconSet::On );
  707. iconSet.setPixmap( pixmap, TQIconSet::Automatic, TQIconSet::Disabled, TQIconSet::Off );
  708. setIconSet( iconSet );
  709. setFocusPolicy( TQ_NoFocus );
  710. setEnabled( action->isEnabled() );
  711. connect( action, TQT_SIGNAL( enabled( bool ) ), TQT_SLOT( setEnabled( bool ) ) );
  712. connect( this, TQT_SIGNAL( clicked() ), action, TQT_SLOT( activate() ) );
  713. startTimer( GLOW_INTERVAL );
  714. }
  715. void NavButton::timerEvent( TQTimerEvent* )
  716. {
  717. if ( isOn() ) {
  718. m_glowIndex++;
  719. m_glowIndex %= NUMPIXMAPS * 2 - 1;
  720. // Repaint widget with new pixmap
  721. update();
  722. }
  723. }
  724. void NavButton::drawButtonLabel( TQPainter* p )
  725. {
  726. int x = width() / 2 - m_pixmapOff.width() / 2;
  727. int y = height() / 2 - m_pixmapOff.height() / 2;
  728. if ( !isEnabled() )
  729. p->drawPixmap( x, y, m_pixmapDisabled );
  730. else if ( isOn() )
  731. p->drawPixmap( x + 2, y + 1, m_glowPixmaps[m_glowIndex] );
  732. else
  733. p->drawPixmap( x, y, m_pixmapOff );
  734. }
  735. //////////////////////////////////////////////////////////////////////////////////////////
  736. // CLASS IconButton
  737. //////////////////////////////////////////////////////////////////////////////////////////
  738. IconButton::IconButton( TQWidget *parent, const TQString &icon, const char *signal )
  739. : TQButton( parent )
  740. , m_up( getPNG( icon + "_active2" ) ) //TODO rename files better (like the right way round for one!)
  741. , m_down( getPNG( icon + "_inactive2" ) )
  742. {
  743. connect( this, TQT_SIGNAL(toggled( bool )), parent, signal );
  744. setToggleButton( true );
  745. setFocusPolicy( TQ_NoFocus ); //we have no way to show focus on these widgets currently
  746. }
  747. IconButton::IconButton( TQWidget *parent, const TQString &icon, TQObject* receiver, const char *slot )
  748. : TQButton( parent )
  749. , m_up( getPNG( icon + "_active2" ) ) //TODO rename files better (like the right way round for one!)
  750. , m_down( getPNG( icon + "_inactive2" ) )
  751. {
  752. connect( this, TQT_SIGNAL(toggled( bool )), receiver, slot );
  753. setToggleButton( true );
  754. setFocusPolicy( TQ_NoFocus ); //we have no way to show focus on these widgets currently
  755. }
  756. void IconButton::drawButton( TQPainter *p )
  757. {
  758. p->drawPixmap( 0, 0, (isOn()||isDown()) ? m_down : m_up );
  759. }
  760. #include "playerwindow.moc"