interface_widgets.cpp 18.1 KB
Newer Older
1
/*****************************************************************************
2
 * interface_widgets.cpp : Custom widgets for the main interface
3
 ****************************************************************************
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2006-2010 the VideoLAN team
5
 * $Id$
6 7
 *
 * Authors: Clément Stenac <zorglub@videolan.org>
8
 *          Jean-Baptiste Kempf <jb@videolan.org>
9
 *          Rafaël Carré <funman@videolanorg>
10
 *          Ilkka Ollakka <ileoo@videolan.org>
11 12 13 14
 *
 * 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
15
 * ( at your option ) any later version.
16 17 18 19 20 21 22 23 24 25
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/
26

27 28 29
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
30

31
#include "qt4.hpp"
32
#include "components/interface_widgets.hpp"
33 34
#include "dialogs_provider.hpp"
#include "util/customwidgets.hpp"               // qtEventToVLCKey, QVLCStackedWidget
35 36 37 38

#include "menus.hpp"             /* Popup menu on bgWidget */

#include <vlc_vout.h>
39

Clément Stenac's avatar
Clément Stenac committed
40
#include <QLabel>
41
#include <QToolButton>
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
42
#include <QPalette>
43
#include <QEvent>
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
44
#include <QResizeEvent>
45
#include <QDate>
46 47
#include <QMenu>
#include <QWidgetAction>
48
#include <QDesktopWidget>
49
#include <QPainter>
50
#include <QTimer>
51
#include <QSlider>
François Cartegnie's avatar
François Cartegnie committed
52
#include <QBitmap>
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
53

54
#ifdef Q_WS_X11
55 56
#   include <X11/Xlib.h>
#   include <qx11info_x11.h>
57
#endif
58

59
#include <math.h>
60
#include <assert.h>
61

62 63 64 65
/**********************************************************************
 * Video Widget. A simple frame on which video is drawn
 * This class handles resize issues
 **********************************************************************/
66

67
VideoWidget::VideoWidget( intf_thread_t *_p_i )
68
            : QFrame( NULL ) , p_intf( _p_i )
69
{
70
    /* Set the policy to expand in both directions */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
71
    // setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
72

73 74
    layout = new QHBoxLayout( this );
    layout->setContentsMargins( 0, 0, 0, 0 );
75
    stable = NULL;
76
    show();
77 78 79 80
}

VideoWidget::~VideoWidget()
{
81
    /* Ensure we are not leaking the video output. This would crash. */
82 83 84 85 86 87 88 89 90 91 92
    assert( !stable );
}

void VideoWidget::sync( void )
{
#ifdef Q_WS_X11
    /* Make sure the X server has processed all requests.
     * This protects other threads using distinct connections from getting
     * the video widget window in an inconsistent states. */
    XSync( QX11Info::display(), False );
#endif
93 94
}

95
/**
96
 * Request the video to avoid the conflicts
97
 **/
98
WId VideoWidget::request( int *pi_x, int *pi_y,
99 100
                          unsigned int *pi_width, unsigned int *pi_height,
                          bool b_keep_size )
