interface_widgets.cpp 21.6 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
    VLCMenuBar::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
    speedControlMenu = new QMenu( this );

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

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

349 350
    DCONNECT( THEMIM, inputChanged( input_thread_t * ),
              speedControl, activateOnState() );
351 352 353 354

    setFrameStyle( QFrame::StyledPanel | QFrame::Raised );
    setLineWidth( 1 );

355
    setRate( var_InheritFloat( p_intf, "rate" ) );
356
}
357

358 359
SpeedLabel::~SpeedLabel()
{
360 361
    delete speedControl;
    delete speedControlMenu;
362
}
363

364 365 366
/****************************************************************************
 * Small right-click menu for rate control
 ****************************************************************************/
367

368 369 370
void SpeedLabel::showSpeedMenu( QPoint pos )
{
    speedControlMenu->exec( QCursor::pos() - pos
371
                            + QPoint( -70 + width()/2, height() ) );
372 373
}

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

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

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
394
    speedSlider = new QSlider( this );
395
    speedSlider->setSizePolicy( sizePolicy );
396 397
    speedSlider->setMinimumSize( QSize( 140, 20 ) );
    speedSlider->setOrientation( Qt::Horizontal );
398
    speedSlider->setTickPosition( QSlider::TicksBelow );
399

400
    speedSlider->setRange( -34, 34 );
401 402
    speedSlider->setSingleStep( 1 );
    speedSlider->setPageStep( 1 );
403
    speedSlider->setTickInterval( 17 );
404

405
    CONNECT( speedSlider, valueChanged( int ), this, updateRate( int ) );
406

407
    QToolButton *normalSpeedButton = new QToolButton( this );
408
    normalSpeedButton->setMaximumSize( QSize( 26, 16 ) );
409
    normalSpeedButton->setAutoRaise( true );
410
    normalSpeedButton->setText( "1x" );
411
    normalSpeedButton->setToolTip( qtr( "Revert to normal play speed" ) );
412

413
    CONNECT( normalSpeedButton, clicked(), this, resetRate() );
414

415 416 417 418 419 420 421 422 423 424 425 426 427 428
    QToolButton *slowerButton = new QToolButton( this );
    slowerButton->setMaximumSize( QSize( 26, 16 ) );
    slowerButton->setAutoRaise( true );
    slowerButton->setToolTip( tooltipL[SLOWER_BUTTON] );
    slowerButton->setIcon( QIcon( iconL[SLOWER_BUTTON] ) );
    CONNECT( slowerButton, clicked(), THEMIM->getIM(), slower() );

    QToolButton *fasterButton = new QToolButton( this );
    fasterButton->setMaximumSize( QSize( 26, 16 ) );
    fasterButton->setAutoRaise( true );
    fasterButton->setToolTip( tooltipL[FASTER_BUTTON] );
    fasterButton->setIcon( QIcon( iconL[FASTER_BUTTON] ) );
    CONNECT( fasterButton, clicked(), THEMIM->getIM(), faster() );

429 430 431 432 433 434 435 436 437 438 439
/*    spinBox = new QDoubleSpinBox();
    spinBox->setDecimals( 2 );
    spinBox->setMaximum( 32 );
    spinBox->setMinimum( 0.03F );
    spinBox->setSingleStep( 0.10F );
    spinBox->setAlignment( Qt::AlignRight );

    CONNECT( spinBox, valueChanged( double ), this, updateSpinBoxRate( double ) ); */

    QGridLayout* speedControlLayout = new QGridLayout( this );
    speedControlLayout->addWidget( speedSlider, 0, 0, 1, 3 );
440 441 442
    speedControlLayout->addWidget( slowerButton, 1, 0 );
    speedControlLayout->addWidget( normalSpeedButton, 1, 1, 1, 1, Qt::AlignRight );
    speedControlLayout->addWidget( fasterButton, 1, 2, 1, 1, Qt::AlignRight );
443 444 445
    //speedControlLayout->addWidget( spinBox );
    speedControlLayout->setContentsMargins( 0, 0, 0, 0 );
    speedControlLayout->setSpacing( 0 );
446

447 448
    lastValue = 0;

449 450
    activateOnState();
}
451

