/*************************************************************************** amarokslider.cpp - description ------------------- begin : Dec 15 2003 copyright : (C) 2003 by Mark Kretschmann email : markey@web.de copyright : (C) 2005 by Gábor Lehel email : illissius@gmail.com ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include #include "amarok.h" #include "amarokconfig.h" #include "app.h" #include "debug.h" #include "enginecontroller.h" #include "sliderwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include Amarok::Slider::Slider( Qt::Orientation orientation, TQWidget *parent, uint max ) : TQSlider( orientation, parent ) , m_sliding( false ) , m_outside( false ) , m_prevValue( 0 ) { setRange( 0, max ); } void Amarok::Slider::wheelEvent( TQWheelEvent *e ) { if( orientation() == Qt::Vertical ) { // Will be handled by the parent widget e->ignore(); return; } // Position Slider (horizontal) int step = e->delta() * 1500 / 18; int nval = TQSlider::value() + step; nval = TQMAX(nval, minValue()); nval = TQMIN(nval, maxValue()); TQSlider::setValue( nval ); emit sliderReleased( value() ); } void Amarok::Slider::mouseMoveEvent( TQMouseEvent *e ) { if ( m_sliding ) { //feels better, but using set value of 20 is bad of course TQRect rect( -20, -20, width()+40, height()+40 ); if ( orientation() == Qt::Horizontal && !rect.contains( e->pos() ) ) { if ( !m_outside ) TQSlider::setValue( m_prevValue ); m_outside = true; } else { m_outside = false; slideEvent( e ); emit sliderMoved( value() ); } } else TQSlider::mouseMoveEvent( e ); } void Amarok::Slider::slideEvent( TQMouseEvent *e ) { TQSlider::setValue( orientation() == Qt::Horizontal ? ((TQApplication::reverseLayout()) ? TQRangeControl::valueFromPosition( width() - (e->pos().x() - sliderRect().width()/2), width() + sliderRect().width() ) : TQRangeControl::valueFromPosition( e->pos().x() - sliderRect().width()/2, width() - sliderRect().width() ) ) : TQRangeControl::valueFromPosition( e->pos().y() - sliderRect().height()/2, height() - sliderRect().height() ) ); } void Amarok::Slider::mousePressEvent( TQMouseEvent *e ) { m_sliding = true; m_prevValue = TQSlider::value(); if ( !sliderRect().contains( e->pos() ) ) mouseMoveEvent( e ); } void Amarok::Slider::mouseReleaseEvent( TQMouseEvent* ) { if( !m_outside && TQSlider::value() != m_prevValue ) emit sliderReleased( value() ); m_sliding = false; m_outside = false; } void Amarok::Slider::setValue( int newValue ) { //don't adjust the slider while the user is dragging it! if ( !m_sliding || m_outside ) TQSlider::setValue( adjustValue( newValue ) ); else m_prevValue = newValue; } ////////////////////////////////////////////////////////////////////////////////////////// /// CLASS PrettySlider ////////////////////////////////////////////////////////////////////////////////////////// #define THICKNESS 7 #define MARGIN 3 Amarok::PrettySlider::PrettySlider( Qt::Orientation orientation, SliderMode mode, TQWidget *parent, uint max ) : Amarok::Slider( orientation, parent, max ) , m_mode( mode ) , m_showingMoodbar( false ) { if( m_mode == Pretty) { setWFlags( TQt::WNoAutoErase ); setFocusPolicy( TQ_NoFocus ); } // We only have to connect this *once*, since our MetaBundle // doesn't get destroyed until we do. connect( &m_bundle.moodbar(), TQT_SIGNAL( jobEvent( int ) ), TQT_SLOT( moodbarJobEvent( int ) ) ); // We want to know if we should reset our moodbar data connect( App::instance(), TQT_SIGNAL( moodbarPrefs( bool, bool, int, bool ) ), TQT_SLOT( slotMoodbarPrefs( bool, bool, int, bool ) ) ); } void Amarok::PrettySlider::mousePressEvent( TQMouseEvent *e ) { Amarok::Slider::mousePressEvent( e ); slideEvent( e ); } void Amarok::PrettySlider::slideEvent( TQMouseEvent *e ) { if( m_mode == Pretty || m_showingMoodbar ) TQSlider::setValue( orientation() == Qt::Horizontal ? TQRangeControl::valueFromPosition( e->pos().x(), width()-2 ) : TQRangeControl::valueFromPosition( e->pos().y(), height()-2 ) ); else Amarok::Slider::slideEvent( e ); } namespace Amarok { namespace ColorScheme { extern TQColor Background; extern TQColor Foreground; } } void Amarok::PrettySlider::paintEvent( TQPaintEvent *e ) { const int w = orientation() == Qt::Horizontal ? width() : height(); const int pos = int( double( w-2 ) / maxValue() * Slider::value() ); int h = THICKNESS; m_showingMoodbar = ( !m_bundle.url().isEmpty() && m_bundle.moodbar().dataExists() && AmarokConfig::showMoodbar() ); TQPixmap mood; if( m_showingMoodbar ) { if( m_mode == Normal ) h = (orientation() == Qt::Vertical ? width() : height()) - 2*MARGIN; mood = m_bundle.moodbar().draw( w, h ); } // If we're a Normal PrettySlider and we have no moodbar, // emulate the behavior of Slider else if( m_mode == Normal ) { Amarok::Slider::paintEvent( e ); return; } TQPixmap buf( size() ); TQPainter p( &buf, this ); buf.fill( parentWidget()->backgroundColor() ); if ( orientation() == Qt::Vertical ) { p.translate( 0, height()-1 ); p.rotate( -90 ); //90 degrees clockwise } if( !m_showingMoodbar ) { p.translate( 0, MARGIN ); p.setPen( Amarok::ColorScheme::Foreground ); p.fillRect( 0, 0, pos, h, TQColor( Amarok::ColorScheme::Background ) ); p.drawRect( 0, 0, w, h ); p.translate( 0, -MARGIN ); } else { p.translate( 0, MARGIN ); p.drawPixmap( 0, 0, mood ); p.setPen( Amarok::ColorScheme::Foreground ); p.drawRect( 0, 0, w, h ); p.translate( 0, -MARGIN ); // Larger triangle for the moodbar } // if( m_mode == Pretty ) { TQPointArray pa( 3 ); pa.setPoint( 0, pos - 3, 1 ); pa.setPoint( 1, pos + 3, 1 ); pa.setPoint( 2, pos, 5 ); p.setBrush( paletteForegroundColor() ); p.drawConvexPolygon( pa ); } else if( m_mode == Normal ) { TQPointArray pa( 3 ); pa.setPoint( 0, pos - 5, 1 ); pa.setPoint( 1, pos + 5, 1 ); pa.setPoint( 2, pos, 9 ); p.setBrush( paletteForegroundColor() ); p.drawConvexPolygon( pa ); } // p.end(); bitBlt( this, 0, 0, &buf ); } // This gets called when the moodbar job starts or finishes void Amarok::PrettySlider::moodbarJobEvent( int newState ) { if( newState == Moodbar::JobStateSucceeded ) { debug() << "moodbarJobEvent: new moodbar data" << endl; update(); } } // This gets called when the user presses "Ok" or "Apply" in the // config dialog. Reload our moodbar data, in case it was // permanently disabled before because the moodbar was disabled. void Amarok::PrettySlider::slotMoodbarPrefs( bool show, bool moodier, int alter, bool withMusic ) { (void) moodier; (void) alter; (void) withMusic; if( show ) { m_bundle.moodbar().reset(); if( !m_bundle.moodbar().dataExists() ) m_bundle.moodbar().load(); update(); } } // This is called when the track changes / stops / starts void Amarok::PrettySlider::newBundle( const MetaBundle &bundle ) { m_bundle = bundle; m_bundle.detach(); // This is the easiest way to tell if the bundle refers // to a real track, or if we're STOP'd. if( m_bundle.url().isEmpty() ) return; // It's a real track; get the moodbar data if it's not there if( !m_bundle.moodbar().dataExists() ) m_bundle.moodbar().load(); else update(); } #if 0 /** these functions aren't required in our fixed size world, but they may become useful one day **/ TQSize Amarok::PrettySlider::tqminimumSizeHint() const { return tqsizeHint(); } TQSize Amarok::PrettySlider::tqsizeHint() const { constPolish(); return (orientation() == Horizontal ? TQSize( maxValue(), THICKNESS + MARGIN ) : TQSize( THICKNESS + MARGIN, maxValue() )).expandedTo( TQApplit ication::globalStrut() ); } #endif ////////////////////////////////////////////////////////////////////////////////////////// /// CLASS VolumeSlider ////////////////////////////////////////////////////////////////////////////////////////// Amarok::VolumeSlider::VolumeSlider( TQWidget *parent, uint max ) : Amarok::Slider( Qt::Horizontal, parent, max ) , m_animCount( 0 ) , m_animTimer( new TQTimer( this ) ) , m_pixmapInset( TQPixmap( locate( "data","amarok/images/volumeslider-inset.png" ) ) ) { setWFlags( getWFlags() | WNoAutoErase ); setFocusPolicy( TQ_NoFocus ); if (TQPaintDevice::x11AppDepth() == 32) m_pixmapInset.convertFromImage(KImageEffect::convertToPremultipliedAlpha( m_pixmapInset.convertToImage() )); // BEGIN Calculate handle animation pixmaps for mouse-over effect TQImage pixmapHandle ( locate( "data","amarok/images/volumeslider-handle.png" ) ); if (TQPaintDevice::x11AppDepth() == 32) pixmapHandle = KImageEffect::convertToPremultipliedAlpha( pixmapHandle ); TQImage pixmapHandleGlow( locate( "data","amarok/images/volumeslider-handle_glow.png" ) ); float opacity = 0.0; const float step = 1.0 / ANIM_MAX; TQImage dst; for ( int i = 0; i < ANIM_MAX; ++i ) { dst = pixmapHandle; KImageEffect::blend( pixmapHandleGlow, dst, opacity ); if (TQPaintDevice::x11AppDepth() == 32) dst = KImageEffect::convertToPremultipliedAlpha( dst ); m_handlePixmaps.append( TQPixmap( dst ) ); opacity += step; } // END generateGradient(); setMinimumWidth( m_pixmapInset.width() ); setMinimumHeight( m_pixmapInset.height() ); connect( m_animTimer, TQT_SIGNAL( timeout() ), this, TQT_SLOT( slotAnimTimer() ) ); } void Amarok::VolumeSlider::generateGradient() { //TQImage temp( locate( "data","amarok/images/volumeslider-gradient.png" ) ); //KIconEffect::colorize( temp, tqcolorGroup().highlight(), 1.0 ); const TQPixmap temp( locate( "data","amarok/images/volumeslider-gradient.png" ) ); const TQBitmap mask( temp.createHeuristicMask() ); m_pixmapGradient = TQPixmap( m_pixmapInset.size() ); KPixmapEffect::gradient( m_pixmapGradient, tqcolorGroup().background(), tqcolorGroup().highlight(), KPixmapEffect::HorizontalGradient ); m_pixmapGradient.setMask( mask ); } void Amarok::VolumeSlider::slotAnimTimer() //SLOT { if ( m_animEnter ) { m_animCount++; tqrepaint( false ); if ( m_animCount == ANIM_MAX - 1 ) m_animTimer->stop(); } else { m_animCount--; tqrepaint( false ); if ( m_animCount == 0 ) m_animTimer->stop(); } } void Amarok::VolumeSlider::mousePressEvent( TQMouseEvent *e ) { if( e->button() != Qt::RightButton ) { Amarok::Slider::mousePressEvent( e ); slideEvent( e ); } } void Amarok::VolumeSlider::contextMenuEvent( TQContextMenuEvent *e ) { KPopupMenu menu; menu.insertTitle( i18n( "Volume" ) ); menu.insertItem( i18n( "100%" ), 100 ); menu.insertItem( i18n( "80%" ), 80 ); menu.insertItem( i18n( "60%" ), 60 ); menu.insertItem( i18n( "40%" ), 40 ); menu.insertItem( i18n( "20%" ), 20 ); menu.insertItem( i18n( "0%" ), 0 ); if( EngineController::hasEngineProperty( "HasEqualizer" ) ) { menu.insertSeparator(); menu.insertItem( SmallIconSet( "equalizer" ), i18n( "&Equalizer" ), kapp, TQT_SLOT( slotConfigEqualizer() ) ); } const int n = menu.exec( mapToGlobal( e->pos() ) ); if( n >= 0 ) { TQSlider::setValue( n ); emit sliderReleased( n ); } } void Amarok::VolumeSlider::slideEvent( TQMouseEvent *e ) { TQSlider::setValue( TQRangeControl::valueFromPosition( e->pos().x(), width()-2 ) ); } void Amarok::VolumeSlider::wheelEvent( TQWheelEvent *e ) { const uint step = e->delta() / Amarok::VOLUME_SENSITIVITY; TQSlider::setValue( TQSlider::value() + step ); emit sliderReleased( value() ); } void Amarok::VolumeSlider::paintEvent( TQPaintEvent * ) { TQPixmap buf( size() ); // Erase background if( parentWidget()->backgroundPixmap() ) buf.fill( parentWidget(), pos() ); else { buf.fill( tqcolorGroup().background() ); // TQPainter p( &buf ); // p.fillRect( rect(), tqApp->palette().brush( TQPalette::Active, TQColorGroup::Background ) ); } const int padding = 7; const int offset = int( double( ( width() - 2 * padding ) * value() ) / maxValue() ); bitBlt( &buf, 0, 0, &m_pixmapGradient, 0, 0, offset + padding ); bitBlt( &buf, 0, 0, &m_pixmapInset ); bitBlt( &buf, offset - m_handlePixmaps[0].width() / 2 + padding, 0, &m_handlePixmaps[m_animCount] ); // Draw percentage number TQPainter p( &buf ); p.setPen( palette().color( TQPalette::Disabled, TQColorGroup::Text ).dark() ); TQFont font; font.setPixelSize( 9 ); p.setFont( font ); const TQRect rect( 0, 0, 34, 15 ); p.drawText( rect, TQt::AlignRight | TQt::AlignVCenter, TQString::number( value() ) + '%' ); p.end(); bitBlt( this, 0, 0, &buf ); } void Amarok::VolumeSlider::hideEvent( TQHideEvent* ) { setBackgroundMode( PaletteBackground ); // Required to prevent erasing } void Amarok::VolumeSlider::showEvent( TQShowEvent* ) { // HACK To prevent ugly uninitialised background when the window is shown, // needed because we use NoBackground to prevent flickering while painting setBackgroundMode( NoBackground ); } void Amarok::VolumeSlider::enterEvent( TQEvent* ) { m_animEnter = true; m_animCount = 0; m_animTimer->start( ANIM_INTERVAL ); } void Amarok::VolumeSlider::leaveEvent( TQEvent* ) { // This can happen if you enter and leave the widget quickly if ( m_animCount == 0 ) m_animCount = 1; m_animEnter = false; m_animTimer->start( ANIM_INTERVAL ); } void Amarok::VolumeSlider::paletteChange( const TQPalette& ) { generateGradient(); } #include "sliderwidget.moc"