]> git.sesse.net Git - vlc/blob - modules/gui/qt4/components/interface_widgets.cpp
Qt4: update avcodec-hw prefs
[vlc] / modules / gui / qt4 / components / interface_widgets.cpp
1 /*****************************************************************************
2  * interface_widgets.cpp : Custom widgets for the main interface
3  ****************************************************************************
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  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * ( at your option ) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include "qt4.hpp"
32 #include "components/interface_widgets.hpp"
33 #include "dialogs_provider.hpp"
34 #include "util/customwidgets.hpp"               // qtEventToVLCKey, QVLCStackedWidget
35
36 #include "menus.hpp"             /* Popup menu on bgWidget */
37
38 #include <vlc_vout.h>
39
40 #include <QLabel>
41 #include <QToolButton>
42 #include <QPalette>
43 #include <QEvent>
44 #include <QResizeEvent>
45 #include <QDate>
46 #include <QMenu>
47 #include <QWidgetAction>
48 #include <QDesktopWidget>
49 #include <QPainter>
50 #include <QTimer>
51 #include <QSlider>
52 #include <QBitmap>
53 #include <QUrl>
54
55 #ifdef Q_WS_X11
56 #   include <X11/Xlib.h>
57 #   include <qx11info_x11.h>
58 #endif
59
60 #include <math.h>
61 #include <assert.h>
62
63 /**********************************************************************
64  * Video Widget. A simple frame on which video is drawn
65  * This class handles resize issues
66  **********************************************************************/
67
68 VideoWidget::VideoWidget( intf_thread_t *_p_i )
69             : QFrame( NULL ) , p_intf( _p_i )
70 {
71     /* Set the policy to expand in both directions */
72     // setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
73
74     layout = new QHBoxLayout( this );
75     layout->setContentsMargins( 0, 0, 0, 0 );
76     stable = NULL;
77     show();
78 }
79
80 VideoWidget::~VideoWidget()
81 {
82     /* Ensure we are not leaking the video output. This would crash. */
83     assert( !stable );
84 }
85
86 void VideoWidget::sync( void )
87 {
88 #ifdef Q_WS_X11
89     /* Make sure the X server has processed all requests.
90      * This protects other threads using distinct connections from getting
91      * the video widget window in an inconsistent states. */
92     XSync( QX11Info::display(), False );
93 #endif
94 }
95
96 /**
97  * Request the video to avoid the conflicts
98  **/
99 WId VideoWidget::request( int *pi_x, int *pi_y,
100                           unsigned int *pi_width, unsigned int *pi_height,
101                           bool b_keep_size )
102 {
103     msg_Dbg( p_intf, "Video was requested %i, %i", *pi_x, *pi_y );
104
105     if( stable )
106     {
107         msg_Dbg( p_intf, "embedded video already in use" );
108         return 0;
109     }
110     if( b_keep_size )
111     {
112         *pi_width  = size().width();
113         *pi_height = size().height();
114     }
115
116     /* The owner of the video window needs a stable handle (WinId). Reparenting
117      * in Qt4-X11 changes the WinId of the widget, so we need to create another
118      * dummy widget that stays within the reparentable widget. */
119     stable = new QWidget();
120     QPalette plt = palette();
121     plt.setColor( QPalette::Window, Qt::black );
122     stable->setPalette( plt );
123     stable->setAutoFillBackground(true);
124     /* Force the widget to be native so that it gets a winId() */
125     stable->setAttribute( Qt::WA_NativeWindow, true );
126     /* Indicates that the widget wants to draw directly onto the screen.
127        Widgets with this attribute set do not participate in composition
128        management */
129     /* This is currently disabled on X11 as it does not seem to improve
130      * performance, but causes the video widget to be transparent... */
131 #if !defined (Q_WS_X11) && !defined (Q_WS_QPA)
132     stable->setAttribute( Qt::WA_PaintOnScreen, true );
133 #endif
134
135     layout->addWidget( stable );
136
137 #ifdef Q_WS_X11
138     /* HACK: Only one X11 client can subscribe to mouse button press events.
139      * VLC currently handles those in the video display.
140      * Force Qt4 to unsubscribe from mouse press and release events. */
141     Display *dpy = QX11Info::display();
142     Window w = stable->winId();
143     XWindowAttributes attr;
144
145     XGetWindowAttributes( dpy, w, &attr );
146     attr.your_event_mask &= ~(ButtonPressMask|ButtonReleaseMask);
147     XSelectInput( dpy, w, attr.your_event_mask );
148 #endif
149     sync();
150     return stable->winId();
151 }
152
153 /* Set the Widget to the correct Size */
154 /* Function has to be called by the parent
155    Parent has to care about resizing itself */
156 void VideoWidget::SetSizing( unsigned int w, unsigned int h )
157 {
158     resize( w, h );
159     emit sizeChanged( w, h );
160     /* Work-around a bug?misconception? that would happen when vout core resize
161        twice to the same size and would make the vout not centered.
162        This cause a small flicker.
163        See #3621
164      */
165     if( (unsigned)size().width() == w && (unsigned)size().height() == h )
166         updateGeometry();
167     sync();
168 }
169
170 void VideoWidget::release( void )
171 {
172     msg_Dbg( p_intf, "Video is not needed anymore" );
173
174     if( stable )
175     {
176         layout->removeWidget( stable );
177         stable->deleteLater();
178         stable = NULL;
179     }
180
181     updateGeometry();
182 }
183
184 /**********************************************************************
185  * Background Widget. Show a simple image background. Currently,
186  * it's album art if present or cone.
187  **********************************************************************/
188
189 BackgroundWidget::BackgroundWidget( intf_thread_t *_p_i )
190     :QWidget( NULL ), p_intf( _p_i ), b_expandPixmap( false ), b_withart( true )
191 {
192     /* A dark background */
193     setAutoFillBackground( true );
194     QPalette plt = palette();
195     plt.setColor( QPalette::Active, QPalette::Window , Qt::black );
196     plt.setColor( QPalette::Inactive, QPalette::Window , Qt::black );
197     setPalette( plt );
198
199     /* Init the cone art */
200     updateArt( "" );
201
202     /* fade in animator */
203     setProperty( "opacity", 1.0 );
204     fadeAnimation = new QPropertyAnimation( this, "opacity", this );
205     fadeAnimation->setDuration( 1000 );
206     fadeAnimation->setStartValue( 0.0 );
207     fadeAnimation->setEndValue( 1.0 );
208     fadeAnimation->setEasingCurve( QEasingCurve::OutSine );
209     CONNECT( fadeAnimation, valueChanged( const QVariant & ),
210              this, update() );
211
212     CONNECT( THEMIM->getIM(), artChanged( QString ),
213              this, updateArt( const QString& ) );
214 }
215
216 void BackgroundWidget::updateArt( const QString& url )
217 {
218     if ( !url.isEmpty() )
219     {
220         pixmapUrl = url;
221     }
222     else
223     {   /* Xmas joke */
224         if( QDate::currentDate().dayOfYear() >= QT_XMAS_JOKE_DAY && var_InheritBool( p_intf, "qt-icon-change" ) )
225             pixmapUrl = QString( ":/logo/vlc128-xmas.png" );
226         else
227             pixmapUrl = QString( ":/logo/vlc128.png" );
228     }
229     update();
230 }
231
232 void BackgroundWidget::showEvent( QShowEvent * e )
233 {
234     Q_UNUSED( e );
235     if ( b_withart ) fadeAnimation->start();
236 }
237
238 void BackgroundWidget::paintEvent( QPaintEvent *e )
239 {
240     if ( !b_withart )
241     {
242         /* we just want background autofill */
243         QWidget::paintEvent( e );
244         return;
245     }
246
247     int i_maxwidth, i_maxheight;
248     QPixmap pixmap = QPixmap( pixmapUrl );
249     QPainter painter(this);
250     QBitmap pMask;
251     float f_alpha = 1.0;
252
253     i_maxwidth  = __MIN( maximumWidth(), width() ) - MARGIN * 2;
254     i_maxheight = __MIN( maximumHeight(), height() ) - MARGIN * 2;
255
256     painter.setOpacity( property( "opacity" ).toFloat() );
257
258     if ( height() > MARGIN * 2 )
259     {
260         /* Scale down the pixmap if the widget is too small */
261         if( pixmap.width() > i_maxwidth || pixmap.height() > i_maxheight )
262         {
263             pixmap = pixmap.scaled( i_maxwidth, i_maxheight,
264                             Qt::KeepAspectRatio, Qt::SmoothTransformation );
265         }
266         else
267         if ( b_expandPixmap &&
268              pixmap.width() < width() && pixmap.height() < height() )
269         {
270             /* Scale up the pixmap to fill widget's size */
271             f_alpha = ( (float) pixmap.height() / (float) height() );
272             pixmap = pixmap.scaled(
273                     width() - MARGIN * 2,
274                     height() - MARGIN * 2,
275                     Qt::KeepAspectRatio,
276                     ( f_alpha < .2 )? /* Don't waste cpu when not visible */
277                         Qt::SmoothTransformation:
278                         Qt::FastTransformation
279                     );
280             /* Non agressive alpha compositing when sizing up */
281             pMask = QBitmap( pixmap.width(), pixmap.height() );
282             pMask.fill( QColor::fromRgbF( 1.0, 1.0, 1.0, f_alpha ) );
283             pixmap.setMask( pMask );
284         }
285
286         painter.drawPixmap(
287                 MARGIN + ( i_maxwidth - pixmap.width() ) /2,
288                 MARGIN + ( i_maxheight - pixmap.height() ) /2,
289                 pixmap);
290     }
291     QWidget::paintEvent( e );
292 }
293
294 void BackgroundWidget::contextMenuEvent( QContextMenuEvent *event )
295 {
296     VLCMenuBar::PopupMenu( p_intf, true );
297     event->accept();
298 }
299
300 #if 0
301 #include <QPushButton>
302 #include <QHBoxLayout>
303
304 /**********************************************************************
305  * Visualization selector panel
306  **********************************************************************/
307 VisualSelector::VisualSelector( intf_thread_t *_p_i ) :
308                                 QFrame( NULL ), p_intf( _p_i )
309 {
310     QHBoxLayout *layout = new QHBoxLayout( this );
311     layout->setMargin( 0 );
312     QPushButton *prevButton = new QPushButton( "Prev" );
313     QPushButton *nextButton = new QPushButton( "Next" );
314     layout->addWidget( prevButton );
315     layout->addWidget( nextButton );
316
317     layout->addStretch( 10 );
318     layout->addWidget( new QLabel( qtr( "Current visualization" ) ) );
319
320     current = new QLabel( qtr( "None" ) );
321     layout->addWidget( current );
322
323     BUTTONACT( prevButton, prev() );
324     BUTTONACT( nextButton, next() );
325
326     setLayout( layout );
327     setMaximumHeight( 35 );
328 }
329
330 VisualSelector::~VisualSelector()
331 {}
332
333 void VisualSelector::prev()
334 {
335     char *psz_new = aout_VisualPrev( p_intf );
336     if( psz_new )
337     {
338         current->setText( qfu( psz_new ) );
339         free( psz_new );
340     }
341 }
342
343 void VisualSelector::next()
344 {
345     char *psz_new = aout_VisualNext( p_intf );
346     if( psz_new )
347     {
348         current->setText( qfu( psz_new ) );
349         free( psz_new );
350     }
351 }
352 #endif
353
354 SpeedLabel::SpeedLabel( intf_thread_t *_p_intf, QWidget *parent )
355            : QLabel( parent ), p_intf( _p_intf )
356 {
357     tooltipStringPattern = qtr( "Current playback speed: %1\nClick to adjust" );
358
359     /* Create the Speed Control Widget */
360     speedControl = new SpeedControlWidget( p_intf, this );
361     speedControlMenu = new QMenu( this );
362
363     QWidgetAction *widgetAction = new QWidgetAction( speedControl );
364     widgetAction->setDefaultWidget( speedControl );
365     speedControlMenu->addAction( widgetAction );
366
367     /* Change the SpeedRate in the Label */
368     CONNECT( THEMIM->getIM(), rateChanged( float ), this, setRate( float ) );
369
370     DCONNECT( THEMIM, inputChanged( input_thread_t * ),
371               speedControl, activateOnState() );
372
373     setFrameStyle( QFrame::StyledPanel | QFrame::Raised );
374     setLineWidth( 1 );
375
376     setRate( var_InheritFloat( THEPL, "rate" ) );
377 }
378
379 SpeedLabel::~SpeedLabel()
380 {
381     delete speedControl;
382     delete speedControlMenu;
383 }
384
385 /****************************************************************************
386  * Small right-click menu for rate control
387  ****************************************************************************/
388
389 void SpeedLabel::showSpeedMenu( QPoint pos )
390 {
391     speedControlMenu->exec( QCursor::pos() - pos
392                             + QPoint( -70 + width()/2, height() ) );
393 }
394
395 void SpeedLabel::setRate( float rate )
396 {
397     QString str;
398     str.setNum( rate, 'f', 2 );
399     str.append( "x" );
400     setText( str );
401     setToolTip( tooltipStringPattern.arg( str ) );
402     speedControl->updateControls( rate );
403 }
404
405 /**********************************************************************
406  * Speed control widget
407  **********************************************************************/
408 SpeedControlWidget::SpeedControlWidget( intf_thread_t *_p_i, QWidget *_parent )
409                     : QFrame( _parent ), p_intf( _p_i )
410 {
411     QSizePolicy sizePolicy( QSizePolicy::Fixed, QSizePolicy::Maximum );
412     sizePolicy.setHorizontalStretch( 0 );
413     sizePolicy.setVerticalStretch( 0 );
414
415     speedSlider = new QSlider( this );
416     speedSlider->setSizePolicy( sizePolicy );
417     speedSlider->setMinimumSize( QSize( 140, 20 ) );
418     speedSlider->setOrientation( Qt::Horizontal );
419     speedSlider->setTickPosition( QSlider::TicksBelow );
420
421     speedSlider->setRange( -34, 34 );
422     speedSlider->setSingleStep( 1 );
423     speedSlider->setPageStep( 1 );
424     speedSlider->setTickInterval( 17 );
425
426     CONNECT( speedSlider, valueChanged( int ), this, updateRate( int ) );
427
428     QToolButton *normalSpeedButton = new QToolButton( this );
429     normalSpeedButton->setMaximumSize( QSize( 26, 16 ) );
430     normalSpeedButton->setAutoRaise( true );
431     normalSpeedButton->setText( "1x" );
432     normalSpeedButton->setToolTip( qtr( "Revert to normal play speed" ) );
433
434     CONNECT( normalSpeedButton, clicked(), this, resetRate() );
435
436     QToolButton *slowerButton = new QToolButton( this );
437     slowerButton->setMaximumSize( QSize( 26, 16 ) );
438     slowerButton->setAutoRaise( true );
439     slowerButton->setToolTip( tooltipL[SLOWER_BUTTON] );
440     slowerButton->setIcon( QIcon( iconL[SLOWER_BUTTON] ) );
441     CONNECT( slowerButton, clicked(), THEMIM->getIM(), slower() );
442
443     QToolButton *fasterButton = new QToolButton( this );
444     fasterButton->setMaximumSize( QSize( 26, 16 ) );
445     fasterButton->setAutoRaise( true );
446     fasterButton->setToolTip( tooltipL[FASTER_BUTTON] );
447     fasterButton->setIcon( QIcon( iconL[FASTER_BUTTON] ) );
448     CONNECT( fasterButton, clicked(), THEMIM->getIM(), faster() );
449
450 /*    spinBox = new QDoubleSpinBox();
451     spinBox->setDecimals( 2 );
452     spinBox->setMaximum( 32 );
453     spinBox->setMinimum( 0.03F );
454     spinBox->setSingleStep( 0.10F );
455     spinBox->setAlignment( Qt::AlignRight );
456
457     CONNECT( spinBox, valueChanged( double ), this, updateSpinBoxRate( double ) ); */
458
459     QGridLayout* speedControlLayout = new QGridLayout( this );
460     speedControlLayout->addWidget( speedSlider, 0, 0, 1, 3 );
461     speedControlLayout->addWidget( slowerButton, 1, 0 );
462     speedControlLayout->addWidget( normalSpeedButton, 1, 1, 1, 1, Qt::AlignRight );
463     speedControlLayout->addWidget( fasterButton, 1, 2, 1, 1, Qt::AlignRight );
464     //speedControlLayout->addWidget( spinBox );
465     speedControlLayout->setContentsMargins( 0, 0, 0, 0 );
466     speedControlLayout->setSpacing( 0 );
467
468     lastValue = 0;
469
470     activateOnState();
471 }
472
473 void SpeedControlWidget::activateOnState()
474 {
475     speedSlider->setEnabled( THEMIM->getIM()->hasInput() );
476     //spinBox->setEnabled( THEMIM->getIM()->hasInput() );
477 }
478
479 void SpeedControlWidget::updateControls( float rate )
480 {
481     if( speedSlider->isSliderDown() )
482     {
483         //We don't want to change anything if the user is using the slider
484         return;
485     }
486
487     double value = 17 * log( rate ) / log( 2. );
488     int sliderValue = (int) ( ( value > 0 ) ? value + .5 : value - .5 );
489
490     if( sliderValue < speedSlider->minimum() )
491     {
492         sliderValue = speedSlider->minimum();
493     }
494     else if( sliderValue > speedSlider->maximum() )
495     {
496         sliderValue = speedSlider->maximum();
497     }
498     lastValue = sliderValue;
499
500     speedSlider->setValue( sliderValue );
501     //spinBox->setValue( rate );
502 }
503
504 void SpeedControlWidget::updateRate( int sliderValue )
505 {
506     if( sliderValue == lastValue )
507         return;
508
509     double speed = pow( 2, (double)sliderValue / 17 );
510     int rate = INPUT_RATE_DEFAULT / speed;
511
512     THEMIM->getIM()->setRate(rate);
513     //spinBox->setValue( var_InheritFloat( THEPL, "rate" ) );
514 }
515
516 void SpeedControlWidget::updateSpinBoxRate( double r )
517 {
518     var_SetFloat( THEPL, "rate", r );
519 }
520
521 void SpeedControlWidget::resetRate()
522 {
523     THEMIM->getIM()->setRate( INPUT_RATE_DEFAULT );
524 }
525
526 CoverArtLabel::CoverArtLabel( QWidget *parent, intf_thread_t *_p_i )
527     : QLabel( parent ), p_intf( _p_i ), p_item( NULL )
528 {
529     setContextMenuPolicy( Qt::ActionsContextMenu );
530     CONNECT( THEMIM->getIM(), artChanged( input_item_t * ),
531              this, showArtUpdate( input_item_t * ) );
532
533     setMinimumHeight( 128 );
534     setMinimumWidth( 128 );
535     setScaledContents( false );
536     setAlignment( Qt::AlignCenter );
537
538     QAction *action = new QAction( qtr( "Download cover art" ), this );
539     CONNECT( action, triggered(), this, askForUpdate() );
540     addAction( action );
541
542     action = new QAction( qtr( "Add cover art from file" ), this );
543     CONNECT( action, triggered(), this, setArtFromFile() );
544     addAction( action );
545
546     p_item = THEMIM->currentInputItem();
547     if( p_item )
548         showArtUpdate( p_item );
549     else
550         showArtUpdate( "" );
551 }
552
553 CoverArtLabel::~CoverArtLabel()
554 {
555     QList< QAction* > artActions = actions();
556     foreach( QAction *act, artActions )
557         removeAction( act );
558     if ( p_item ) vlc_gc_decref( p_item );
559 }
560
561 void CoverArtLabel::setItem( input_item_t *_p_item )
562 {
563     if ( p_item ) vlc_gc_decref( p_item );
564     p_item = _p_item;
565     if ( p_item ) vlc_gc_incref( p_item );
566 }
567
568 void CoverArtLabel::showArtUpdate( const QString& url )
569 {
570     QPixmap pix;
571     if( !url.isEmpty() && pix.load( url ) )
572     {
573         pix = pix.scaled( minimumWidth(), minimumHeight(),
574                           Qt::KeepAspectRatioByExpanding,
575                           Qt::SmoothTransformation );
576     }
577     else
578     {
579         pix = QPixmap( ":/noart.png" );
580     }
581     setPixmap( pix );
582 }
583
584 void CoverArtLabel::showArtUpdate( input_item_t *_p_item )
585 {
586     /* not for me */
587     if ( _p_item != p_item )
588         return;
589
590     QString url;
591     if ( _p_item ) url = THEMIM->getIM()->decodeArtURL( _p_item );
592     showArtUpdate( url );
593 }
594
595 void CoverArtLabel::askForUpdate()
596 {
597     THEMIM->getIM()->requestArtUpdate( p_item );
598 }
599
600 void CoverArtLabel::setArtFromFile()
601 {
602     if( !p_item )
603         return;
604
605     QString filePath = QFileDialog::getOpenFileName( this, qtr( "Choose Cover Art" ),
606         p_intf->p_sys->filepath, qtr( "Image Files (*.gif *.jpg *.jpeg *.png)" ) );
607
608     if( filePath.isEmpty() )
609         return;
610
611     QString fileUrl = QUrl::fromLocalFile( filePath ).toString();
612
613     THEMIM->getIM()->setArt( p_item, fileUrl );
614 }
615
616 TimeLabel::TimeLabel( intf_thread_t *_p_intf, TimeLabel::Display _displayType  )
617     : ClickableQLabel(), p_intf( _p_intf ), bufTimer( new QTimer(this) ),
618       buffering( false ), showBuffering(false), bufVal( -1 ), displayType( _displayType )
619 {
620     b_remainingTime = false;
621     if( _displayType != TimeLabel::Elapsed )
622         b_remainingTime = getSettings()->value( "MainWindow/ShowRemainingTime", false ).toBool();
623     switch( _displayType ) {
624         case TimeLabel::Elapsed:
625             setText( " --:-- " );
626             setToolTip( qtr("Elapsed time") );
627             break;
628         case TimeLabel::Remaining:
629             setText( " --:-- " );
630             setToolTip( qtr("Total/Remaining time")
631                         + QString("\n-")
632                         + qtr("Click to toggle between total and remaining time")
633                       );
634             break;
635         case TimeLabel::Both:
636             setText( " --:--/--:-- " );
637             setToolTip( QString( "- " )
638                 + qtr( "Click to toggle between elapsed and remaining time" )
639                 + QString( "\n- " )
640                 + qtr( "Double click to jump to a chosen time position" ) );
641             break;
642     }
643     setAlignment( Qt::AlignRight | Qt::AlignVCenter );
644
645     bufTimer->setSingleShot( true );
646
647     CONNECT( THEMIM->getIM(), positionUpdated( float, int64_t, int ),
648               this, setDisplayPosition( float, int64_t, int ) );
649     CONNECT( THEMIM->getIM(), cachingChanged( float ),
650               this, updateBuffering( float ) );
651     CONNECT( bufTimer, timeout(), this, updateBuffering() );
652
653     setStyleSheet( "padding-left: 4px; padding-right: 4px;" );
654 }
655
656 void TimeLabel::setDisplayPosition( float pos, int64_t t, int length )
657 {
658     showBuffering = false;
659     bufTimer->stop();
660
661     if( pos == -1.f )
662     {
663         setMinimumSize( QSize( 0, 0 ) );
664         if( displayType == TimeLabel::Both )
665             setText( "--:--/--:--" );
666         else
667             setText( "--:--" );
668         return;
669     }
670
671     int time = t / 1000000;
672
673     secstotimestr( psz_length, length );
674     secstotimestr( psz_time, ( b_remainingTime && length ) ? length - time
675                                                            : time );
676
677     // compute the minimum size that will be required for the psz_length
678     // and use it to enforce a minimal size to avoid "dancing" widgets
679     QSize minsize( 0, 0 );
680     if ( length > 0 )
681     {
682         QMargins margins = contentsMargins();
683         minsize += QSize(
684                   fontMetrics().size( 0, QString( psz_length ), 0, 0 ).width(),
685                   sizeHint().height()
686                 );
687         minsize += QSize( margins.left() + margins.right() + 8, 0 ); /* +padding */
688
689         if ( b_remainingTime )
690             minsize += QSize( fontMetrics().size( 0, "-", 0, 0 ).width(), 0 );
691     }
692
693     switch( displayType )
694     {
695         case TimeLabel::Elapsed:
696             setMinimumSize( minsize );
697             setText( QString( psz_time ) );
698             break;
699         case TimeLabel::Remaining:
700             if( b_remainingTime )
701             {
702                 setMinimumSize( minsize );
703                 setText( QString("-") + QString( psz_time ) );
704             }
705             else
706             {
707                 setMinimumSize( QSize( 0, 0 ) );
708                 setText( QString( psz_length ) );
709             }
710             break;
711         case TimeLabel::Both:
712         default:
713             QString timestr = QString( "%1%2/%3" )
714             .arg( QString( (b_remainingTime && length) ? "-" : "" ) )
715             .arg( QString( psz_time ) )
716             .arg( QString( ( !length && time ) ? "--:--" : psz_length ) );
717
718             setText( timestr );
719             break;
720     }
721     cachedLength = length;
722 }
723
724 void TimeLabel::setDisplayPosition( float pos )
725 {
726     if( pos == -1.f || cachedLength == 0 )
727     {
728         setText( " --:--/--:-- " );
729         return;
730     }
731
732     int time = pos * cachedLength;
733     secstotimestr( psz_time,
734                    ( b_remainingTime && cachedLength ?
735                    cachedLength - time : time ) );
736     QString timestr = QString( "%1%2/%3" )
737         .arg( QString( (b_remainingTime && cachedLength) ? "-" : "" ) )
738         .arg( QString( psz_time ) )
739         .arg( QString( ( !cachedLength && time ) ? "--:--" : psz_length ) );
740
741     setText( timestr );
742 }
743
744
745 void TimeLabel::toggleTimeDisplay()
746 {
747     b_remainingTime = !b_remainingTime;
748     getSettings()->setValue( "MainWindow/ShowRemainingTime", b_remainingTime );
749 }
750
751
752 void TimeLabel::updateBuffering( float _buffered )
753 {
754     bufVal = _buffered;
755     if( !buffering || bufVal == 0 )
756     {
757         showBuffering = false;
758         buffering = true;
759         bufTimer->start(200);
760     }
761     else if( bufVal == 1 )
762     {
763         showBuffering = buffering = false;
764         bufTimer->stop();
765     }
766     update();
767 }
768
769 void TimeLabel::updateBuffering()
770 {
771     showBuffering = true;
772     update();
773 }
774
775 void TimeLabel::paintEvent( QPaintEvent* event )
776 {
777     if( showBuffering )
778     {
779         QRect r( rect() );
780         r.setLeft( r.width() * bufVal );
781         QPainter p( this );
782         p.setOpacity( 0.4 );
783         p.fillRect( r, palette().color( QPalette::Highlight ) );
784     }
785     QLabel::paintEvent( event );
786 }