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
15 KiB
546 lines
15 KiB
/***************************************************************************
|
|
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 <config.h>
|
|
|
|
#include "amarok.h"
|
|
#include "amarokconfig.h"
|
|
#include "app.h"
|
|
#include "debug.h"
|
|
#include "enginecontroller.h"
|
|
#include "sliderwidget.h"
|
|
|
|
#include <tqapplication.h>
|
|
#include <tqbitmap.h>
|
|
#include <tqbrush.h>
|
|
#include <tqimage.h>
|
|
#include <tqpainter.h>
|
|
#include <tqsize.h>
|
|
#include <tqtimer.h>
|
|
|
|
#include <kiconloader.h>
|
|
#include <kimageeffect.h>
|
|
#include <tdelocale.h>
|
|
#include <kpixmapeffect.h>
|
|
#include <tdepopupmenu.h>
|
|
#include <kstandarddirs.h>
|
|
|
|
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( TQWidget::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
|
|
}
|
|
|
|
//<Triangle Marker>
|
|
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 );
|
|
}
|
|
//</Triangle Marker>
|
|
|
|
|
|
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::minimumSizeHint() const
|
|
{
|
|
return sizeHint();
|
|
}
|
|
|
|
TQSize
|
|
Amarok::PrettySlider::sizeHint() 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( TQWidget::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" ) );
|
|
//TDEIconEffect::colorize( temp, colorGroup().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, colorGroup().background(), colorGroup().highlight(),
|
|
KPixmapEffect::HorizontalGradient );
|
|
m_pixmapGradient.setMask( mask );
|
|
}
|
|
|
|
void
|
|
Amarok::VolumeSlider::slotAnimTimer() //SLOT
|
|
{
|
|
if ( m_animEnter ) {
|
|
m_animCount++;
|
|
repaint( false );
|
|
if ( m_animCount == ANIM_MAX - 1 )
|
|
m_animTimer->stop();
|
|
} else {
|
|
m_animCount--;
|
|
repaint( 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 )
|
|
{
|
|
TDEPopupMenu 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( colorGroup().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"
|