101
{
102
    msg_Dbg( p_intf, "Video was requested %i, %i", *pi_x, *pi_y );
103

104
    if( stable )
105 106
    {
        msg_Dbg( p_intf, "embedded video already in use" );
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
107
        return 0;
108
    }
109 110 111 112 113 114
    if( b_keep_size )
    {
        *pi_width  = size().width();
        *pi_height = size().height();
    }

115 116 117
    /* The owner of the video window needs a stable handle (WinId). Reparenting
     * in Qt4-X11 changes the WinId of the widget, so we need to create another
     * dummy widget that stays within the reparentable widget. */
118
    stable = new QWidget();
119 120 121 122
    QPalette plt = palette();
    plt.setColor( QPalette::Window, Qt::black );
    stable->setPalette( plt );
    stable->setAutoFillBackground(true);
123 124 125
    /* Indicates that the widget wants to draw directly onto the screen.
       Widgets with this attribute set do not participate in composition
       management */
126 127 128
    /* This is currently disabled on X11 as it does not seem to improve
     * performance, but causes the video widget to be transparent... */
#ifndef Q_WS_X11
129
    stable->setAttribute( Qt::WA_PaintOnScreen, true );
130
#endif
131

132
    layout->addWidget( stable );
133

134
#ifdef Q_WS_X11
135
    /* HACK: Only one X11 client can subscribe to mouse button press events.
136
     * VLC currently handles those in the video display.
137
     * Force Qt4 to unsubscribe from mouse press and release events. */
138 139 140 141 142
    Display *dpy = QX11Info::display();
    Window w = stable->winId();
    XWindowAttributes attr;

    XGetWindowAttributes( dpy, w, &attr );
143 144
    attr.your_event_mask &= ~(ButtonPressMask|ButtonReleaseMask);
    XSelectInput( dpy, w, attr.your_event_mask );
145
#endif
146
    sync();
147
    return stable->winId();
148 149
}

150
/* Set the Widget to the correct Size */
151
/* Function has to be called by the parent
Rémi Denis-Courmont's avatar
typo  
Rémi Denis-Courmont committed
152
   Parent has to care about resizing itself */
153
void VideoWidget::SetSizing( unsigned int w, unsigned int h )
154
{
155 156
    resize( w, h );
    emit sizeChanged( w, h );
157 158 159 160 161
    /* Work-around a bug?misconception? that would happen when vout core resize
       twice to the same size and would make the vout not centered.
       This cause a small flicker.
       See #3621
     */
162
    if( (unsigned)size().width() == w && (unsigned)size().height() == h )
163
        updateGeometry();
164
    sync();
165 166
}

167
void VideoWidget::release( void )
168
{
Rémi Denis-Courmont's avatar
Typo  
Rémi Denis-Courmont committed
169
    msg_Dbg( p_intf, "Video is not needed anymore" );
170

171 172 173 174 175 176
    if( stable )
    {
        layout->removeWidget( stable );
        stable->deleteLater();
        stable = NULL;
    }
177

178
    updateGeometry();
179
}
180

181 182
/**********************************************************************
 * Background Widget. Show a simple image background. Currently,
183
 * it's album art if present or cone.
184
 **********************************************************************/
185

186
BackgroundWidget::BackgroundWidget( intf_thread_t *_p_i )
187
    :QWidget( NULL ), p_intf( _p_i ), b_expandPixmap( false ), b_withart( true )
188
{
189
    /* A dark background */
Clément Stenac's avatar
Clément Stenac committed
190
    setAutoFillBackground( true );
191
    QPalette plt = palette();
192 193 194 195
    plt.setColor( QPalette::Active, QPalette::Window , Qt::black );
    plt.setColor( QPalette::Inactive, QPalette::Window , Qt::black );
    setPalette( plt );

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
196 197 198
    /* Init the cone art */
    updateArt( "" );

199
    CONNECT( THEMIM->getIM(), artChanged( QString ),
200
             this, updateArt( const QString& ) );
201 202
}

203
void BackgroundWidget::updateArt( const QString& url )
204
{
François Cartegnie's avatar
François Cartegnie committed
205
    if ( !url.isEmpty() )
Ilkka Ollakka's avatar
 
Ilkka Ollakka committed
206
    {
François Cartegnie's avatar
François Cartegnie committed
207 208 209 210
        pixmapUrl = url;
    }
    else
    {   /* Xmas joke */
211
        if( QDate::currentDate().dayOfYear() >= QT_XMAS_JOKE_DAY && var_InheritBool( p_intf, "qt-icon-change" ) )
212
            pixmapUrl = QString( ":/logo/vlc128-xmas.png" );
213
        else
François Cartegnie's avatar
François Cartegnie committed
214
            pixmapUrl = QString( ":/logo/vlc128.png" );
Ilkka Ollakka's avatar
 
Ilkka Ollakka committed
215
    }
216
    update();
François Cartegnie's avatar
François Cartegnie committed
217 218 219 220
}

