interface_widgets.cpp 21.2 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 147 148 149
    stable->setAttribute( Qt::WA_PaintOnScreen, true );

    innerLayout->addWidget( stable );

    layout->addWidget( reparentable );

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

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

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

190 191 192 193
void VideoWidget::SetFullScreen( bool b_fs )
{
    const Qt::WindowStates curstate = reparentable->windowState();
    Qt::WindowStates newstate = curstate;
194
    Qt::WindowFlags  newflags = reparentable->windowFlags();
195

196

197
    if( b_fs )
198
    {
199
        newstate |= Qt::WindowFullScreen;
200
        newflags |= Qt::WindowStaysOnTopHint;
201
    }
202
    else
203
    {
204
        newstate &= ~Qt::WindowFullScreen;
205
        newflags &= ~Qt::WindowStaysOnTopHint;
206
    }
207 208 209 210 211
    if( newstate == curstate )
        return; /* no changes needed */

    if( b_fs )
    {   /* Go full-screen */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
212
        int numscreen = var_InheritInteger( p_intf, "qt-fullscreen-screennumber" );
213 214 215 216 217 218 219
        /* 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 );

220 221 222 223 224 225 226 227
        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() ) );
        }
228 229
        reparentable->setParent( NULL, newflags );
        reparentable->setWindowState( newstate );
230 231 232 233 234 235

        /* 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 );

236 237 238 239
        reparentable->show();
    }
    else
    {   /* Go windowed */
240
        reparentable->setWindowFlags( newflags );
241
        reparentable->setWindowState( newstate );
242
        layout->addWidget( reparentable );
243
    }
244
    videoSync();
245 246
}

247
void VideoWidget::release( void )
248
{
Rémi Denis-Courmont's avatar
Typo  
Rémi Denis-Courmont committed
249
    msg_Dbg( p_intf, "Video is not needed anymore" );
250
    //layout->removeWidget( reparentable );
251

252
    reparentable->deleteLater();
253
    reparentable = NULL;
254
    updateGeometry();
255
    hide();
256
}
257

258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
#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;
        }
273 274 275 276 277 278
        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;
        }
279 280 281 282
    }
    return false;
}

283 284
/**********************************************************************
 * Background Widget. Show a simple image background. Currently,
285
 * it's album art if present or cone.
286
 **********************************************************************/
287

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

301
    CONNECT( THEMIM->getIM(), artChanged( QString ),
302
             this, updateArt( const QString& ) );
