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 "components/interface_widgets.hpp"
32 33
#include "dialogs_provider.hpp"
#include "util/customwidgets.hpp"               // qtEventToVLCKey, QVLCStackedWidget
34 35 36 37

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

#include <vlc_vout.h>
38

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

53 54 55
#ifdef Q_WS_X11
# include <X11/Xlib.h>
# include <qx11info_x11.h>
56 57 58 59 60 61 62 63 64
static void videoSync( void )
{
    /* 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 );
}
#else
# define videoSync() (void)0
65
#endif
66

67
#include <math.h>
68
#include <assert.h>
69

70 71 72 73 74 75
class ReparentableWidget : public QWidget
{
private:
    VideoWidget *owner;
public:
    ReparentableWidget( VideoWidget *owner ) : owner( owner )
76
    {}
77 78
};

79 80 81 82
/**********************************************************************
 * Video Widget. A simple frame on which video is drawn
 * This class handles resize issues
 **********************************************************************/
83

84 85 86 87
VideoWidget::VideoWidget( intf_thread_t *_p_i )
    : QFrame( NULL )
      , p_intf( _p_i )
      , reparentable( NULL )
88
{
89
    /* Set the policy to expand in both directions */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
90
    // setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
91

92 93 94
    layout = new QHBoxLayout( this );
    layout->setContentsMargins( 0, 0, 0, 0 );
    setLayout( layout );
95 96 97 98
}

VideoWidget::~VideoWidget()
{
99
    /* Ensure we are not leaking the video output. This would crash. */
100
    assert( reparentable == NULL );
101 102
}

103
/**
104
 * Request the video to avoid the conflicts
105
 **/
106
WId VideoWidget::request( int *pi_x, int *pi_y,
107 108
                          unsigned int *pi_width, unsigned int *pi_height,
                          bool b_keep_size )
109
{
110
    msg_Dbg( p_intf, "Video was requested %i, %i", *pi_x, *pi_y );
111

112 113 114 115 116
    if( reparentable != NULL )
    {
        msg_Dbg( p_intf, "embedded video already in use" );
        return NULL;
    }
117 118 119 120 121 122
    if( b_keep_size )
    {
        *pi_width  = size().width();
        *pi_height = size().height();
    }

123 124 125 126 127
    /* The Qt4 UI needs a fixed a widget ("this"), so that the parent layout is
     * not messed up when we the video is reparented. Hence, we create an extra
     * reparentable widget, that will be within the VideoWidget in windowed
     * mode, and within the root window (NULL parent) in full-screen mode.
     */
128
    reparentable = new ReparentableWidget( this );
129
    reparentable->installEventFilter(this );
130 131 132 133 134 135 136 137 138 139 140
    QLayout *innerLayout = new QHBoxLayout( reparentable );
    innerLayout->setContentsMargins( 0, 0, 0, 0 );

    /* 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. */
    QWidget *stable = new QWidget();
    QPalette plt = palette();
    plt.setColor( QPalette::Window, Qt::black );
    stable->setPalette( plt );
    stable->setAutoFillBackground(true);
141 142 143
    /* Indicates that the widget wants to draw directly onto the screen.
       Widgets with this attribute set do not participate in composition
       management */
144 145 146
    /* 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
147
    stable->setAttribute( Qt::WA_PaintOnScreen, true );
148
#endif
149 150 151 152 153

    innerLayout->addWidget( stable );

    layout->addWidget( reparentable );

154
#ifdef Q_WS_X11
155
    /* HACK: Only one X11 client can subscribe to mouse button press events.
156
     * VLC currently handles those in the video display.
157
     * Force Qt4 to unsubscribe from mouse press and release events. */
158 159 160 161 162
    Display *dpy = QX11Info::display();
    Window w = stable->winId();
    XWindowAttributes attr;

    XGetWindowAttributes( dpy, w, &attr );
163 164
    attr.your_event_mask &= ~(ButtonPressMask|ButtonReleaseMask);
    XSelectInput( dpy, w, attr.your_event_mask );
165
#endif
166
    videoSync();
167
#ifndef NDEBUG
168 169
    msg_Dbg( p_intf, "embedded video ready (handle %p)",
             (void *)stable->winId() );
170
#endif
171
    return stable->winId();
172 173
}

174
/* Set the Widget to the correct Size */
175
/* Function has to be called by the parent
Rémi Denis-Courmont's avatar
typo  
Rémi Denis-Courmont committed
176
   Parent has to care about resizing itself */
177
void VideoWidget::SetSizing( unsigned int w, unsigned int h )
178
{
179 180
    if (reparentable->windowState() & Qt::WindowFullScreen )
        return;
181
    if( !isVisible() ) show();
182 183
    resize( w, h );
    emit sizeChanged( w, h );
184 185 186 187 188 189 190
    /* 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
     */
    if( size().width() == w && size().height() == h )
        updateGeometry();
191
    videoSync();
192 193
}

194
void VideoWidget::SetFullScreen( bool b_fs, bool b_ontop )
195 196 197
{
    const Qt::WindowStates curstate = reparentable->windowState();
    Qt::WindowStates newstate = curstate;
198
    Qt::WindowFlags  newflags = reparentable->windowFlags();
199

200

201
    if( b_fs )
202
    {
203
        newstate |= Qt::WindowFullScreen;
204 205
        if( b_ontop )
            newflags |= Qt::WindowStaysOnTopHint;
206
    }
207
    else
208
    {
209
        newstate &= ~Qt::WindowFullScreen;
210
        newflags &= ~Qt::WindowStaysOnTopHint;
211
    }
212 213 214 215 216
    if( newstate == curstate )
        return; /* no changes needed */

    if( b_fs )
    {   /* Go full-screen */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
217
        int numscreen = var_InheritInteger( p_intf, "qt-fullscreen-screennumber" );
218 219 220 221 222 223 224
        /* if user hasn't defined screennumber, or screennumber that is bigger
         * than current number of screens, take screennumber where current interface
         * is
         */
        if( numscreen == -1 || numscreen > QApplication::desktop()->numScreens() )
            numscreen = QApplication::desktop()->screenNumber( p_intf->p_sys->p_mi );

225 226 227 228 229 230 231 232
        QRect screenres = QApplication::desktop()->screenGeometry( numscreen );

        /* To be sure window is on proper-screen in xinerama */
        if( !screenres.contains( reparentable->pos() ) )
        {
            msg_Dbg( p_intf, "Moving video to correct screen");
            reparentable->move( QPoint( screenres.x(), screenres.y() ) );
        }
233 234
        reparentable->setParent( NULL, newflags );
        reparentable->setWindowState( newstate );
235 236 237 238 239 240

        /* FIXME: inherit from the vout window, not the interface */
        char *title = var_InheritString( p_intf, "video-title" );
        reparentable->setWindowTitle( qfu(title ? title : _("Video")) );
        free( title );

241
        reparentable->show();
242 243
        reparentable->activateWindow();
        reparentable->raise();
244 245 246
    }
    else
    {   /* Go windowed */
247
        reparentable->setWindowFlags( newflags );
248
        reparentable->setWindowState( newstate );
249
        layout->addWidget( reparentable );
250
    }
251
    videoSync();
252 253
}

254
void VideoWidget::release( void )
255
{
Rémi Denis-Courmont's avatar
Typo  
Rémi Denis-Courmont committed
256
    msg_Dbg( p_intf, "Video is not needed anymore" );
257
    //layout->removeWidget( reparentable );
258

259
    reparentable->deleteLater();
260
    reparentable = NULL;
261
    updateGeometry();
262
    hide();
263
}
264

265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
#undef KeyPress
bool VideoWidget::eventFilter(QObject *obj, QEvent *event)
{
    if( obj == reparentable )
    {
        if (event->type() == QEvent::Close)
        {
            THEDP->quit();
            return true;
        }
        else if( event->type() == QEvent::KeyPress )
        {
            emit keyPressed( static_cast<QKeyEvent *>(event) );
            return true;
        }
280 281 282 283 284 285
        else if( event->type() == QEvent::Wheel )
        {
            int i_vlckey = qtWheelEventToVLCKey( static_cast<QWheelEvent *>(event) );
            var_SetInteger( p_intf->p_libvlc, "key-pressed", i_vlckey );
            return true;
        }
286 287 288 289
    }
    return false;
}

290 291
/**********************************************************************
 * Background Widget. Show a simple image background. Currently,
292
 * it's album art if present or cone.
293
 **********************************************************************/
294

295
BackgroundWidget::BackgroundWidget( intf_thread_t *_p_i )
François Cartegnie's avatar
François Cartegnie committed
296
                 :QWidget( NULL ), p_intf( _p_i ), b_expandPixmap( false )
297
{
298
    /* A dark background */
Clément Stenac's avatar
Clément Stenac committed
299
    setAutoFillBackground( true );
300
    QPalette plt = palette();
301 302 303 304
    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
305 306 307
    /* Init the cone art */
    updateArt( "" );

308
    CONNECT( THEMIM->getIM(), artChanged( QString ),
309
             this, updateArt( const QString& ) );
310 311
}

312
void BackgroundWidget::updateArt( const QString& url )
313
{
François Cartegnie's avatar
François Cartegnie committed
314
    if ( !url.isEmpty() )
Ilkka Ollakka's avatar
 
Ilkka Ollakka committed
315
    {
François Cartegnie's avatar
François Cartegnie committed
316 317 318 319
        pixmapUrl = url;
    }
    else
    {   /* Xmas joke */
320
        if( QDate::currentDate().dayOfYear() >= 354 )
François Cartegnie's avatar
François Cartegnie committed
321
            pixmapUrl = QString( ":/logo/vlc128-christmas.png" );
322
        else
François Cartegnie's avatar
François Cartegnie committed
323
            pixmapUrl = QString( ":/logo/vlc128.png" );
Ilkka Ollakka's avatar
 
Ilkka Ollakka committed
324
    }
325
    update();
François Cartegnie's avatar
François Cartegnie committed
326 327 328 329 330 331 332 333 334 335 336 337 338 339
}

void BackgroundWidget::paintEvent( QPaintEvent *e )
{
    int i_maxwidth, i_maxheight;
    QPixmap pixmap = QPixmap( pixmapUrl );
    QPainter painter(this);
    QBitmap pMask;
    float f_alpha = 1.0;

    i_maxwidth = std::min( maximumWidth(), width() ) - MARGIN * 2;
    i_maxheight = std::min( maximumHeight(), height() ) - MARGIN * 2;

    if ( height() > MARGIN * 2 )
Ilkka Ollakka's avatar
 
Ilkka Ollakka committed
340
    {
François Cartegnie's avatar
François Cartegnie committed
341 342 343 344 345 346 347 348 349
        /* 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() )
350
        {
François Cartegnie's avatar
François Cartegnie committed
351 352 353 354 355 356 357 358 359 360 361 362 363 364
            /* 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 );
365 366
        }

François Cartegnie's avatar
François Cartegnie committed
367 368 369 370
        painter.drawPixmap(
                MARGIN + ( i_maxwidth - pixmap.width() ) /2,
                MARGIN + ( i_maxheight - pixmap.height() ) /2,
                pixmap);
Ilkka Ollakka's avatar
 
Ilkka Ollakka committed
371
    }
François Cartegnie's avatar
François Cartegnie committed
372
    QWidget::paintEvent( e );
373 374
}

375 376
void BackgroundWidget::contextMenuEvent( QContextMenuEvent *event )
{
Erwan Tulou's avatar
Erwan Tulou committed
377
    QVLCMenu::PopupMenu( p_intf, true );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
378
    event->accept();
379
}
380

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
381
#if 0
382 383 384
#include <QPushButton>
#include <QHBoxLayout>

Clément Stenac's avatar
Clément Stenac committed
385 386 387 388
/**********************************************************************
 * Visualization selector panel
 **********************************************************************/
VisualSelector::VisualSelector( intf_thread_t *_p_i ) :
389
                                QFrame( NULL ), p_intf( _p_i )
Clément Stenac's avatar
Clément Stenac committed
390 391
{
    QHBoxLayout *layout = new QHBoxLayout( this );
392
    layout->setMargin( 0 );
Clément Stenac's avatar
Clément Stenac committed
393
    QPushButton *prevButton = new QPushButton( "Prev" );
394
    QPushButton *nextButton = new QPushButton( "Next" );
Clément Stenac's avatar
Clément Stenac committed
395 396
    layout->addWidget( prevButton );
    layout->addWidget( nextButton );
Clément Stenac's avatar
Clément Stenac committed
397

398
    layout->addStretch( 10 );
399
    layout->addWidget( new QLabel( qtr( "Current visualization" ) ) );
Clément Stenac's avatar
Clément Stenac committed
400 401 402 403 404 405 406

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

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

Clément Stenac's avatar
Clément Stenac committed
407
    setLayout( layout );
408
    setMaximumHeight( 35 );
Clément Stenac's avatar
Clément Stenac committed
409 410 411
}

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

Clément Stenac's avatar
Clément Stenac committed
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
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
433
#endif
Clément Stenac's avatar
Clément Stenac committed
434

435 436
SpeedLabel::SpeedLabel( intf_thread_t *_p_intf, QWidget *parent )
           : QLabel( parent ), p_intf( _p_intf )
437
{
438
    tooltipStringPattern = qtr( "Current playback speed: %1\nClick to adjust" );
439 440

    /* Create the Speed Control Widget */
441
    speedControl = new SpeedControlWidget( p_intf, this );
442 443 444 445 446 447 448
    speedControlMenu = new QMenu( this );

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

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

451 452
    DCONNECT( THEMIM, inputChanged( input_thread_t * ),
              speedControl, activateOnState() );
453
    setRate( var_InheritFloat( p_intf, "rate" ) );
454
}
455

456 457
SpeedLabel::~SpeedLabel()
{
458 459
    delete speedControl;
    delete speedControlMenu;
460
}
461

462 463 464
/****************************************************************************
 * Small right-click menu for rate control
 ****************************************************************************/
465

466 467 468 469 470 471
void SpeedLabel::showSpeedMenu( QPoint pos )
{
    speedControlMenu->exec( QCursor::pos() - pos
                          + QPoint( 0, height() ) );
}

472
void SpeedLabel::setRate( float rate )
473 474
{
    QString str;
475
    str.setNum( rate, 'f', 2 );
476 477
    str.append( "x" );
    setText( str );
478
    setToolTip( tooltipStringPattern.arg( str ) );
479 480 481
    speedControl->updateControls( rate );
}

482 483 484
/**********************************************************************
 * Speed control widget
 **********************************************************************/
485 486
SpeedControlWidget::SpeedControlWidget( intf_thread_t *_p_i, QWidget *_parent )
                    : QFrame( _parent ), p_intf( _p_i )
487
{
488
    QSizePolicy sizePolicy( QSizePolicy::Maximum, QSizePolicy::Fixed );
489 490
    sizePolicy.setHorizontalStretch( 0 );
    sizePolicy.setVerticalStretch( 0 );
491

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
492
    speedSlider = new QSlider( this );
493 494 495 496 497
    speedSlider->setSizePolicy( sizePolicy );
    speedSlider->setMaximumSize( QSize( 80, 200 ) );
    speedSlider->setOrientation( Qt::Vertical );
    speedSlider->setTickPosition( QSlider::TicksRight );

498
    speedSlider->setRange( -34, 34 );
499 500
    speedSlider->setSingleStep( 1 );
    speedSlider->setPageStep( 1 );
501
    speedSlider->setTickInterval( 17 );
502

503
    CONNECT( speedSlider, valueChanged( int ), this, updateRate( int ) );
504

505
    QToolButton *normalSpeedButton = new QToolButton( this );
506
    normalSpeedButton->setMaximumSize( QSize( 26, 20 ) );
507
    normalSpeedButton->setAutoRaise( true );
508
    normalSpeedButton->setText( "1x" );
509
    normalSpeedButton->setToolTip( qtr( "Revert to normal play speed" ) );
510

511
    CONNECT( normalSpeedButton, clicked(), this, resetRate() );
512

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
513
    QVBoxLayout *speedControlLayout = new QVBoxLayout( this );
514
    speedControlLayout->setContentsMargins( 4, 4, 4, 4 );
515
    speedControlLayout->setSpacing( 4 );
516 517
    speedControlLayout->addWidget( speedSlider );
    speedControlLayout->addWidget( normalSpeedButton );
518

519 520
    lastValue = 0;

521 522
    activateOnState();
}
523

524
void SpeedControlWidget::activateOnState()
525
{
526
    speedSlider->setEnabled( THEMIM->getIM()->hasInput() );
527 528
}

529
void SpeedControlWidget::updateControls( float rate )
530
{
531 532 533 534 535
    if( speedSlider->isSliderDown() )
    {
        //We don't want to change anything if the user is using the slider
        return;
    }
536

537
    double value = 17 * log( rate ) / log( 2 );
538
    int sliderValue = (int) ( ( value > 0 ) ? value + .5 : value - .5 );
539

540
    if( sliderValue < speedSlider->minimum() )
541
    {
542
        sliderValue = speedSlider->minimum();
543
    }
544
    else if( sliderValue > speedSlider->maximum() )
545
    {
546
        sliderValue = speedSlider->maximum();
547
    }
548
    lastValue = sliderValue;
549

550
    speedSlider->setValue( sliderValue );
551 552 553 554
}

void SpeedControlWidget::updateRate( int sliderValue )
{
555 556 557
    if( sliderValue == lastValue )
        return;

558
    double speed = pow( 2, (double)sliderValue / 17 );
559
    int rate = INPUT_RATE_DEFAULT / speed;
560

561
    THEMIM->getIM()->setRate(rate);
562 563 564 565
}

void SpeedControlWidget::resetRate()
{
566
    THEMIM->getIM()->setRate( INPUT_RATE_DEFAULT );
567
}
568

569
CoverArtLabel::CoverArtLabel( QWidget *parent, intf_thread_t *_p_i )
570
              : QLabel( parent ), p_intf( _p_i )
571 572
{
    setContextMenuPolicy( Qt::ActionsContextMenu );
573
    CONNECT( this, updateRequested(), this, askForUpdate() );
574

575
    setMinimumHeight( 128 );
576 577
    setMinimumWidth( 128 );
    setMaximumHeight( 128 );
578
    setMaximumWidth( 128 );
579 580
    setScaledContents( false );
    setAlignment( Qt::AlignCenter );
581

582 583
    QList< QAction* > artActions = actions();
    QAction *action = new QAction( qtr( "Download cover art" ), this );
584
    CONNECT( action, triggered(), this, askForUpdate() );
585
    addAction( action );
586

587
    showArtUpdate( "" );
588 589
}

590 591 592 593 594 595 596
CoverArtLabel::~CoverArtLabel()
{
    QList< QAction* > artActions = actions();
    foreach( QAction *act, artActions )
        removeAction( act );
}

597
void CoverArtLabel::showArtUpdate( const QString& url )
598
{
599
    QPixmap pix;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
600
    if( !url.isEmpty() && pix.load( url ) )
601
    {
602
        pix = pix.scaled( maximumWidth(), maximumHeight(),
603 604
                          Qt::KeepAspectRatioByExpanding,
                          Qt::SmoothTransformation );
605 606 607
    }
    else
    {
608
        pix = QPixmap( ":/noart.png" );
609
    }
610
    setPixmap( pix );
611 612
}

613
void CoverArtLabel::askForUpdate()
614
{
615
    THEMIM->getIM()->requestArtUpdate();
616 617
}

618 619 620
TimeLabel::TimeLabel( intf_thread_t *_p_intf  )
    : QLabel(), p_intf( _p_intf ), bufTimer( new QTimer(this) ),
      buffering( false ), showBuffering(false), bufVal( -1 )
621
{
622 623 624
    b_remainingTime = false;
    setText( " --:--/--:-- " );
    setAlignment( Qt::AlignRight | Qt::AlignVCenter );
625 626 627 628
    setToolTip( QString( "- " )
        + qtr( "Click to toggle between elapsed and remaining time" )
        + QString( "\n- " )
        + qtr( "Double click to jump to a chosen time position" ) );
629
    bufTimer->setSingleShot( true );
630

631 632 633 634 635
    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() );
636 637
}

638
void TimeLabel::setDisplayPosition( float pos, int64_t t, int length )
639
{
640 641 642
    showBuffering = false;
    bufTimer->stop();

643
    if( pos == -1.f )
644 645 646 647
    {
        setText( " --:--/--:-- " );
        return;
    }
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
648

649
    int time = t / 1000000;
650

651 652 653 654
    secstotimestr( psz_length, length );
    secstotimestr( psz_time, ( b_remainingTime && length ) ? length - time
                                                           : time );

655 656 657 658
    QString timestr = QString( " %1%2/%3 " )
            .arg( QString( (b_remainingTime && length) ? "-" : "" ) )
            .arg( QString( psz_time ) )
            .arg( QString( ( !length && time ) ? "--:--" : psz_length ) );
659

JP Dinger's avatar
JP Dinger committed
660
    setText( timestr );
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676

    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 ) );
677 678 679 680
    QString timestr = QString( " %1%2/%3 " )
        .arg( QString( (b_remainingTime && cachedLength) ? "-" : "" ) )
        .arg( QString( psz_time ) )
        .arg( QString( ( !cachedLength && time ) ? "--:--" : psz_length ) );
681 682

    setText( timestr );
683 684
}

685

686 687 688 689
void TimeLabel::toggleTimeDisplay()
{
    b_remainingTime = !b_remainingTime;
}
690 691


692
void TimeLabel::updateBuffering( float _buffered )
693
{
694 695 696 697 698 699 700 701 702 703
    bufVal = _buffered;
    if( !buffering || bufVal == 0 )
    {
        showBuffering = false;
        buffering = true;
        bufTimer->start(200);
    }
    else if( bufVal == 1 )
    {
        showBuffering = buffering = false;
704
        bufTimer->stop();
705 706
    }
    update();
707
}
708

709
void TimeLabel::updateBuffering()
710
{
711 712
    showBuffering = true;
    update();
713 714
}

715
void TimeLabel::paintEvent( QPaintEvent* event )
716
{
717 718 719 720 721 722 723 724
    if( showBuffering )
    {
        QRect r( rect() );
        r.setLeft( r.width() * bufVal );
        QPainter p( this );
        p.setOpacity( 0.4 );
        p.fillRect( r, palette().color( QPalette::Highlight ) );
    }
725 726
    QLabel::paintEvent( event );
}