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.

546 lines
15KB

  1. /***************************************************************************
  2. amarokslider.cpp - description
  3. -------------------
  4. begin : Dec 15 2003
  5. copyright : (C) 2003 by Mark Kretschmann
  6. email : markey@web.de
  7. copyright : (C) 2005 by Gábor Lehel
  8. email : illissius@gmail.com
  9. ***************************************************************************/
  10. /***************************************************************************
  11. * *
  12. * This program is free software; you can redistribute it and/or modify *
  13. * it under the terms of the GNU General Public License as published by *
  14. * the Free Software Foundation; either version 2 of the License, or *
  15. * (at your option) any later version. *
  16. * *
  17. ***************************************************************************/
  18. #include <config.h>
  19. #include "amarok.h"
  20. #include "amarokconfig.h"
  21. #include "app.h"
  22. #include "debug.h"
  23. #include "enginecontroller.h"
  24. #include "sliderwidget.h"
  25. #include <tqapplication.h>
  26. #include <tqbitmap.h>
  27. #include <tqbrush.h>
  28. #include <tqimage.h>
  29. #include <tqpainter.h>
  30. #include <tqsize.h>
  31. #include <tqtimer.h>
  32. #include <kiconloader.h>
  33. #include <kimageeffect.h>
  34. #include <tdelocale.h>
  35. #include <kpixmapeffect.h>
  36. #include <tdepopupmenu.h>
  37. #include <kstandarddirs.h>
  38. Amarok::Slider::Slider( Qt::Orientation orientation, TQWidget *parent, uint max )
  39. : TQSlider( orientation, parent )
  40. , m_sliding( false )
  41. , m_outside( false )
  42. , m_prevValue( 0 )
  43. {
  44. setRange( 0, max );
  45. }
  46. void
  47. Amarok::Slider::wheelEvent( TQWheelEvent *e )
  48. {
  49. if( orientation() == Qt::Vertical ) {
  50. // Will be handled by the parent widget
  51. e->ignore();
  52. return;
  53. }
  54. // Position Slider (horizontal)
  55. int step = e->delta() * 1500 / 18;
  56. int nval = TQSlider::value() + step;
  57. nval = TQMAX(nval, minValue());
  58. nval = TQMIN(nval, maxValue());
  59. TQSlider::setValue( nval );
  60. emit sliderReleased( value() );
  61. }
  62. void
  63. Amarok::Slider::mouseMoveEvent( TQMouseEvent *e )
  64. {
  65. if ( m_sliding )
  66. {
  67. //feels better, but using set value of 20 is bad of course
  68. TQRect rect( -20, -20, width()+40, height()+40 );
  69. if ( orientation() == Qt::Horizontal && !rect.contains( e->pos() ) ) {
  70. if ( !m_outside )
  71. TQSlider::setValue( m_prevValue );
  72. m_outside = true;
  73. } else {
  74. m_outside = false;
  75. slideEvent( e );
  76. emit sliderMoved( value() );
  77. }
  78. }
  79. else TQSlider::mouseMoveEvent( e );
  80. }
  81. void
  82. Amarok::Slider::slideEvent( TQMouseEvent *e )
  83. {
  84. TQSlider::setValue( orientation() == Qt::Horizontal
  85. ? ((TQApplication::reverseLayout())
  86. ? TQRangeControl::valueFromPosition( width() - (e->pos().x() - sliderRect().width()/2), width() + sliderRect().width() )
  87. : TQRangeControl::valueFromPosition( e->pos().x() - sliderRect().width()/2, width() - sliderRect().width() ) )
  88. : TQRangeControl::valueFromPosition( e->pos().y() - sliderRect().height()/2, height() - sliderRect().height() ) );
  89. }
  90. void
  91. Amarok::Slider::mousePressEvent( TQMouseEvent *e )
  92. {
  93. m_sliding = true;
  94. m_prevValue = TQSlider::value();
  95. if ( !sliderRect().contains( e->pos() ) )
  96. mouseMoveEvent( e );
  97. }
  98. void
  99. Amarok::Slider::mouseReleaseEvent( TQMouseEvent* )
  100. {
  101. if( !m_outside && TQSlider::value() != m_prevValue )
  102. emit sliderReleased( value() );
  103. m_sliding = false;
  104. m_outside = false;
  105. }
  106. void
  107. Amarok::Slider::setValue( int newValue )
  108. {
  109. //don't adjust the slider while the user is dragging it!
  110. if ( !m_sliding || m_outside )
  111. TQSlider::setValue( adjustValue( newValue ) );
  112. else
  113. m_prevValue = newValue;
  114. }
  115. //////////////////////////////////////////////////////////////////////////////////////////
  116. /// CLASS PrettySlider
  117. //////////////////////////////////////////////////////////////////////////////////////////
  118. #define THICKNESS 7
  119. #define MARGIN 3
  120. Amarok::PrettySlider::PrettySlider( Qt::Orientation orientation, SliderMode mode,
  121. TQWidget *parent, uint max )
  122. : Amarok::Slider( orientation, parent, max )
  123. , m_mode( mode )
  124. , m_showingMoodbar( false )
  125. {
  126. if( m_mode == Pretty)
  127. {
  128. setWFlags( TQt::WNoAutoErase );
  129. setFocusPolicy( TQ_NoFocus );
  130. }
  131. // We only have to connect this *once*, since our MetaBundle
  132. // doesn't get destroyed until we do.
  133. connect( &m_bundle.moodbar(), TQT_SIGNAL( jobEvent( int ) ),
  134. TQT_SLOT( moodbarJobEvent( int ) ) );
  135. // We want to know if we should reset our moodbar data
  136. connect( App::instance(), TQT_SIGNAL( moodbarPrefs( bool, bool, int, bool ) ),
  137. TQT_SLOT( slotMoodbarPrefs( bool, bool, int, bool ) ) );
  138. }
  139. void
  140. Amarok::PrettySlider::mousePressEvent( TQMouseEvent *e )
  141. {
  142. Amarok::Slider::mousePressEvent( e );
  143. slideEvent( e );
  144. }
  145. void
  146. Amarok::PrettySlider::slideEvent( TQMouseEvent *e )
  147. {
  148. if( m_mode == Pretty || m_showingMoodbar )
  149. TQSlider::setValue( orientation() == Qt::Horizontal
  150. ? TQRangeControl::valueFromPosition( e->pos().x(), width()-2 )
  151. : TQRangeControl::valueFromPosition( e->pos().y(), height()-2 ) );
  152. else
  153. Amarok::Slider::slideEvent( e );
  154. }
  155. namespace Amarok {
  156. namespace ColorScheme {
  157. extern TQColor Background;
  158. extern TQColor Foreground;
  159. }
  160. }
  161. void
  162. Amarok::PrettySlider::paintEvent( TQPaintEvent *e )
  163. {
  164. const int w = orientation() == Qt::Horizontal ? width() : height();
  165. const int pos = int( double( w-2 ) / maxValue() * Slider::value() );
  166. int h = THICKNESS;
  167. m_showingMoodbar = ( !m_bundle.url().isEmpty() &&
  168. m_bundle.moodbar().dataExists() &&
  169. AmarokConfig::showMoodbar() );
  170. TQPixmap mood;
  171. if( m_showingMoodbar )
  172. {
  173. if( m_mode == Normal )
  174. h = (orientation() == Qt::Vertical ? width() : height()) - 2*MARGIN;
  175. mood = m_bundle.moodbar().draw( w, h );
  176. }
  177. // If we're a Normal PrettySlider and we have no moodbar,
  178. // emulate the behavior of Slider
  179. else if( m_mode == Normal )
  180. {
  181. Amarok::Slider::paintEvent( e );
  182. return;
  183. }
  184. TQPixmap buf( size() );
  185. TQPainter p( &buf, this );
  186. buf.fill( parentWidget()->backgroundColor() );
  187. if ( orientation() == Qt::Vertical )
  188. {
  189. p.translate( 0, height()-1 );
  190. p.rotate( -90 ); //90 degrees clockwise
  191. }
  192. if( !m_showingMoodbar )
  193. {
  194. p.translate( 0, MARGIN );
  195. p.setPen( Amarok::ColorScheme::Foreground );
  196. p.fillRect( 0, 0, pos, h, TQColor( Amarok::ColorScheme::Background ) );
  197. p.drawRect( 0, 0, w, h );
  198. p.translate( 0, -MARGIN );
  199. }
  200. else
  201. {
  202. p.translate( 0, MARGIN );
  203. p.drawPixmap( 0, 0, mood );
  204. p.setPen( Amarok::ColorScheme::Foreground );
  205. p.drawRect( 0, 0, w, h );
  206. p.translate( 0, -MARGIN );
  207. // Larger triangle for the moodbar
  208. }
  209. //<Triangle Marker>
  210. if( m_mode == Pretty )
  211. {
  212. TQPointArray pa( 3 );
  213. pa.setPoint( 0, pos - 3, 1 );
  214. pa.setPoint( 1, pos + 3, 1 );
  215. pa.setPoint( 2, pos, 5 );
  216. p.setBrush( paletteForegroundColor() );
  217. p.drawConvexPolygon( pa );
  218. }
  219. else if( m_mode == Normal )
  220. {
  221. TQPointArray pa( 3 );
  222. pa.setPoint( 0, pos - 5, 1 );
  223. pa.setPoint( 1, pos + 5, 1 );
  224. pa.setPoint( 2, pos, 9 );
  225. p.setBrush( paletteForegroundColor() );
  226. p.drawConvexPolygon( pa );
  227. }
  228. //</Triangle Marker>
  229. p.end();
  230. bitBlt( this, 0, 0, &buf );
  231. }
  232. // This gets called when the moodbar job starts or finishes
  233. void
  234. Amarok::PrettySlider::moodbarJobEvent( int newState )
  235. {
  236. if( newState == Moodbar::JobStateSucceeded )
  237. {
  238. debug() << "moodbarJobEvent: new moodbar data" << endl;
  239. update();
  240. }
  241. }
  242. // This gets called when the user presses "Ok" or "Apply" in the
  243. // config dialog. Reload our moodbar data, in case it was
  244. // permanently disabled before because the moodbar was disabled.
  245. void
  246. Amarok::PrettySlider::slotMoodbarPrefs( bool show, bool moodier, int alter, bool withMusic )
  247. {
  248. (void) moodier; (void) alter; (void) withMusic;
  249. if( show )
  250. {
  251. m_bundle.moodbar().reset();
  252. if( !m_bundle.moodbar().dataExists() )
  253. m_bundle.moodbar().load();
  254. update();
  255. }
  256. }
  257. // This is called when the track changes / stops / starts
  258. void
  259. Amarok::PrettySlider::newBundle( const MetaBundle &bundle )
  260. {
  261. m_bundle = bundle;
  262. m_bundle.detach();
  263. // This is the easiest way to tell if the bundle refers
  264. // to a real track, or if we're STOP'd.
  265. if( m_bundle.url().isEmpty() )
  266. return;
  267. // It's a real track; get the moodbar data if it's not there
  268. if( !m_bundle.moodbar().dataExists() )
  269. m_bundle.moodbar().load();
  270. else
  271. update();
  272. }
  273. #if 0
  274. /** these functions aren't required in our fixed size world,
  275. but they may become useful one day **/
  276. TQSize
  277. Amarok::PrettySlider::minimumSizeHint() const
  278. {
  279. return sizeHint();
  280. }
  281. TQSize
  282. Amarok::PrettySlider::sizeHint() const
  283. {
  284. constPolish();
  285. return (orientation() == Horizontal
  286. ? TQSize( maxValue(), THICKNESS + MARGIN )
  287. : TQSize( THICKNESS + MARGIN, maxValue() )).expandedTo( TQApplit ication::globalStrut() );
  288. }
  289. #endif
  290. //////////////////////////////////////////////////////////////////////////////////////////
  291. /// CLASS VolumeSlider
  292. //////////////////////////////////////////////////////////////////////////////////////////
  293. Amarok::VolumeSlider::VolumeSlider( TQWidget *parent, uint max )
  294. : Amarok::Slider( Qt::Horizontal, parent, max )
  295. , m_animCount( 0 )
  296. , m_animTimer( new TQTimer( this ) )
  297. , m_pixmapInset( TQPixmap( locate( "data","amarok/images/volumeslider-inset.png" ) ) )
  298. {
  299. setWFlags( getWFlags() | WNoAutoErase );
  300. setFocusPolicy( TQ_NoFocus );
  301. if (TQPaintDevice::x11AppDepth() == 32) m_pixmapInset.convertFromImage(KImageEffect::convertToPremultipliedAlpha( m_pixmapInset.convertToImage() ));
  302. // BEGIN Calculate handle animation pixmaps for mouse-over effect
  303. TQImage pixmapHandle ( locate( "data","amarok/images/volumeslider-handle.png" ) );
  304. if (TQPaintDevice::x11AppDepth() == 32) pixmapHandle = KImageEffect::convertToPremultipliedAlpha( pixmapHandle );
  305. TQImage pixmapHandleGlow( locate( "data","amarok/images/volumeslider-handle_glow.png" ) );
  306. float opacity = 0.0;
  307. const float step = 1.0 / ANIM_MAX;
  308. TQImage dst;
  309. for ( int i = 0; i < ANIM_MAX; ++i ) {
  310. dst = pixmapHandle;
  311. KImageEffect::blend( pixmapHandleGlow, dst, opacity );
  312. if (TQPaintDevice::x11AppDepth() == 32) dst = KImageEffect::convertToPremultipliedAlpha( dst );
  313. m_handlePixmaps.append( TQPixmap( dst ) );
  314. opacity += step;
  315. }
  316. // END
  317. generateGradient();
  318. setMinimumWidth( m_pixmapInset.width() );
  319. setMinimumHeight( m_pixmapInset.height() );
  320. connect( m_animTimer, TQT_SIGNAL( timeout() ), this, TQT_SLOT( slotAnimTimer() ) );
  321. }
  322. void
  323. Amarok::VolumeSlider::generateGradient()
  324. {
  325. //TQImage temp( locate( "data","amarok/images/volumeslider-gradient.png" ) );
  326. //TDEIconEffect::colorize( temp, colorGroup().highlight(), 1.0 );
  327. const TQPixmap temp( locate( "data","amarok/images/volumeslider-gradient.png" ) );
  328. const TQBitmap mask( temp.createHeuristicMask() );
  329. m_pixmapGradient = TQPixmap( m_pixmapInset.size() );
  330. KPixmapEffect::gradient( m_pixmapGradient, colorGroup().background(), colorGroup().highlight(),
  331. KPixmapEffect::HorizontalGradient );
  332. m_pixmapGradient.setMask( mask );
  333. }
  334. void
  335. Amarok::VolumeSlider::slotAnimTimer() //SLOT
  336. {
  337. if ( m_animEnter ) {
  338. m_animCount++;
  339. repaint( false );
  340. if ( m_animCount == ANIM_MAX - 1 )
  341. m_animTimer->stop();
  342. } else {
  343. m_animCount--;
  344. repaint( false );
  345. if ( m_animCount == 0 )
  346. m_animTimer->stop();
  347. }
  348. }
  349. void
  350. Amarok::VolumeSlider::mousePressEvent( TQMouseEvent *e )
  351. {
  352. if( e->button() != Qt::RightButton ) {
  353. Amarok::Slider::mousePressEvent( e );
  354. slideEvent( e );
  355. }
  356. }
  357. void
  358. Amarok::VolumeSlider::contextMenuEvent( TQContextMenuEvent *e )
  359. {
  360. TDEPopupMenu menu;
  361. menu.insertTitle( i18n( "Volume" ) );
  362. menu.insertItem( i18n( "100%" ), 100 );
  363. menu.insertItem( i18n( "80%" ), 80 );
  364. menu.insertItem( i18n( "60%" ), 60 );
  365. menu.insertItem( i18n( "40%" ), 40 );
  366. menu.insertItem( i18n( "20%" ), 20 );
  367. menu.insertItem( i18n( "0%" ), 0 );
  368. if( EngineController::hasEngineProperty( "HasEqualizer" ) )
  369. {
  370. menu.insertSeparator();
  371. menu.insertItem( SmallIconSet( "equalizer" ), i18n( "&Equalizer" ),
  372. kapp, TQT_SLOT( slotConfigEqualizer() ) );
  373. }
  374. const int n = menu.exec( mapToGlobal( e->pos() ) );
  375. if( n >= 0 )
  376. {
  377. TQSlider::setValue( n );
  378. emit sliderReleased( n );
  379. }
  380. }
  381. void
  382. Amarok::VolumeSlider::slideEvent( TQMouseEvent *e )
  383. {
  384. TQSlider::setValue( TQRangeControl::valueFromPosition( e->pos().x(), width()-2 ) );
  385. }
  386. void
  387. Amarok::VolumeSlider::wheelEvent( TQWheelEvent *e )
  388. {
  389. const uint step = e->delta() / Amarok::VOLUME_SENSITIVITY;
  390. TQSlider::setValue( TQSlider::value() + step );
  391. emit sliderReleased( value() );
  392. }
  393. void
  394. Amarok::VolumeSlider::paintEvent( TQPaintEvent * )
  395. {
  396. TQPixmap buf( size() );
  397. // Erase background
  398. if( parentWidget()->backgroundPixmap() )
  399. buf.fill( parentWidget(), pos() );
  400. else {
  401. buf.fill( colorGroup().background() );
  402. // TQPainter p( &buf );
  403. // p.fillRect( rect(), tqApp->palette().brush( TQPalette::Active, TQColorGroup::Background ) );
  404. }
  405. const int padding = 7;
  406. const int offset = int( double( ( width() - 2 * padding ) * value() ) / maxValue() );
  407. bitBlt( &buf, 0, 0, &m_pixmapGradient, 0, 0, offset + padding );
  408. bitBlt( &buf, 0, 0, &m_pixmapInset );
  409. bitBlt( &buf, offset - m_handlePixmaps[0].width() / 2 + padding, 0, &m_handlePixmaps[m_animCount] );
  410. // Draw percentage number
  411. TQPainter p( &buf );
  412. p.setPen( palette().color( TQPalette::Disabled, TQColorGroup::Text ).dark() );
  413. TQFont font;
  414. font.setPixelSize( 9 );
  415. p.setFont( font );
  416. const TQRect rect( 0, 0, 34, 15 );
  417. p.drawText( rect, TQt::AlignRight | TQt::AlignVCenter, TQString::number( value() ) + '%' );
  418. p.end();
  419. bitBlt( this, 0, 0, &buf );
  420. }
  421. void
  422. Amarok::VolumeSlider::hideEvent( TQHideEvent* )
  423. {
  424. setBackgroundMode( PaletteBackground ); // Required to prevent erasing
  425. }
  426. void
  427. Amarok::VolumeSlider::showEvent( TQShowEvent* )
  428. {
  429. // HACK To prevent ugly uninitialised background when the window is shown,
  430. // needed because we use NoBackground to prevent flickering while painting
  431. setBackgroundMode( NoBackground );
  432. }
  433. void
  434. Amarok::VolumeSlider::enterEvent( TQEvent* )
  435. {
  436. m_animEnter = true;
  437. m_animCount = 0;
  438. m_animTimer->start( ANIM_INTERVAL );
  439. }
  440. void
  441. Amarok::VolumeSlider::leaveEvent( TQEvent* )
  442. {
  443. // This can happen if you enter and leave the widget quickly
  444. if ( m_animCount == 0 )
  445. m_animCount = 1;
  446. m_animEnter = false;
  447. m_animTimer->start( ANIM_INTERVAL );
  448. }
  449. void
  450. Amarok::VolumeSlider::paletteChange( const TQPalette& )
  451. {
  452. generateGradient();
  453. }
  454. #include "sliderwidget.moc"