452
void SpeedControlWidget::activateOnState()
453
{
454
    speedSlider->setEnabled( THEMIM->getIM()->hasInput() );
455
    //spinBox->setEnabled( THEMIM->getIM()->hasInput() );
456 457
}

458
void SpeedControlWidget::updateControls( float rate )
459
{
460 461 462 463 464
    if( speedSlider->isSliderDown() )
    {
        //We don't want to change anything if the user is using the slider
        return;
    }
465

466
    double value = 17 * log( rate ) / log( 2. );
467
    int sliderValue = (int) ( ( value > 0 ) ? value + .5 : value - .5 );
468

469
    if( sliderValue < speedSlider->minimum() )
470
    {
471
        sliderValue = speedSlider->minimum();
472
    }
473
    else if( sliderValue > speedSlider->maximum() )
474
    {
475
        sliderValue = speedSlider->maximum();
476
    }
477
    lastValue = sliderValue;
478

479
    speedSlider->setValue( sliderValue );
480
    //spinBox->setValue( rate );
481 482 483 484
}

void SpeedControlWidget::updateRate( int sliderValue )
{
485 486 487
    if( sliderValue == lastValue )
        return;

488
    double speed = pow( 2, (double)sliderValue / 17 );
489
    int rate = INPUT_RATE_DEFAULT / speed;
490

491
    THEMIM->getIM()->setRate(rate);
492 493 494 495 496 497
    //spinBox->setValue( var_InheritFloat( THEPL, "rate" ) );
}

void SpeedControlWidget::updateSpinBoxRate( double r )
{
    var_SetFloat( THEPL, "rate", r );
498 499 500 501
}

void SpeedControlWidget::resetRate()
{
502
    THEMIM->getIM()->setRate( INPUT_RATE_DEFAULT );
503
}
504

505
CoverArtLabel::CoverArtLabel( QWidget *parent, intf_thread_t *_p_i )
506
              : QLabel( parent ), p_intf( _p_i )
507 508
{
    setContextMenuPolicy( Qt::ActionsContextMenu );
509
    CONNECT( this, updateRequested(), this, askForUpdate() );
510

511
    setMinimumHeight( 128 );
512 513
    setMinimumWidth( 128 );
    setMaximumHeight( 128 );
514 515
    setScaledContents( false );
    setAlignment( Qt::AlignCenter );
516

517 518
    QList< QAction* > artActions = actions();
    QAction *action = new QAction( qtr( "Download cover art" ), this );
519
    CONNECT( action, triggered(), this, askForUpdate() );
520
    addAction( action );
521

522
    showArtUpdate( "" );
523 524
}

525 526 527 528 529 530 531
CoverArtLabel::~CoverArtLabel()
{
    QList< QAction* > artActions = actions();
    foreach( QAction *act, artActions )
        removeAction( act );
}

532
void CoverArtLabel::showArtUpdate( const QString& url )
533
{
534
    QPixmap pix;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
535
    if( !url.isEmpty() && pix.load( url ) )
536
    {
537
        pix = pix.scaled( minimumWidth(), maximumHeight(),
538 539
                          Qt::KeepAspectRatioByExpanding,
                          Qt::SmoothTransformation );
540 541 542
    }
    else
    {
543
        pix = QPixmap( ":/noart.png" );
544
    }
545
    setPixmap( pix );
546 547
}

548
void CoverArtLabel::askForUpdate()
549
{
550
    THEMIM->getIM()->requestArtUpdate();
551 552
}

553
TimeLabel::TimeLabel( intf_thread_t *_p_intf, TimeLabel::Display _displayType  )
554
    : ClickableQLabel(), p_intf( _p_intf ), bufTimer( new QTimer(this) ),
555
      buffering( false ), showBuffering(false), bufVal( -1 ), displayType( _displayType )
556
{
557
    b_remainingTime = false;
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
    switch( _displayType ) {
        case TimeLabel::Elapsed:
            setText( " --:-- " );
            setToolTip( qtr("Elapsed time") );
            break;
        case TimeLabel::Remaining:
            setText( " --:-- " );
            setToolTip( qtr("Total/Remaining time")
                        + QString("\n-")
                        + qtr("Click to toggle between total and remaining time")
                      );
            break;
        case TimeLabel::Both:
            setText( " --:--/--:-- " );
            setToolTip( QString( "- " )
                + qtr( "Click to toggle between elapsed and remaining time" )
                + QString( "\n- " )
                + qtr( "Double click to jump to a chosen time position" ) );
            break;
    }
578
    setAlignment( Qt::AlignRight | Qt::AlignVCenter );
579

580
    bufTimer->setSingleShot( true );
581

582 583 584 585 586
    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() );