void BackgroundWidget::paintEvent( QPaintEvent *e )
{
221 222 223 224 225 226 227
    if ( !b_withart )
    {
        /* we just want background autofill */
        QWidget::paintEvent( e );
        return;
    }

François Cartegnie's avatar
François Cartegnie committed
228 229 230 231 232 233
    int i_maxwidth, i_maxheight;
    QPixmap pixmap = QPixmap( pixmapUrl );
    QPainter painter(this);
    QBitmap pMask;
    float f_alpha = 1.0;

234 235
    i_maxwidth  = __MIN( maximumWidth(), width() ) - MARGIN * 2;
    i_maxheight = __MIN( maximumHeight(), height() ) - MARGIN * 2;
François Cartegnie's avatar
François Cartegnie committed
236 237

    if ( height() > MARGIN * 2 )
Ilkka Ollakka's avatar
 
Ilkka Ollakka committed
238
    {
François Cartegnie's avatar
François Cartegnie committed
239 240 241 242 243 244 245 246 247
        /* Scale down the pixmap if the widget is too small */
        if( pixmap.width() > i_maxwidth || pixmap.height() > i_maxheight )
        {
            pixmap = pixmap.scaled( i_maxwidth, i_maxheight,
                            Qt::KeepAspectRatio, Qt::SmoothTransformation );
        }
        else
        if ( b_expandPixmap &&
             pixmap.width() < width() && pixmap.height() < height() )
248
        {
François Cartegnie's avatar
François Cartegnie committed
249 250 251 252 253 254 255 256 257 258 259 260 261 262
            /* Scale up the pixmap to fill widget's size */
            f_alpha = ( (float) pixmap.height() / (float) height() );
            pixmap = pixmap.scaled(
                    width() - MARGIN * 2,
                    height() - MARGIN * 2,
                    Qt::KeepAspectRatio,
                    ( f_alpha < .2 )? /* Don't waste cpu when not visible */
                        Qt::SmoothTransformation:
                        Qt::FastTransformation
                    );
            /* Non agressive alpha compositing when sizing up */
            pMask = QBitmap( pixmap.width(), pixmap.height() );
            pMask.fill( QColor::fromRgbF( 1.0, 1.0, 1.0, f_alpha ) );
            pixmap.setMask( pMask );
263 264
        }

François Cartegnie's avatar
François Cartegnie committed
265 266 267 268
        painter.drawPixmap(
                MARGIN + ( i_maxwidth - pixmap.width() ) /2,
                MARGIN + ( i_maxheight - pixmap.height() ) /2,
                pixmap);
Ilkka Ollakka's avatar
 
Ilkka Ollakka committed
269
    }
François Cartegnie's avatar
François Cartegnie committed
270
    QWidget::paintEvent( e );
271 272
}

273 274
void BackgroundWidget::contextMenuEvent( QContextMenuEvent *event )
{
275
    QVLCMenuManager::PopupMenu( p_intf, true );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
276
    event->accept();
277
}
278

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
279
#if 0
280 281 282
#include <QPushButton>
#include <QHBoxLayout>

Clément Stenac's avatar
Clément Stenac committed
283 284 285 286
/**********************************************************************
 * Visualization selector panel
 **********************************************************************/
VisualSelector::VisualSelector( intf_thread_t *_p_i ) :
287
                                QFrame( NULL ), p_intf( _p_i )