303 304
}

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

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
333
    {
François Cartegnie's avatar
François Cartegnie committed
334 335 336 337 338 339 340 341 342
        /* 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() )
343
        {
François Cartegnie's avatar
François Cartegnie committed
344 345 346 347 348 349 350 351 352 353 354 355 356 357
            /* 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 );
358 359
        }

François Cartegnie's avatar
François Cartegnie committed
360 361 362 363
        painter.drawPixmap(
                MARGIN + ( i_maxwidth - pixmap.width() ) /2,
                MARGIN + ( i_maxheight - pixmap.height() ) /2,
                pixmap);
Ilkka Ollakka's avatar
 
Ilkka Ollakka committed
364
    }
François Cartegnie's avatar
François Cartegnie committed
365
    QWidget::paintEvent( e );
366 367
}

368 369
void BackgroundWidget::contextMenuEvent( QContextMenuEvent *event )
{
Erwan Tulou's avatar
Erwan Tulou committed
370
    QVLCMenu::PopupMenu( p_intf, true );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
371
    event->accept();
372
}
373

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
374
#if 0
375 376 377
#include <QPushButton>
#include <QHBoxLayout>

Clément Stenac's avatar
Clément Stenac committed
378 379 380 381
/**********************************************************************
 * Visualization selector panel
 **********************************************************************/
VisualSelector::VisualSelector( intf_thread_t *_p_i ) :
382
                                QFrame( NULL ), p_intf( _p_i )
Clément Stenac's avatar
Clément Stenac committed
383 384
{
    QHBoxLayout *layout = new QHBoxLayout( this );
385
    layout->setMargin( 0 );
Clément Stenac's avatar
Clément Stenac committed
386
    QPushButton *prevButton = new QPushButton( "Prev" );
387
    QPushButton *nextButton = new QPushButton( "Next" );
Clément Stenac's avatar
Clément Stenac committed
388 389
    layout->addWidget( prevButton );
    layout->addWidget( nextButton );
Clément Stenac's avatar
Clément Stenac committed
390

391
    layout->addStretch( 10 );
392
    layout->addWidget( new QLabel( qtr( "Current visualization" ) ) );
Clément Stenac's avatar
Clément Stenac committed
393 394 395 396 397 398 399

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

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

Clément Stenac's avatar
Clément Stenac committed
400
    setLayout( layout );
401
    setMaximumHeight( 35 );
Clément Stenac's avatar
Clément Stenac committed
402 403 404
}

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

Clément Stenac's avatar
Clément Stenac committed
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
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
426
#endif
Clément Stenac's avatar
Clément Stenac committed
427

428 429
SpeedLabel::SpeedLabel( intf_thread_t *_p_intf, QWidget *parent )
           : QLabel( parent ), p_intf( _p_intf )
430
{
431
    tooltipStringPattern = qtr( "Current playback speed: %1\nClick to adjust" );
432 433

    /* Create the Speed Control Widget */
434
    speedControl = new SpeedControlWidget( p_intf, this );
435 436 437 438 439 440 441
    speedControlMenu = new QMenu( this );

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

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

444 445
    DCONNECT( THEMIM, inputChanged( input_thread_t * ),
              speedControl, activateOnState() );
446
    setRate( var_InheritFloat( p_intf, "rate" ) );
447
}
448

449 450
SpeedLabel::~SpeedLabel()
{
451 452
    delete speedControl;
    delete speedControlMenu;
453
}
454

455 456 457
/****************************************************************************
 * Small right-click menu for rate control
 ****************************************************************************/
458

459 460 461 462 463 464
void SpeedLabel::showSpeedMenu( QPoint pos )
{
    speedControlMenu->exec( QCursor::pos() - pos
                          + QPoint( 0, height() ) );
}

465
void SpeedLabel::setRate( float rate )
466 467
{
    QString str;
468
    str.setNum( rate, 'f', 2 );
469 470
    str.append( "x" );
    setText( str );
471
    setToolTip( tooltipStringPattern.arg( str ) );
472 473 474
    speedControl->updateControls( rate );
}

475 476 477
/**********************************************************************
 * Speed control widget
 **********************************************************************/
478 479
SpeedControlWidget::SpeedControlWidget( intf_thread_t *_p_i, QWidget *_parent )
                    : QFrame( _parent ), p_intf( _p_i )
480
{
481
    QSizePolicy sizePolicy( QSizePolicy::Maximum, QSizePolicy::Fixed );
482 483
    sizePolicy.setHorizontalStretch( 0 );
    sizePolicy.setVerticalStretch( 0 );
484

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
485
    speedSlider = new QSlider( this );
486 487 488 489 490
    speedSlider->setSizePolicy( sizePolicy );
    speedSlider->setMaximumSize( QSize( 80, 200 ) );
    speedSlider->setOrientation( Qt::Vertical );
    speedSlider->setTickPosition( QSlider::TicksRight );

491
    speedSlider->setRange( -34, 34 );
492 493
    speedSlider->setSingleStep( 1 );
    speedSlider->setPageStep( 1 );
494
    speedSlider->setTickInterval( 17 );
495

496
    CONNECT( speedSlider, valueChanged( int ), this, updateRate( int ) );
497

498
    QToolButton *normalSpeedButton = new QToolButton( this );
499
    normalSpeedButton->setMaximumSize( QSize( 26, 20 ) );
500
    normalSpeedButton->setAutoRaise( true );
501
    normalSpeedButton->setText( "1x" );
502
    normalSpeedButton->setToolTip( qtr( "Revert to normal play speed" ) );
503

504
    CONNECT( normalSpeedButton, clicked(), this, resetRate() );
505

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
506
    QVBoxLayout *speedControlLayout = new QVBoxLayout( this );
507
    speedControlLayout->setContentsMargins( 4, 4, 4, 4 );
508
    speedControlLayout->setSpacing( 4 );
509 510
    speedControlLayout->addWidget( speedSlider );
    speedControlLayout->addWidget( normalSpeedButton );
511

512 513
    lastValue = 0;

514 515
    activateOnState();
}
516

517
void SpeedControlWidget::activateOnState()
518
{
519
    speedSlider->setEnabled( THEMIM->getIM()->hasInput() );
520 521
}

522
void SpeedControlWidget::updateControls( float rate )
523
{
524 525 526 527 528
    if( speedSlider->isSliderDown() )
    {
        //We don't want to change anything if the user is using the slider
        return;
    }
529

530
    double value = 17 * log( rate ) / log( 2 );
531
    int sliderValue = (int) ( ( value > 0 ) ? value + .5 : value - .5 );
532

533
    if( sliderValue < speedSlider->minimum() )
534
    {
535
        sliderValue = speedSlider->minimum();
536
    }
537
    else if( sliderValue > speedSlider->maximum() )
538
    {
539
        sliderValue = speedSlider->maximum();
540
    }
541
    lastValue = sliderValue;
542

543
    speedSlider->setValue( sliderValue );
544 545 546 547
}

void SpeedControlWidget::updateRate( int sliderValue )
{
548 549 550
    if( sliderValue == lastValue )
        return;

551
    double speed = pow( 2, (double)sliderValue / 17 );
552
    int rate = INPUT_RATE_DEFAULT / speed;
553

554
    THEMIM->getIM()->setRate(rate);
555 556 557 558
}

void SpeedControlWidget::resetRate()
{
559
    THEMIM->getIM()->setRate( INPUT_RATE_DEFAULT );
560
}
561

562
CoverArtLabel::CoverArtLabel( QWidget *parent, intf_thread_t *_p_i )
563
              : QLabel( parent ), p_intf( _p_i )
564 565
{
    setContextMenuPolicy( Qt::ActionsContextMenu );
566
    CONNECT( this, updateRequested(), this, askForUpdate() );
567

568
    setMinimumHeight( 128 );
569 570
    setMinimumWidth( 128 );
    setMaximumHeight( 128 );
571
    setMaximumWidth( 128 );
572 573
    setScaledContents( false );
    setAlignment( Qt::AlignCenter );
574

575 576
    QList< QAction* > artActions = actions();
    QAction *action = new QAction( qtr( "Download cover art" ), this );
577
    CONNECT( action, triggered(), this, askForUpdate() );
578
    addAction( action );
579

580
    showArtUpdate( "" );
581 582
}

583 584 585 586 587 588 589
CoverArtLabel::~CoverArtLabel()
{
    QList< QAction* > artActions = actions();
    foreach( QAction *act, artActions )
        removeAction( act );
}

590
void CoverArtLabel::showArtUpdate( const QString& url )
591
{
592
    QPixmap pix;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
593
    if( !url.isEmpty() && pix.load( url ) )
594
    {
595 596
        pix = pix.scaled( maximumWidth(), maximumHeight(),
                          Qt::KeepAspectRatioByExpanding );
597 598 599
    }
    else
    {
600
        pix = QPixmap( ":/noart.png" );
601
    }
602
    setPixmap( pix );
603 604
}

605
void CoverArtLabel::askForUpdate()
606
{
607
    THEMIM->getIM()->requestArtUpdate();
608 609
}

610 611 612
TimeLabel::TimeLabel( intf_thread_t *_p_intf  )
    : QLabel(), p_intf( _p_intf ), bufTimer( new QTimer(this) ),
      buffering( false ), showBuffering(false), bufVal( -1 )
613
{
614 615 616
    b_remainingTime = false;
    setText( " --:--/--:-- " );
    setAlignment( Qt::AlignRight | Qt::AlignVCenter );
617 618 619 620
    setToolTip( QString( "- " )
        + qtr( "Click to toggle between elapsed and remaining time" )
        + QString( "\n- " )
        + qtr( "Double click to jump to a chosen time position" ) );
621
    bufTimer->setSingleShot( true );
622

623 624 625 626 627
    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() );
628 629
}

630
void TimeLabel::setDisplayPosition( float pos, int64_t t, int length )
631
{
632 633 634
    showBuffering = false;
    bufTimer->stop();

635
    if( pos == -1.f )
636 637 638 639
    {
        setText( " --:--/--:-- " );
        return;
    }
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
640

641
    int time = t / 1000000;
642

643 644 645 646
    secstotimestr( psz_length, length );
    secstotimestr( psz_time, ( b_remainingTime && length ) ? length - time
                                                           : time );

647 648 649 650
    QString timestr = QString( " %1%2/%3 " )
            .arg( QString( (b_remainingTime && length) ? "-" : "" ) )
            .arg( QString( psz_time ) )
            .arg( QString( ( !length && time ) ? "--:--" : psz_length ) );
651

JP Dinger's avatar
JP Dinger committed
652
    setText( timestr );
653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668

    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 ) );
669 670 671 672
    QString timestr = QString( " %1%2/%3 " )
        .arg( QString( (b_remainingTime && cachedLength) ? "-" : "" ) )
        .arg( QString( psz_time ) )
        .arg( QString( ( !cachedLength && time ) ? "--:--" : psz_length ) );
673 674

    setText( timestr );
675 676
}

677

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


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

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

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