]> git.sesse.net Git - vlc/blob - modules/gui/qt4/util/input_slider.cpp
Qt: SoundSlider: create gradients according to sound max value
[vlc] / modules / gui / qt4 / util / input_slider.cpp
1 /*****************************************************************************
2  * input_slider.cpp : VolumeSlider and SeekSlider
3  ****************************************************************************
4  * Copyright (C) 2006-2011 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Clément Stenac <zorglub@videolan.org>
8  *          Jean-Baptiste Kempf <jb@videolan.org>
9  *          Ludovic Fauvet <etix@videolan.org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include "qt4.hpp"
31
32 #include "util/input_slider.hpp"
33 #include "adapters/seekpoints.hpp"
34 #include <vlc_aout_intf.h>
35
36 #include <QPaintEvent>
37 #include <QPainter>
38 #include <QBitmap>
39 #include <QPainter>
40 #include <QStyleOptionSlider>
41 #include <QLinearGradient>
42 #include <QTimer>
43 #include <QRadialGradient>
44 #include <QLinearGradient>
45 #include <QSize>
46 #include <QPalette>
47 #include <QColor>
48 #include <QPoint>
49 #include <QPropertyAnimation>
50 #include <QApplication>
51
52 #define MINIMUM 0
53 #define MAXIMUM 1000
54 #define CHAPTERSSPOTSIZE 3
55 #define FADEDURATION 300
56 #define FADEOUTDELAY 2000
57
58 SeekSlider::SeekSlider( Qt::Orientation q, QWidget *_parent, bool _static )
59           : QSlider( q, _parent ), b_classic( _static )
60 {
61     isSliding = false;
62     f_buffering = 1.0;
63     mHandleOpacity = 1.0;
64     chapters = NULL;
65
66     // prepare some static colors
67     QPalette p = palette();
68     QColor background = p.color( QPalette::Active, QPalette::Background );
69     tickpointForeground = p.color( QPalette::Active, QPalette::WindowText );
70     tickpointForeground.setHsv( tickpointForeground.hue(),
71             ( background.saturation() + tickpointForeground.saturation() ) / 2,
72             ( background.value() + tickpointForeground.value() ) / 2 );
73
74     // set the background color and gradient
75     QColor backgroundBase( p.window().color() );
76     backgroundGradient.setColorAt( 0.0, backgroundBase.darker( 140 ) );
77     backgroundGradient.setColorAt( 1.0, backgroundBase );
78
79     // set the foreground color and gradient
80     QColor foregroundBase( 50, 156, 255 );
81     foregroundGradient.setColorAt( 0.0,  foregroundBase );
82     foregroundGradient.setColorAt( 1.0,  foregroundBase.darker( 140 ) );
83
84     // prepare the handle's gradient
85     handleGradient.setColorAt( 0.0, p.window().color().lighter( 120 ) );
86     handleGradient.setColorAt( 0.9, p.window().color().darker( 120 ) );
87
88     // prepare the handle's shadow gradient
89     QColor shadowBase = p.shadow().color();
90     if( shadowBase.lightness() > 100 )
91         shadowBase = QColor( 60, 60, 60 ); // Palette's shadow is too bright
92     shadowDark = shadowBase.darker( 150 );
93     shadowLight = shadowBase.lighter( 180 );
94     shadowLight.setAlpha( 50 );
95
96     /* Timer used to fire intermediate updatePos() when sliding */
97     seekLimitTimer = new QTimer( this );
98     seekLimitTimer->setSingleShot( true );
99
100     /* Tooltip bubble */
101     mTimeTooltip = new TimeTooltip( this );
102     mTimeTooltip->setMouseTracking( true );
103
104     /* Properties */
105     setRange( MINIMUM, MAXIMUM );
106     setSingleStep( 2 );
107     setPageStep( 10 );
108     setMouseTracking( true );
109     setTracking( true );
110     setFocusPolicy( Qt::NoFocus );
111
112     /* Init to 0 */
113     setPosition( -1.0, 0, 0 );
114     secstotimestr( psz_length, 0 );
115
116     animHandle = new QPropertyAnimation( this, "handleOpacity", this );
117     animHandle->setDuration( FADEDURATION );
118     animHandle->setStartValue( 0.0 );
119     animHandle->setEndValue( 1.0 );
120
121     hideHandleTimer = new QTimer( this );
122     hideHandleTimer->setSingleShot( true );
123     hideHandleTimer->setInterval( FADEOUTDELAY );
124
125     CONNECT( this, sliderMoved( int ), this, startSeekTimer() );
126     CONNECT( seekLimitTimer, timeout(), this, updatePos() );
127     CONNECT( hideHandleTimer, timeout(), this, hideHandle() );
128     mTimeTooltip->installEventFilter( this );
129 }
130
131 SeekSlider::~SeekSlider()
132 {
133     delete chapters;
134 }
135
136 /***
137  * \brief Sets the chapters seekpoints adapter
138  *
139  * \params SeekPoints initilized with current intf thread
140 ***/
141 void SeekSlider::setChapters( SeekPoints *chapters_ )
142 {
143     delete chapters;
144     chapters = chapters_;
145     chapters->setParent( this );
146 }
147
148 /***
149  * \brief Main public method, superseeding setValue. Disabling the slider when neeeded
150  *
151  * \param pos Position, between 0 and 1. -1 disables the slider
152  * \param time Elapsed time. Unused
153  * \param legnth Duration time.
154  ***/
155 void SeekSlider::setPosition( float pos, int64_t time, int length )
156 {
157     VLC_UNUSED(time);
158     if( pos == -1.0 )
159     {
160         setEnabled( false );
161         isSliding = false;
162     }
163     else
164         setEnabled( true );
165
166     if( !isSliding )
167         setValue( (int)( pos * 1000.0 ) );
168
169     inputLength = length;
170 }
171
172 void SeekSlider::startSeekTimer()
173 {
174     /* Only fire one update, when sliding, every 150ms */
175     if( isSliding && !seekLimitTimer->isActive() )
176         seekLimitTimer->start( 150 );
177 }
178
179 void SeekSlider::updatePos()
180 {
181     float f_pos = (float)( value() ) / 1000.0;
182     emit sliderDragged( f_pos ); /* Send new position to VLC's core */
183 }
184
185 void SeekSlider::updateBuffering( float f_buffering_ )
186 {
187     f_buffering = f_buffering_;
188     repaint();
189 }
190
191 void SeekSlider::mouseReleaseEvent( QMouseEvent *event )
192 {
193     event->accept();
194     isSliding = false;
195     bool b_seekPending = seekLimitTimer->isActive();
196     seekLimitTimer->stop(); /* We're not sliding anymore: only last seek on release */
197     if ( isJumping )
198     {
199         isJumping = false;
200         return;
201     }
202     QSlider::mouseReleaseEvent( event );
203     if( b_seekPending )
204         updatePos();
205 }
206
207 void SeekSlider::mousePressEvent( QMouseEvent* event )
208 {
209     /* Right-click */
210     if( event->button() != Qt::LeftButton &&
211         event->button() != Qt::MidButton )
212     {
213         QSlider::mousePressEvent( event );
214         return;
215     }
216
217     isJumping = false;
218     /* handle chapter clicks */
219     int i_width = size().width();
220     if ( chapters && inputLength && i_width)
221     {
222         if ( orientation() == Qt::Horizontal ) /* TODO: vertical */
223         {
224              /* only on chapters zone */
225             if ( event->y() < CHAPTERSSPOTSIZE ||
226                  event->y() > ( size().height() - CHAPTERSSPOTSIZE ) )
227             {
228                 QList<SeekPoint> points = chapters->getPoints();
229                 int i_selected = -1;
230                 bool b_startsnonzero = false; /* as we always starts at 1 */
231                 if ( points.count() > 0 ) /* do we need an extra offset ? */
232                     b_startsnonzero = ( points.at(0).time > 0 );
233                 int i_min_diff = i_width + 1;
234                 for( int i = 0 ; i < points.count() ; i++ )
235                 {
236                     int x = points.at(i).time / 1000000.0 / inputLength * i_width;
237                     int diff_x = abs( x - event->x() );
238                     if ( diff_x < i_min_diff )
239                     {
240                         i_min_diff = diff_x;
241                         i_selected = i + ( ( b_startsnonzero )? 1 : 0 );
242                     } else break;
243                 }
244                 if ( i_selected && i_min_diff < 4 ) // max 4px around mark
245                 {
246                     chapters->jumpTo( i_selected );
247                     event->accept();
248                     isJumping = true;
249                     return;
250                 }
251             }
252         }
253     }
254
255     isSliding = true ;
256     setValue( QStyle::sliderValueFromPosition( MINIMUM, MAXIMUM, event->x(), width(), false ) );
257     emit sliderMoved( value() );
258     event->accept();
259 }
260
261 void SeekSlider::mouseMoveEvent( QMouseEvent *event )
262 {
263     if( isSliding )
264     {
265         setValue( QStyle::sliderValueFromPosition( MINIMUM, MAXIMUM, event->x(), width(), false) );
266         emit sliderMoved( value() );
267     }
268
269     /* Tooltip */
270     if ( inputLength > 0 )
271     {
272         int posX = qMax( rect().left(), qMin( rect().right(), event->x() ) );
273
274         QString chapterLabel;
275
276         if ( orientation() == Qt::Horizontal ) /* TODO: vertical */
277         {
278                 QList<SeekPoint> points = chapters->getPoints();
279                 int i_selected = -1;
280                 bool b_startsnonzero = false;
281                 if ( points.count() > 0 )
282                     b_startsnonzero = ( points.at(0).time > 0 );
283                 for( int i = 0 ; i < points.count() ; i++ )
284                 {
285                     int x = points.at(i).time / 1000000.0 / inputLength * size().width();
286                     if ( event->x() >= x )
287                         i_selected = i + ( ( b_startsnonzero )? 1 : 0 );
288                 }
289                 if ( i_selected >= 0 && i_selected < points.size() )
290                     chapterLabel = points.at( i_selected ).name;
291         }
292
293         QPoint target( event->globalX() - ( event->x() - posX ),
294                   QWidget::mapToGlobal( pos() ).y() );
295         secstotimestr( psz_length, ( posX * inputLength ) / size().width() );
296         mTimeTooltip->setTip( target, psz_length, chapterLabel );
297     }
298     event->accept();
299 }
300
301 void SeekSlider::wheelEvent( QWheelEvent *event )
302 {
303     /* Don't do anything if we are for somehow reason sliding */
304     if( !isSliding )
305     {
306         setValue( value() + event->delta() / 12 ); /* 12 = 8 * 15 / 10
307          Since delta is in 1/8 of ° and mouse have steps of 15 °
308          and that our slider is in 0.1% and we want one step to be a 1%
309          increment of position */
310         emit sliderDragged( value() / 1000.0 );
311     }
312     event->accept();
313 }
314
315 void SeekSlider::enterEvent( QEvent * )
316 {
317     /* Cancel the fade-out timer */
318     hideHandleTimer->stop();
319     /* Only start the fade-in if needed */
320     if( animHandle->direction() != QAbstractAnimation::Forward )
321     {
322         /* If pause is called while not running Qt will complain */
323         if( animHandle->state() == QAbstractAnimation::Running )
324             animHandle->pause();
325         animHandle->setDirection( QAbstractAnimation::Forward );
326         animHandle->start();
327     }
328     /* Don't show the tooltip if the slider is disabled or a menu is open */
329     if( isEnabled() && inputLength > 0 && !qApp->activePopupWidget() )
330         mTimeTooltip->show();
331 }
332
333 void SeekSlider::leaveEvent( QEvent * )
334 {
335     hideHandleTimer->start();
336     /* Hide the tooltip
337        - if the mouse leave the slider rect (Note: it can still be
338          over the tooltip!)
339        - if another window is on the way of the cursor */
340     if( !rect().contains( mapFromGlobal( QCursor::pos() ) ) ||
341       ( !isActiveWindow() && !mTimeTooltip->isActiveWindow() ) )
342     {
343         mTimeTooltip->hide();
344     }
345 }
346
347 void SeekSlider::hideEvent( QHideEvent * )
348 {
349     mTimeTooltip->hide();
350 }
351
352 bool SeekSlider::eventFilter( QObject *obj, QEvent *event )
353 {
354     if( obj == mTimeTooltip )
355     {
356         if( event->type() == QEvent::Leave ||
357             event->type() == QEvent::MouseMove )
358         {
359             QMouseEvent *e = static_cast<QMouseEvent*>( event );
360             if( !rect().contains( mapFromGlobal( e->globalPos() ) ) )
361                 mTimeTooltip->hide();
362         }
363         return false;
364     }
365     else
366         return QSlider::eventFilter( obj, event );
367 }
368
369 QSize SeekSlider::sizeHint() const
370 {
371     return ( orientation() == Qt::Horizontal ) ? QSize( 100, 18 )
372                                                : QSize( 18, 100 );
373 }
374
375 QSize SeekSlider::handleSize() const
376 {
377     const int size = ( orientation() == Qt::Horizontal ? height() : width() );
378     return QSize( size, size );
379 }
380
381 void SeekSlider::paintEvent( QPaintEvent *event )
382 {
383     if( b_classic )
384         return QSlider::paintEvent( event );
385
386     QStyleOptionSlider option;
387     initStyleOption( &option );
388
389     /* */
390     QPainter painter( this );
391     painter.setRenderHints( QPainter::Antialiasing );
392
393     // draw bar
394     const int barCorner = 3;
395     qreal sliderPos     = -1;
396     int range           = MAXIMUM;
397     QRect barRect       = rect();
398
399     // adjust positions based on the current orientation
400     if ( option.sliderPosition != 0 )
401     {
402         switch ( orientation() )
403         {
404             case Qt::Horizontal:
405                 sliderPos = ( ( (qreal)width() ) / (qreal)range )
406                         * (qreal)option.sliderPosition;
407                 break;
408             case Qt::Vertical:
409                 sliderPos = ( ( (qreal)height() ) / (qreal)range )
410                         * (qreal)option.sliderPosition;
411                 break;
412         }
413     }
414
415     switch ( orientation() )
416     {
417         case Qt::Horizontal:
418             barRect.setHeight( height() /2 );
419             break;
420         case Qt::Vertical:
421             barRect.setWidth( width() /2 );
422             break;
423     }
424
425     barRect.moveCenter( rect().center() );
426
427     QSize hSize( handleSize() - QSize( 6, 6 ) );
428     QSize sSize( handleSize() - QSize( 2, 2 ) );
429
430     if ( gradientsTargetSize != size() )
431     {
432         /* Need to fix gradients */
433         gradientsTargetSize = size();
434         backgroundGradient.setFinalStop( 0, height() );
435         foregroundGradient.setFinalStop( 0, height() );
436         handleGradient.setFinalStop( 0, hSize.height() );
437     }
438
439     // draw a slight 3d effect on the bottom
440     painter.setPen( QColor( 230, 230, 230 ) );
441     painter.setBrush( Qt::NoBrush );
442     painter.drawRoundedRect( barRect.adjusted( 0, 2, 0, 0 ), barCorner, barCorner );
443
444     // draw background
445     painter.setPen( Qt::NoPen );
446     painter.setBrush( backgroundGradient );
447     painter.drawRoundedRect( barRect, barCorner, barCorner );
448
449     // adjusted foreground rectangle
450     QRect valueRect = barRect.adjusted( 1, 1, -1, 0 );
451
452     switch ( orientation() )
453     {
454         case Qt::Horizontal:
455             valueRect.setWidth( qMin( width(), int( sliderPos ) ) );
456             break;
457         case Qt::Vertical:
458             valueRect.setHeight( qMin( height(), int( sliderPos ) ) );
459             valueRect.moveBottom( rect().bottom() );
460             break;
461     }
462
463     if ( option.sliderPosition > minimum() && option.sliderPosition <= maximum() )
464     {
465         // draw foreground
466         painter.setPen( Qt::NoPen );
467         painter.setBrush( foregroundGradient );
468         painter.drawRoundedRect( valueRect, barCorner, barCorner );
469     }
470
471     // draw buffering overlay
472     if ( f_buffering < 1.0 )
473     {
474         QRect innerRect = barRect.adjusted( 1, 1,
475                             barRect.width() * ( -1.0 + f_buffering ) - 1, 0 );
476         QColor overlayColor = QColor( "Orange" );
477         overlayColor.setAlpha( 128 );
478         painter.setBrush( overlayColor );
479         painter.drawRoundedRect( innerRect, barCorner, barCorner );
480     }
481
482     if ( option.state & QStyle::State_MouseOver || isAnimationRunning() )
483     {
484         /* draw chapters tickpoints */
485         if ( chapters && inputLength && size().width() )
486         {
487             if ( orientation() == Qt::Horizontal ) /* TODO: vertical */
488             {
489                 QList<SeekPoint> points = chapters->getPoints();
490                 painter.setPen( tickpointForeground );
491                 painter.setBrush( Qt::NoBrush );
492                 foreach( SeekPoint point, points )
493                 {
494                     int x = point.time / 1000000.0 / inputLength * size().width();
495                     painter.drawLine( x, height(), x, height() - CHAPTERSSPOTSIZE );
496                 }
497             }
498         }
499
500         // draw handle
501         if ( sliderPos != -1 )
502         {
503             const int margin = 0;
504             QPoint pos;
505
506             switch ( orientation() )
507             {
508                 case Qt::Horizontal:
509                     pos = QPoint( sliderPos - ( hSize.width() / 2 ), 2 );
510                     pos.rx() = qMax( margin, pos.x() );
511                     pos.rx() = qMin( width() - hSize.width() - margin, pos.x() );
512                     break;
513                 case Qt::Vertical:
514                     pos = QPoint( 2, height() - ( sliderPos + ( hSize.height() / 2 ) ) );
515                     pos.ry() = qMax( margin, pos.y() );
516                     pos.ry() = qMin( height() - hSize.height() - margin, pos.y() );
517                     break;
518             }
519
520             QPoint shadowPos( pos - QPoint( 2, 2 ) );
521
522             /* FIXME: precompute gradient. Anim Compatible ? */
523             QRadialGradient shadowGradient( shadowPos.x() + ( sSize.width() / 2 ),
524                                             shadowPos.y() + ( sSize.height() / 2 ),
525                                             qMax( sSize.width(), sSize.height() ) / 2 );
526             shadowGradient.setColorAt( 0.4, shadowDark );
527             shadowGradient.setColorAt( 1.0, shadowLight );
528
529             painter.setPen( Qt::NoPen );
530             painter.setOpacity( mHandleOpacity );
531
532             // draw the handle's shadow
533             painter.setBrush( shadowGradient );
534             painter.drawEllipse( shadowPos.x(), shadowPos.y() + 1, sSize.width(), sSize.height() );
535
536             // finally draw the handle
537             painter.setBrush( handleGradient );
538             painter.drawEllipse( pos.x(), pos.y(), hSize.width(), hSize.height() );
539         }
540     }
541 }
542
543 qreal SeekSlider::handleOpacity() const
544 {
545     return mHandleOpacity;
546 }
547
548 void SeekSlider::setHandleOpacity(qreal opacity)
549 {
550     mHandleOpacity = opacity;
551     /* Request a new paintevent */
552     update();
553 }
554
555 void SeekSlider::hideHandle()
556 {
557     /* If pause is called while not running Qt will complain */
558     if( animHandle->state() == QAbstractAnimation::Running )
559         animHandle->pause();
560     /* Play the animation backward */
561     animHandle->setDirection( QAbstractAnimation::Backward );
562     animHandle->start();
563 }
564
565 bool SeekSlider::isAnimationRunning() const
566 {
567     return animHandle->state() == QAbstractAnimation::Running
568             || hideHandleTimer->isActive();
569 }
570
571 /* This work is derived from Amarok's work under GPLv2+
572     - Mark Kretschmann
573     - Gábor Lehel
574    */
575 #define WLENGTH   80 // px
576 #define WHEIGHT   22  // px
577 #define SOUNDMIN  0   // %
578 #define SOUNDMAX  200 // % OR 400 ?
579
580 SoundSlider::SoundSlider( QWidget *_parent, int _i_step, bool b_hard,
581                           char *psz_colors )
582                         : QAbstractSlider( _parent )
583 {
584     f_step = ( _i_step * 100 ) / AOUT_VOLUME_MAX ;
585     setRange( SOUNDMIN, b_hard ? (2 * SOUNDMAX) : SOUNDMAX  );
586     setMouseTracking( true );
587     isSliding = false;
588     b_mouseOutside = true;
589     b_isMuted = false;
590
591     pixOutside = QPixmap( ":/toolbar/volslide-outside" );
592
593     const QPixmap temp( ":/toolbar/volslide-inside" );
594     const QBitmap mask( temp.createHeuristicMask() );
595
596     setFixedSize( pixOutside.size() );
597
598     pixGradient = QPixmap( mask.size() );
599     pixGradient2 = QPixmap( mask.size() );
600
601     /* Gradient building from the preferences */
602     QLinearGradient gradient( paddingL, 2, WLENGTH + paddingL , 2 );
603     QLinearGradient gradient2( paddingL, 2, WLENGTH + paddingL , 2 );
604
605     QStringList colorList = qfu( psz_colors ).split( ";" );
606     free( psz_colors );
607
608     /* Fill with 255 if the list is too short */
609     if( colorList.count() < 12 )
610         for( int i = colorList.count(); i < 12; i++)
611             colorList.append( "255" );
612
613     background = palette().color( QPalette::Active, QPalette::Background );
614     foreground = palette().color( QPalette::Active, QPalette::WindowText );
615     foreground.setHsv( foreground.hue(),
616                     ( background.saturation() + foreground.saturation() ) / 2,
617                     ( background.value() + foreground.value() ) / 2 );
618
619     textfont.setPixelSize( 9 );
620     textrect.setRect( 0, 0, 34, 15 );
621
622     /* Regular colors */
623 #define c(i) colorList.at(i).toInt()
624 #define add_color(gradient, range, c1, c2, c3) \
625     gradient.setColorAt( range, QColor( c(c1), c(c2), c(c3) ) );
626
627     /* Desaturated colors */
628 #define desaturate(c) c->setHsvF( c->hueF(), 0.2 , 0.5, 1.0 )
629 #define add_desaturated_color(gradient, range, c1, c2, c3) \
630     foo = new QColor( c(c1), c(c2), c(c3) );\
631     desaturate( foo ); gradient.setColorAt( range, *foo );\
632     delete foo;
633
634     /* combine the two helpers */
635 #define add_colors( gradient1, gradient2, range, c1, c2, c3 )\
636     add_color( gradient1, range, c1, c2, c3 ); \
637     add_desaturated_color( gradient2, range, c1, c2, c3 );
638
639     float f_mid_point = ( 100.0 / maximum() );
640     QColor * foo;
641     add_colors( gradient, gradient2, 0.0, 0, 1, 2 );
642     add_colors( gradient, gradient2, f_mid_point - 0.05, 3, 4, 5 );
643     add_colors( gradient, gradient2, f_mid_point + 0.05, 6, 7, 8 );
644     add_colors( gradient, gradient2, 1.0, 9, 10, 11 );
645
646     painter.begin( &pixGradient );
647     painter.setPen( Qt::NoPen );
648     painter.setBrush( gradient );
649     painter.drawRect( pixGradient.rect() );
650     painter.end();
651
652     painter.begin( &pixGradient2 );
653     painter.setPen( Qt::NoPen );
654     painter.setBrush( gradient2 );
655     painter.drawRect( pixGradient2.rect() );
656     painter.end();
657
658     pixGradient.setMask( mask );
659     pixGradient2.setMask( mask );
660 }
661
662 void SoundSlider::wheelEvent( QWheelEvent *event )
663 {
664     int newvalue = value() + event->delta() / ( 8 * 15 ) * f_step;
665     setValue( __MIN( __MAX( minimum(), newvalue ), maximum() ) );
666
667     emit sliderReleased();
668     emit sliderMoved( value() );
669 }
670
671 void SoundSlider::mousePressEvent( QMouseEvent *event )
672 {
673     if( event->button() != Qt::RightButton )
674     {
675         /* We enter the sliding mode */
676         isSliding = true;
677         i_oldvalue = value();
678         emit sliderPressed();
679         changeValue( event->x() - paddingL );
680         emit sliderMoved( value() );
681     }
682 }
683
684 void SoundSlider::mouseReleaseEvent( QMouseEvent *event )
685 {
686     if( event->button() != Qt::RightButton )
687     {
688         if( !b_mouseOutside && value() != i_oldvalue )
689         {
690             emit sliderReleased();
691             setValue( value() );
692             emit sliderMoved( value() );
693         }
694         isSliding = false;
695         b_mouseOutside = false;
696     }
697 }
698
699 void SoundSlider::mouseMoveEvent( QMouseEvent *event )
700 {
701     if( isSliding )
702     {
703         QRect rect( paddingL - 15,    -1,
704                     WLENGTH + 15 * 2 , WHEIGHT + 5 );
705         if( !rect.contains( event->pos() ) )
706         { /* We are outside */
707             if ( !b_mouseOutside )
708                 setValue( i_oldvalue );
709             b_mouseOutside = true;
710         }
711         else
712         { /* We are inside */
713             b_mouseOutside = false;
714             changeValue( event->x() - paddingL );
715             emit sliderMoved( value() );
716         }
717     }
718     else
719     {
720         int i = ( ( event->x() - paddingL ) * maximum() + 40 ) / WLENGTH;
721         i = __MIN( __MAX( 0, i ), maximum() );
722         setToolTip( QString("%1  %" ).arg( i ) );
723     }
724 }
725
726 void SoundSlider::changeValue( int x )
727 {
728     setValue( (x * maximum() + 40 ) / WLENGTH );
729 }
730
731 void SoundSlider::setMuted( bool m )
732 {
733     b_isMuted = m;
734     update();
735 }
736
737 void SoundSlider::paintEvent( QPaintEvent *e )
738 {
739     QPixmap *paintGradient;
740     if (b_isMuted)
741         paintGradient = &this->pixGradient2;
742     else
743         paintGradient = &this->pixGradient;
744
745     painter.begin( this );
746
747     const int offset = int( ( WLENGTH * value() + 100 ) / maximum() ) + paddingL;
748
749     const QRectF boundsG( 0, 0, offset , paintGradient->height() );
750     painter.drawPixmap( boundsG, *paintGradient, boundsG );
751
752     const QRectF boundsO( 0, 0, pixOutside.width(), pixOutside.height() );
753     painter.drawPixmap( boundsO, pixOutside, boundsO );
754
755     painter.setPen( foreground );
756     painter.setFont( textfont );
757     painter.drawText( textrect, Qt::AlignRight | Qt::AlignVCenter,
758                       QString::number( value() ) + '%' );
759
760     painter.end();
761     e->accept();
762 }