Clément Stenac's avatar
Clément Stenac committed
288 289
{
    QHBoxLayout *layout = new QHBoxLayout( this );
290
    layout->setMargin( 0 );
Clément Stenac's avatar
Clément Stenac committed
291
    QPushButton *prevButton = new QPushButton( "Prev" );
292
    QPushButton *nextButton = new QPushButton( "Next" );
Clément Stenac's avatar
Clément Stenac committed
293 294
    layout->addWidget( prevButton );
    layout->addWidget( nextButton );
Clément Stenac's avatar
Clément Stenac committed
295

296
    layout->addStretch( 10 );
297
    layout->addWidget( new QLabel( qtr( "Current visualization" ) ) );
Clément Stenac's avatar
Clément Stenac committed
298 299 300 301 302 303 304

    current = new QLabel( qtr( "None" ) );
    layout->addWidget( current );

    BUTTONACT( prevButton, prev() );
    BUTTONACT( nextButton, next() );

Clément Stenac's avatar
Clément Stenac committed
305
    setLayout( layout );
306
    setMaximumHeight( 35 );
Clément Stenac's avatar
Clément Stenac committed
307 308 309
}

VisualSelector::~VisualSelector()
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
310
{}
Clément Stenac's avatar
Clément Stenac committed
311

Clément Stenac's avatar
Clément Stenac committed
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
void VisualSelector::prev()
{
    char *psz_new = aout_VisualPrev( p_intf );
    if( psz_new )
    {
        current->setText( qfu( psz_new ) );
        free( psz_new );
    }
}

void VisualSelector::next()
{
    char *psz_new = aout_VisualNext( p_intf );
    if( psz_new )
    {
        current->setText( qfu( psz_new ) );
        free( psz_new );
    }
}
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
331
#endif
Clément Stenac's avatar
Clément Stenac committed
332

333 334
SpeedLabel::SpeedLabel( intf_thread_t *_p_intf, QWidget *parent )
           : QLabel( parent ), p_intf( _p_intf )
335
{
336
    tooltipStringPattern = qtr( "Current playback speed: %1\nClick to adjust" );
337 338

    /* Create the Speed Control Widget */
339
    speedControl = new SpeedControlWidget( p_intf, this );
340 341 342 343 344 345 346
    speedControlMenu = new QMenu( this );

    QWidgetAction *widgetAction = new QWidgetAction( speedControl );
    widgetAction->setDefaultWidget( speedControl );
    speedControlMenu->addAction( widgetAction );

    /* Change the SpeedRate in the Status Bar */
347
    CONNECT( THEMIM->getIM(), rateChanged( float ), this, setRate( float ) );
348

349 350
    DCONNECT( THEMIM, inputChanged( input_thread_t * ),
              speedControl, activateOnState() );
351
    setRate( var_InheritFloat( p_intf, "rate" ) );
352
}
353

354 355
SpeedLabel::~SpeedLabel()
{
356 357
    delete speedControl;
    delete speedControlMenu;
358
}
359

360 361 362
/****************************************************************************
 * Small right-click menu for rate control
 ****************************************************************************/
363

364 365 366 367 368 369
void SpeedLabel::showSpeedMenu( QPoint pos )
{
    speedControlMenu->exec( QCursor::pos() - pos
                          + QPoint( 0, height() ) );
}

370
void SpeedLabel::setRate( float rate )
371 372
{
    QString str;
373
    str.setNum( rate, 'f', 2 );
374 375
    str.append( "x" );
    setText( str );
376
    setToolTip( tooltipStringPattern.arg( str ) );
377 378 379
    speedControl->updateControls( rate );
}

380 381 382
/**********************************************************************
 * Speed control widget
 **********************************************************************/
383 384
SpeedControlWidget::SpeedControlWidget( intf_thread_t *_p_i, QWidget *_parent )
                    : QFrame( _parent ), p_intf( _p_i )