587

588
    setStyleSheet( "padding-left: 4px; padding-right: 4px;" );
589 590
}

591
void TimeLabel::setDisplayPosition( float pos, int64_t t, int length )
592
{
593 594 595
    showBuffering = false;
    bufTimer->stop();

596
    if( pos == -1.f )
597
    {
598
        setMinimumSize( QSize( 0, 0 ) );
599
        if( displayType == TimeLabel::Both )
600
            setText( "--:--/--:--" );
601
        else
602
            setText( "--:--" );
603 604
        return;
    }
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
605

606
    int time = t / 1000000;
607

608 609 610
    secstotimestr( psz_length, length );
    secstotimestr( psz_time, ( b_remainingTime && length ) ? length - time
                                                           : time );
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627

    // compute the minimum size that will be required for the psz_length
    // and use it to enforce a minimal size to avoid "dancing" widgets
    QSize minsize;
    if ( length > 0 )
    {
        QMargins margins = contentsMargins();
        minsize += QSize(
                  fontMetrics().size( 0, QString( psz_length ), 0, 0 ).width(),
                  sizeHint().height()
                );
        minsize += QSize( margins.left() + margins.right() + 8, 0 ); /* +padding */

        if ( b_remainingTime )
            minsize += QSize( fontMetrics().size( 0, "-", 0, 0 ).width(), 0 );
    }

628 629 630
    switch( displayType )
    {
        case TimeLabel::Elapsed:
631
            setMinimumSize( minsize );
632
            setText( QString( psz_time ) );
633 634 635
            break;
        case TimeLabel::Remaining:
            if( b_remainingTime )
636 637
            {
                setMinimumSize( minsize );
638
                setText( QString("-") + QString( psz_time ) );
639
            }
640
            else
641 642
            {
                setMinimumSize( QSize( 0, 0 ) );
643
                setText( QString( psz_length ) );
644
            }
645 646 647
            break;
        case TimeLabel::Both:
        default:
648
            QString timestr = QString( "%1%2/%3" )
649 650 651
            .arg( QString( (b_remainingTime && length) ? "-" : "" ) )
            .arg( QString( psz_time ) )
            .arg( QString( ( !length && time ) ? "--:--" : psz_length ) );
652

653 654 655
            setText( timestr );
            break;
    }
656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
    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 ) );
671
    QString timestr = QString( "%1%2/%3" )
672 673 674
        .arg( QString( (b_remainingTime && cachedLength) ? "-" : "" ) )
        .arg( QString( psz_time ) )
        .arg( QString( ( !cachedLength && time ) ? "--:--" : psz_length ) );
675 676

    setText( timestr );
677 678
}

679

680 681 682 683
void TimeLabel::toggleTimeDisplay()
{
    b_remainingTime = !b_remainingTime;
}
684 685


686
void TimeLabel::updateBuffering( float _buffered )
687
{
688 689 690 691 692 693 694 695 696 697
    bufVal = _buffered;
    if( !buffering || bufVal == 0 )
    {
        showBuffering = false;
        buffering = true;
        bufTimer->start(200);
    }
    else if( bufVal == 1 )
    {
        showBuffering = buffering = false;
698
        bufTimer->stop();
699 700
    }
    update();
701
}
702

703
void TimeLabel::updateBuffering()
704
{
705 706
    showBuffering = true;
    update();
707 708
}

709
void TimeLabel::paintEvent( QPaintEvent* event )
710
{
711 712 713 714 715 716 717 718
    if( showBuffering )
    {
        QRect r( rect() );
        r.setLeft( r.width() * bufVal );
        QPainter p( this );
        p.setOpacity( 0.4 );
        p.fillRect( r, palette().color( QPalette::Highlight ) );
    }
719 720
    QLabel::paintEvent( event );
}