385
{
386
    QSizePolicy sizePolicy( QSizePolicy::Maximum, QSizePolicy::Fixed );
387 388
    sizePolicy.setHorizontalStretch( 0 );
    sizePolicy.setVerticalStretch( 0 );
389

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
390
    speedSlider = new QSlider( this );
391 392 393 394 395
    speedSlider->setSizePolicy( sizePolicy );
    speedSlider->setMaximumSize( QSize( 80, 200 ) );
    speedSlider->setOrientation( Qt::Vertical );
    speedSlider->setTickPosition( QSlider::TicksRight );

396
    speedSlider->setRange( -34, 34 );
397 398
    speedSlider->setSingleStep( 1 );
    speedSlider->setPageStep( 1 );
399
    speedSlider->setTickInterval( 17 );
400

401
    CONNECT( speedSlider, valueChanged( int ), this, updateRate( int ) );
402

403
    QToolButton *normalSpeedButton = new QToolButton( this );
404
    normalSpeedButton->setMaximumSize( QSize( 26, 20 ) );
405
    normalSpeedButton->setAutoRaise( true );
406
    normalSpeedButton->setText( "1x" );
407
    normalSpeedButton->setToolTip( qtr( "Revert to normal play speed" ) );
408

409
    CONNECT( normalSpeedButton, clicked(), this, resetRate() );
410

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
411
    QVBoxLayout *speedControlLayout = new QVBoxLayout( this );
412
    speedControlLayout->setContentsMargins( 4, 4, 4, 4 );
413
    speedControlLayout->setSpacing( 4 );
414 415
    speedControlLayout->addWidget( speedSlider );
    speedControlLayout->addWidget( normalSpeedButton );
416

417 418
    lastValue = 0;

419 420
    activateOnState();
}
421

422
void SpeedControlWidget::activateOnState()
423
{
424
    speedSlider->setEnabled( THEMIM->getIM()->hasInput() );
425 426
}

427
void SpeedControlWidget::updateControls( float rate )
428
{
429 430 431 432 433
    if( speedSlider->isSliderDown() )
    {
        //We don't want to change anything if the user is using the slider
        return;
    }
434

435
    double value = 17 * log( rate ) / log( 2. );
436
    int sliderValue = (int) ( ( value > 0 ) ? value + .5 : value - .5 );
437

438
    if( sliderValue < speedSlider->minimum() )
439
    {
440
        sliderValue = speedSlider->minimum();
441
    }
442
    else if( sliderValue > speedSlider->maximum() )
443
    {
444
        sliderValue = speedSlider->maximum();
445
    }
446
    lastValue = sliderValue;
447

448
    speedSlider->setValue( sliderValue );
449 450 451 452
}

void SpeedControlWidget::updateRate( int sliderValue )
{
453 454 455
    if( sliderValue == lastValue )
        return;

456
    double speed = pow( 2, (double)sliderValue / 17 );
457
    int rate = INPUT_RATE_DEFAULT / speed;
458

459
    THEMIM->getIM()->setRate(rate);
460 461 462 463
}

void SpeedControlWidget::resetRate()
{
464
    THEMIM->getIM()->setRate( INPUT_RATE_DEFAULT );
465
}
466

467
CoverArtLabel::CoverArtLabel( QWidget *parent, intf_thread_t *_p_i )
468
              : QLabel( parent ), p_intf( _p_i )
469 470
{
    setContextMenuPolicy( Qt::ActionsContextMenu );
471
    CONNECT( this, updateRequested(), this, askForUpdate() );
472

473
    setMinimumHeight( 128 );
474 475
    setMinimumWidth( 128 );
    setMaximumHeight( 128 );
476 477
    setScaledContents( false );
    setAlignment( Qt::AlignCenter );
478

479 480
    QList< QAction* > artActions = actions();
    QAction *action = new QAction( qtr( "Download cover art" ), this );
481
    CONNECT( action, triggered(), this, askForUpdate() );
482
    addAction( action );
483

484
    showArtUpdate( "" );
485 486
}

487 488 489 490 491 492 493
CoverArtLabel::~CoverArtLabel()
{
    QList< QAction* > artActions = actions();
    foreach( QAction *act, artActions )
        removeAction( act );
}

494
void CoverArtLabel::showArtUpdate( const QString& url )
495
{
496
    QPixmap pix;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
497
    if( !url.isEmpty() && pix.load( url ) )
498
    {
499
        pix = pix.scaled( minimumWidth(), maximumHeight(),
500 501
                          Qt::KeepAspectRatioByExpanding,
                          Qt::SmoothTransformation );
502 503 504
    }
    else
    {
505
        pix = QPixmap( ":/noart.png" );
506
    }
507
    setPixmap( pix );
508 509
}

510
void CoverArtLabel::askForUpdate()
511
{
512
    THEMIM->getIM()->requestArtUpdate();
513 514
}

515 516 517
TimeLabel::TimeLabel( intf_thread_t *_p_intf  )
    : QLabel(), p_intf( _p_intf ), bufTimer( new QTimer(this) ),
      buffering( false ), showBuffering(false), bufVal( -1 )
518
{
519 520 521
    b_remainingTime = false;
    setText( " --:--/--:-- " );
    setAlignment( Qt::AlignRight | Qt::AlignVCenter );
522 523 524 525
    setToolTip( QString( "- " )
        + qtr( "Click to toggle between elapsed and remaining time" )
        + QString( "\n- " )
        + qtr( "Double click to jump to a chosen time position" ) );
526
    bufTimer->setSingleShot( true );
527

528 529 530 531 532
    CONNECT( THEMIM->getIM(), positionUpdated( float, int64_t, int ),
              this, setDisplayPosition( float, int64_t, int ) );
    CONNECT( THEMIM->getIM(), cachingChanged( float ),
              this, updateBuffering( float ) );
    CONNECT( bufTimer, timeout(), this, updateBuffering() );
533 534
}

535
void TimeLabel::setDisplayPosition( float pos, int64_t t, int length )
536
{
537 538 539
    showBuffering = false;
    bufTimer->stop();

540
    if( pos == -1.f )
541 542 543 544
    {
        setText( " --:--/--:-- " );
        return;
    }
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
545

546
    int time = t / 1000000;
547

548 549 550 551
    secstotimestr( psz_length, length );
    secstotimestr( psz_time, ( b_remainingTime && length ) ? length - time
                                                           : time );

552 553 554 555
    QString timestr = QString( " %1%2/%3 " )
            .arg( QString( (b_remainingTime && length) ? "-" : "" ) )
            .arg( QString( psz_time ) )
            .arg( QString( ( !length && time ) ? "--:--" : psz_length ) );
556

JP Dinger's avatar
JP Dinger committed
557
    setText( timestr );
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573

    cachedLength = length;
}

void TimeLabel::setDisplayPosition( float pos )
{
    if( pos == -1.f || cachedLength == 0 )
    {
        setText( " --:--/--:-- " );
        return;
    }

    int time = pos * cachedLength;
    secstotimestr( psz_time,
                   ( b_remainingTime && cachedLength ?
                   cachedLength - time : time ) );
574 575 576 577
    QString timestr = QString( " %1%2/%3 " )
        .arg( QString( (b_remainingTime && cachedLength) ? "-" : "" ) )
        .arg( QString( psz_time ) )
        .arg( QString( ( !cachedLength && time ) ? "--:--" : psz_length ) );
578 579

    setText( timestr );
580 581
}

582

583 584 585 586
void TimeLabel::toggleTimeDisplay()
{
    b_remainingTime = !b_remainingTime;
}
587 588


589
void TimeLabel::updateBuffering( float _buffered )
590
{
591 592 593 594 595 596 597 598 599 600
    bufVal = _buffered;
    if( !buffering || bufVal == 0 )
    {
        showBuffering = false;
        buffering = true;
        bufTimer->start(200);
    }
    else if( bufVal == 1 )
    {
        showBuffering = buffering = false;
601
        bufTimer->stop();
602 603
    }
    update();
604
}
605

606
void TimeLabel::updateBuffering()
607
{
608 609
    showBuffering = true;
    update();
610 611
}

612
void TimeLabel::paintEvent( QPaintEvent* event )
613
{
614 615 616 617 618 619 620 621
    if( showBuffering )
    {
        QRect r( rect() );
        r.setLeft( r.width() * bufVal );
        QPainter p( this );
        p.setOpacity( 0.4 );
        p.fillRect( r, palette().color( QPalette::Highlight ) );
    }
622 623
    QLabel::paintEvent( event );
}