interface_widgets.cpp 41.9 KB
Newer Older
1
/*****************************************************************************
2
 * interface_widgets.cpp : Custom widgets for the main interface
3
 ****************************************************************************
4
 * Copyright ( C ) 2006 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

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
31 32
#include <vlc_vout.h>

33
#include "dialogs_provider.hpp"
34
#include "components/interface_widgets.hpp"
35
#include "main_interface.hpp"
36
#include "input_manager.hpp"
37
#include "menus.hpp"
38
#include "util/input_slider.hpp"
39
#include "util/customwidgets.hpp"
40

Clément Stenac's avatar
Clément Stenac committed
41 42
#include <QLabel>
#include <QSpacerItem>
Clément Stenac's avatar
Clément Stenac committed
43
#include <QCursor>
Clément Stenac's avatar
Clément Stenac committed
44
#include <QPushButton>
45
#include <QToolButton>
46
#include <QHBoxLayout>
Clément Stenac's avatar
Clément Stenac committed
47
#include <QMenu>
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
48 49
#include <QPalette>
#include <QResizeEvent>
50
#include <QDate>
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
51

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

57 58
#include <math.h>

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
59
#define I_PLAY_TOOLTIP N_("Play\nIf the playlist is empty, open a media")
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
60

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

66
VideoWidget::VideoWidget( intf_thread_t *_p_i ) : QFrame( NULL ), p_intf( _p_i )
67
{
68
    /* Init */
69
    i_vout = 0;
70 71
    videoSize.rwidth() = -1;
    videoSize.rheight() = -1;
72 73 74 75

    hide();

    /* Set the policy to expand in both directions */
76
    setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
77

78
    /* Black background is more coherent for a Video Widget */
79
    QPalette plt =  palette();
80
    plt.setColor( QPalette::Window, Qt::black );
81
    setPalette( plt );
82 83 84 85 86
    setAutoFillBackground(true);

    /* Indicates that the widget wants to draw directly onto the screen.
       Widgets with this attribute set do not participate in composition
       management */
87
    setAttribute( Qt::WA_PaintOnScreen, true );
88

89
    /* The core can ask through a callback to show the video. */
90
#if HAS_QT43
91 92 93
    connect( this, SIGNAL(askVideoWidgetToShow( unsigned int, unsigned int)),
             this, SLOT(SetSizing(unsigned int, unsigned int )),
             Qt::BlockingQueuedConnection );
94
#else
95
#warning This is broken. Fix it with a QEventLoop with a processEvents ()
96 97 98
    connect( this, SIGNAL(askVideoWidgetToShow( unsigned int, unsigned int)),
             this, SLOT(SetSizing(unsigned int, unsigned int )) );
#endif
99 100
}

101 102 103
void VideoWidget::paintEvent(QPaintEvent *ev)
{
    QFrame::paintEvent(ev);
104 105 106
#ifdef Q_WS_X11
    XFlush( QX11Info::display() );
#endif
107 108
}

109
/* Kill the vout at Destruction */
110 111
VideoWidget::~VideoWidget()
{
112 113
    vout_thread_t *p_vout = i_vout ?
        (vout_thread_t *)vlc_object_get( i_vout ) : NULL;
114

115 116 117 118 119 120 121 122 123 124 125 126
    if( p_vout )
    {
        if( !p_intf->psz_switch_intf )
        {
            if( vout_Control( p_vout, VOUT_CLOSE ) != VLC_SUCCESS )
                vout_Control( p_vout, VOUT_REPARENT );
        }
        else
        {
            if( vout_Control( p_vout, VOUT_REPARENT ) != VLC_SUCCESS )
                vout_Control( p_vout, VOUT_CLOSE );
        }
127
        vlc_object_release( p_vout );
128 129 130
    }
}

131
/**
132
 * Request the video to avoid the conflicts
133
 **/
134
void *VideoWidget::request( vout_thread_t *p_nvout, int *pi_x, int *pi_y,
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
135
                            unsigned int *pi_width, unsigned int *pi_height )
136
{
137
    msg_Dbg( p_intf, "Video was requested %i, %i", *pi_x, *pi_y );
138
    emit askVideoWidgetToShow( *pi_width, *pi_height );
139
    if( i_vout )
140 141 142 143
    {
        msg_Dbg( p_intf, "embedded video already in use" );
        return NULL;
    }
144
    i_vout = p_nvout->i_object_id;
145
#ifndef NDEBUG
146
    msg_Dbg( p_intf, "embedded video ready (handle %p)", winId() );
147
#endif
148
    return ( void* )winId();
149 150
}

151
/* Set the Widget to the correct Size */
152 153
/* Function has to be called by the parent
   Parent has to care about resizing himself*/
154
void VideoWidget::SetSizing( unsigned int w, unsigned int h )
155
{
156 157 158
    msg_Dbg( p_intf, "Video is resizing to: %i %i", w, h );
    videoSize.rwidth() = w;
    videoSize.rheight() = h;
159
    if( isHidden() ) show();
160
    updateGeometry(); // Needed for deinterlace
161 162
}

163
void VideoWidget::release( void *p_win )
164
{
Rémi Denis-Courmont's avatar
Typo  
Rémi Denis-Courmont committed
165
    msg_Dbg( p_intf, "Video is not needed anymore" );
166
    i_vout = 0;
167 168
    videoSize.rwidth() = 0;
    videoSize.rheight() = 0;
169
    updateGeometry();
170
    hide();
171
}
172

173 174 175 176
QSize VideoWidget::sizeHint() const
{
    return videoSize;
}
177

178 179
/**********************************************************************
 * Background Widget. Show a simple image background. Currently,
180
 * it's album art if present or cone.
181
 **********************************************************************/
182 183
#define ICON_SIZE 128
#define MAX_BG_SIZE 400
184
#define MIN_BG_SIZE 128
185

186 187
BackgroundWidget::BackgroundWidget( intf_thread_t *_p_i )
                 :QWidget( NULL ), p_intf( _p_i )
188
{
189
    /* We should use that one to take the more size it can */
190
    setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding);
191

192
    /* A dark background */
Clément Stenac's avatar
Clément Stenac committed
193
    setAutoFillBackground( true );
194
    plt = palette();
195 196 197 198
    plt.setColor( QPalette::Active, QPalette::Window , Qt::black );
    plt.setColor( QPalette::Inactive, QPalette::Window , Qt::black );
    setPalette( plt );

199
    /* A cone in the middle */
200
    label = new QLabel;
201
    label->setMargin( 5 );
202 203
    label->setMaximumHeight( MAX_BG_SIZE );
    label->setMaximumWidth( MAX_BG_SIZE );
204 205
    label->setMinimumHeight( MIN_BG_SIZE );
    label->setMinimumWidth( MIN_BG_SIZE );
206 207 208 209
    if( QDate::currentDate().dayOfYear() >= 354 )
        label->setPixmap( QPixmap( ":/vlc128-christmas.png" ) );
    else
        label->setPixmap( QPixmap( ":/vlc128.png" ) );
210

211 212 213 214
    QGridLayout *backgroundLayout = new QGridLayout( this );
    backgroundLayout->addWidget( label, 0, 1 );
    backgroundLayout->setColumnStretch( 0, 1 );
    backgroundLayout->setColumnStretch( 2, 1 );
215

216
    CONNECT( THEMIM->getIM(), artChanged( QString ), this, updateArt( QString ) );
217 218
}

Clément Stenac's avatar
Clément Stenac committed
219
BackgroundWidget::~BackgroundWidget()
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
220
{}
Ilkka Ollakka's avatar
 
Ilkka Ollakka committed
221

222 223 224 225 226 227 228 229
void BackgroundWidget::resizeEvent( QResizeEvent * event )
{
    if( event->size().height() <= MIN_BG_SIZE )
        label->hide();
    else
        label->show();
}

230
void BackgroundWidget::updateArt( QString url )
231
{
232
    if( url.isEmpty() )
Ilkka Ollakka's avatar
 
Ilkka Ollakka committed
233
    {
234 235 236 237
        if( QDate::currentDate().dayOfYear() >= 354 )
            label->setPixmap( QPixmap( ":/vlc128-christmas.png" ) );
        else
            label->setPixmap( QPixmap( ":/vlc128.png" ) );
Ilkka Ollakka's avatar
 
Ilkka Ollakka committed
238 239
        return;
    }
240
    else
Ilkka Ollakka's avatar
 
Ilkka Ollakka committed
241
    {
242
        label->setPixmap( QPixmap( url ) );
Ilkka Ollakka's avatar
 
Ilkka Ollakka committed
243
    }
244 245
}

246 247 248 249
void BackgroundWidget::contextMenuEvent( QContextMenuEvent *event )
{
    QVLCMenu::PopupMenu( p_intf, true );
}
250

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
251
#if 0
Clément Stenac's avatar
Clément Stenac committed
252 253 254 255
/**********************************************************************
 * Visualization selector panel
 **********************************************************************/
VisualSelector::VisualSelector( intf_thread_t *_p_i ) :
256
                                QFrame( NULL ), p_intf( _p_i )
Clément Stenac's avatar
Clément Stenac committed
257 258
{
    QHBoxLayout *layout = new QHBoxLayout( this );
259
    layout->setMargin( 0 );
Clément Stenac's avatar
Clément Stenac committed
260
    QPushButton *prevButton = new QPushButton( "Prev" );
261
    QPushButton *nextButton = new QPushButton( "Next" );
Clément Stenac's avatar
Clément Stenac committed
262 263
    layout->addWidget( prevButton );
    layout->addWidget( nextButton );
Clément Stenac's avatar
Clément Stenac committed
264

265
    layout->addStretch( 10 );
266
    layout->addWidget( new QLabel( qtr( "Current visualization" ) ) );
Clément Stenac's avatar
Clément Stenac committed
267 268 269 270 271 272 273

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

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

Clément Stenac's avatar
Clément Stenac committed
274
    setLayout( layout );
275
    setMaximumHeight( 35 );
Clément Stenac's avatar
Clément Stenac committed
276 277 278
}

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

Clément Stenac's avatar
Clément Stenac committed
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
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
300
#endif
Clément Stenac's avatar
Clément Stenac committed
301 302

/**********************************************************************
303
 * TEH controls
Clément Stenac's avatar
Clément Stenac committed
304
 **********************************************************************/
305 306 307 308 309 310

#define setupSmallButton( aButton ){  \
    aButton->setMaximumSize( QSize( 26, 26 ) ); \
    aButton->setMinimumSize( QSize( 26, 26 ) ); \
    aButton->setIconSize( QSize( 20, 20 ) ); }

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
311 312 313 314
/* init static variables in advanced controls */
mtime_t AdvControlsWidget::timeA = 0;
mtime_t AdvControlsWidget::timeB = 0;

315
AdvControlsWidget::AdvControlsWidget( intf_thread_t *_p_i, bool b_fsCreation = false ) :
Clément Stenac's avatar
Clément Stenac committed
316 317
                                           QFrame( NULL ), p_intf( _p_i )
{
318 319 320
    QHBoxLayout *advLayout = new QHBoxLayout( this );
    advLayout->setMargin( 0 );
    advLayout->setSpacing( 0 );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
321
    advLayout->setAlignment( Qt::AlignBottom );
Clément Stenac's avatar
Clément Stenac committed
322

323
    /* A to B Button */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
324
    ABButton = new QPushButton;
Lukas Durfina's avatar
Lukas Durfina committed
325
    setupSmallButton( ABButton );
326
    advLayout->addWidget( ABButton );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
327
    BUTTON_SET_ACT_I( ABButton, "", atob_nob,
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
328 329
      qtr( "Loop from point A to point B continuously.\nClick to set point A" ),
      fromAtoB() );
330
    timeA = timeB = 0;
331
    i_last_input_id = 0;
332 333 334 335 336 337 338 339 340
    /* in FS controller we skip this, because we dont want to have it double
       controlled */
    if( !b_fsCreation )
        CONNECT( THEMIM->getIM(), positionUpdated( float, int, int ),
                 this, AtoBLoop( float, int, int ) );
    /* set up synchronization between main controller and fs controller */
    CONNECT( THEMIM->getIM(), advControlsSetIcon(), this, setIcon() );
    connect( this, SIGNAL( timeChanged() ),
        THEMIM->getIM(), SIGNAL( advControlsSetIcon()));
341
#if 0
342 343 344 345
    frameButton = new QPushButton( "Fr" );
    frameButton->setMaximumSize( QSize( 26, 26 ) );
    frameButton->setIconSize( QSize( 20, 20 ) );
    advLayout->addWidget( frameButton );
346
    BUTTON_SET_ACT( frameButton, "Fr", qtr( "Frame by frame" ), frame() );
347
#endif
348

349
    /* Record Button */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
350
    recordButton = new QPushButton;
Lukas Durfina's avatar
Lukas Durfina committed
351
    setupSmallButton( recordButton );
352
    advLayout->addWidget( recordButton );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
353
    BUTTON_SET_ACT_I( recordButton, "", record,
354
            qtr( "Record" ), record() );
355

356
    /* Snapshot Button */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
357
    snapshotButton = new QPushButton;
Lukas Durfina's avatar
Lukas Durfina committed
358
    setupSmallButton( snapshotButton );
359
    advLayout->addWidget( snapshotButton );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
360 361
    BUTTON_SET_ACT_I( snapshotButton, "", snapshot,
            qtr( "Take a snapshot" ), snapshot() );
Clément Stenac's avatar
Clément Stenac committed
362 363
}

364
AdvControlsWidget::~AdvControlsWidget()
365
{}
Clément Stenac's avatar
Clément Stenac committed
366

367
void AdvControlsWidget::enableInput( bool enable )
Clément Stenac's avatar
Clément Stenac committed
368
{
369 370 371 372 373
    int i_input_id = 0;
    if( THEMIM->getInput() != NULL )
    {
        input_item_t *p_item = input_GetItem( THEMIM->getInput() );
        i_input_id = p_item->i_id;
374 375 376 377 378

        if( var_Type( THEMIM->getInput(), "record-toggle" ) == VLC_VAR_VOID )
            recordButton->setVisible( true );
        else
            recordButton->setVisible( false );
379
    }
380 381 382
    else
        recordButton->setVisible( false );

383 384
    ABButton->setEnabled( enable );
    recordButton->setEnabled( enable );
385 386 387 388 389 390 391

    if( enable && ( i_last_input_id != i_input_id ) )
    {
        timeA = timeB = 0;
        i_last_input_id = i_input_id;
        emit timeChanged();
    }
Clément Stenac's avatar
Clément Stenac committed
392
}
393

394
void AdvControlsWidget::enableVideo( bool enable )
Clément Stenac's avatar
Clément Stenac committed
395 396
{
    snapshotButton->setEnabled( enable );
397
#if 0
398
    frameButton->setEnabled( enable );
399
#endif
Clément Stenac's avatar
Clément Stenac committed
400 401
}

402
void AdvControlsWidget::snapshot()
Clément Stenac's avatar
Clément Stenac committed
403
{
404 405
    vout_thread_t *p_vout =
        (vout_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_VOUT, FIND_ANYWHERE );
406
    if( p_vout ) vout_Control( p_vout, VOUT_SNAPSHOT );
Clément Stenac's avatar
Clément Stenac committed
407 408
}

409
/* Function called when the button is clicked() */
410 411 412 413 414
void AdvControlsWidget::fromAtoB()
{
    if( !timeA )
    {
        timeA = var_GetTime( THEMIM->getInput(), "time"  );
415
        emit timeChanged();
416 417 418 419 420 421
        return;
    }
    if( !timeB )
    {
        timeB = var_GetTime( THEMIM->getInput(), "time"  );
        var_SetTime( THEMIM->getInput(), "time" , timeA );
422
        emit timeChanged();
423 424 425 426
        return;
    }
    timeA = 0;
    timeB = 0;
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
    emit timeChanged();
}

/* setting/synchro icons after click on main or fs controller */
void AdvControlsWidget::setIcon()
{
    if( !timeA && !timeB)
    {
        ABButton->setIcon( QIcon( ":/atob_nob" ) );
        ABButton->setToolTip( qtr( "Loop from point A to point B continuously\nClick to set point A" ) );
    }
    else if( timeA && !timeB )
    {
        ABButton->setIcon( QIcon( ":/atob_noa" ) );
        ABButton->setToolTip( qtr( "Click to set point B" ) );
    }
    else if( timeA && timeB )
    {
        ABButton->setIcon( QIcon( ":/atob" ) );
        ABButton->setToolTip( qtr( "Stop the A to B loop" ) );
    }
448
}
449

450
/* Function called regularly when in an AtoB loop */
451 452 453 454
void AdvControlsWidget::AtoBLoop( float f_pos, int i_time, int i_length )
{
    if( timeB )
    {
455 456
        if( ( i_time >= (int)( timeB/1000000 ) )
            || ( i_time < (int)( timeA/1000000 ) ) )
457 458 459
            var_SetTime( THEMIM->getInput(), "time" , timeA );
    }
}
460

461 462 463 464 465 466 467 468
void AdvControlsWidget::record()
{
    input_thread_t *p_input = THEMIM->getInput();
    if( p_input )
    {
        /* This method won't work fine if the stream can't be cut anywhere */
        if( var_Type( p_input, "record-toggle" ) == VLC_VAR_VOID )
            var_TriggerCallback( p_input, "record-toggle" );
469
#if 0
470 471 472 473 474 475 476 477 478 479 480
        else
        {
            /* 'record' access-filter is not loaded, we open Save dialog */
            input_item_t *p_item = input_GetItem( p_input );
            if( !p_item )
                return;

            char *psz = input_item_GetURI( p_item );
            if( psz )
                THEDP->streamingDialog( NULL, psz, true );
        }
481
#endif
482 483
    }
}
484

485
#if 0
486
//FIXME Frame by frame function
487
void AdvControlsWidget::frame(){}
488
#endif
489

490 491 492
/*****************************
 * DA Control Widget !
 *****************************/
493
ControlsWidget::ControlsWidget( intf_thread_t *_p_i,
494
                                MainInterface *_p_mi,
495
                                bool b_advControls,
Lukas Durfina's avatar
Lukas Durfina committed
496 497 498
                                bool b_shiny,
                                bool b_fsCreation) :
                                QFrame( _p_mi ), p_intf( _p_i )
499
{
500
    setSizePolicy( QSizePolicy::Preferred , QSizePolicy::Maximum );
501

502 503 504 505
    /** The main Slider **/
    slider = new InputSlider( Qt::Horizontal, NULL );
    /* Update the position when the IM has changed */
    CONNECT( THEMIM->getIM(), positionUpdated( float, int, int ),
506
             slider, setPosition( float, int, int ) );
507 508 509 510
    /* And update the IM, when the position has changed */
    CONNECT( slider, sliderDragged( float ),
             THEMIM->getIM(), sliderUpdate( float ) );

511
    /** Slower and faster Buttons **/
512 513
    slowerButton = new QToolButton;
    slowerButton->setAutoRaise( true );
514
    slowerButton->setMaximumSize( QSize( 26, 20 ) );
515

516
    BUTTON_SET_ACT( slowerButton, "-", qtr( "Slower" ), slower() );
517

518 519
    fasterButton = new QToolButton;
    fasterButton->setAutoRaise( true );
520
    fasterButton->setMaximumSize( QSize( 26, 20 ) );
521

522
    BUTTON_SET_ACT( fasterButton, "+", qtr( "Faster" ), faster() );
523

524 525 526
    /* advanced Controls handling */
    b_advancedVisible = b_advControls;

527
    advControls = new AdvControlsWidget( p_intf, b_fsCreation );
528 529
    if( !b_advancedVisible ) advControls->hide();

530
    /** Disc and Menus handling */
531
    discFrame = new QWidget( this );
532

533 534 535
    QHBoxLayout *discLayout = new QHBoxLayout( discFrame );
    discLayout->setSpacing( 0 );
    discLayout->setMargin( 0 );
536

537
    prevSectionButton = new QPushButton( discFrame );
538
    setupSmallButton( prevSectionButton );
539 540
    discLayout->addWidget( prevSectionButton );

541
    menuButton = new QPushButton( discFrame );
542 543 544
    setupSmallButton( menuButton );
    discLayout->addWidget( menuButton );

545
    nextSectionButton = new QPushButton( discFrame );
546
    setupSmallButton( nextSectionButton );
547 548
    discLayout->addWidget( nextSectionButton );

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
549 550 551
    BUTTON_SET_IMG( prevSectionButton, "", dvd_prev, "" );
    BUTTON_SET_IMG( nextSectionButton, "", dvd_next, "" );
    BUTTON_SET_IMG( menuButton, "", dvd_menu, qtr( "Menu" ) );
552

553
    discFrame->hide();
554 555 556

    /* Change the navigation button display when the IM navigation changes */
    CONNECT( THEMIM->getIM(), navigationChanged( int ),
557
             this, setNavigation( int ) );
558 559 560 561 562 563 564
    /* Changes the IM navigation when triggered on the nav buttons */
    CONNECT( prevSectionButton, clicked(), THEMIM->getIM(),
             sectionPrev() );
    CONNECT( nextSectionButton, clicked(), THEMIM->getIM(),
             sectionNext() );
    CONNECT( menuButton, clicked(), THEMIM->getIM(),
             sectionMenu() );
565

566
    /**
567
     * Telextext QFrame
568
     * TODO: Merge with upper menu in a StackLayout
569
     **/
570 571 572 573 574
    telexFrame = new QWidget( this );
    QHBoxLayout *telexLayout = new QHBoxLayout( telexFrame );
    telexLayout->setSpacing( 0 );
    telexLayout->setMargin( 0 );

Lukas Durfina's avatar
Lukas Durfina committed
575
    telexOn = new QPushButton;
576 577 578
    setupSmallButton( telexOn );
    telexLayout->addWidget( telexOn );

579
    telexTransparent = new QPushButton;
580 581
    setupSmallButton( telexTransparent );
    telexLayout->addWidget( telexTransparent );
582
    b_telexTransparent = false;
583

584
    telexPage = new QSpinBox;
585
    telexPage->setRange( 0, 999 );
586
    telexPage->setValue( 100 );
587 588
    telexPage->setAccelerated( true );
    telexPage->setWrapping( true );
589
    telexPage->setAlignment( Qt::AlignRight );
590
    telexPage->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Minimum );
591 592
    telexLayout->addWidget( telexPage );

593
    telexFrame->hide(); /* default hidden */
594 595 596

    CONNECT( telexPage, valueChanged( int ), THEMIM->getIM(),
             telexGotoPage( int ) );
Lukas Durfina's avatar
Lukas Durfina committed
597 598 599
    CONNECT( THEMIM->getIM(), setNewTelexPage( int ),
              telexPage, setValue( int ) );

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
600
    BUTTON_SET_IMG( telexOn, "", tv, qtr( "Teletext on" ) );
601

Lukas Durfina's avatar
Lukas Durfina committed
602 603
    CONNECT( telexOn, clicked(), THEMIM->getIM(),
             telexToggleButtons() );
604 605
    CONNECT( telexOn, clicked( bool ), THEMIM->getIM(),
             telexToggle( bool ) );
Lukas Durfina's avatar
Lukas Durfina committed
606 607 608
    CONNECT( THEMIM->getIM(), toggleTelexButtons(),
              this, toggleTeletext() );
    b_telexEnabled = false;
609 610 611
    telexTransparent->setEnabled( false );
    telexPage->setEnabled( false );

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
612
    BUTTON_SET_IMG( telexTransparent, "", tvtelx, qtr( "Teletext" ) );
613
    CONNECT( telexTransparent, clicked( bool ),
614
             THEMIM->getIM(), telexSetTransparency() );
Lukas Durfina's avatar
Lukas Durfina committed
615 616
    CONNECT( THEMIM->getIM(), toggleTelexTransparency(),
              this, toggleTeletextTransparency() );
617
    CONNECT( THEMIM->getIM(), teletextEnabled( bool ),
618
             this, enableTeletext( bool ) );
619

620
    /** Play Buttons **/
621
    QSizePolicy sizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
622 623 624 625
    sizePolicy.setHorizontalStretch( 0 );
    sizePolicy.setVerticalStretch( 0 );

    /* Play */
626
    playButton = new QPushButton;
627
    playButton->setSizePolicy( sizePolicy );
628 629
    playButton->setMaximumSize( QSize( 36, 36 ) );
    playButton->setMinimumSize( QSize( 36, 36 ) );
630
    playButton->setIconSize( QSize( 30, 30 ) );
631

632

633
    /** Prev + Stop + Next Block **/
Lukas Durfina's avatar
Lukas Durfina committed
634
    controlButLayout = new QHBoxLayout;
635 636 637 638 639
    controlButLayout->setSpacing( 0 ); /* Don't remove that, will be useful */

    /* Prev */
    QPushButton *prevButton = new QPushButton;
    prevButton->setSizePolicy( sizePolicy );
640
    setupSmallButton( prevButton );
641 642 643 644 645 646

    controlButLayout->addWidget( prevButton );

    /* Stop */
    QPushButton *stopButton = new QPushButton;
    stopButton->setSizePolicy( sizePolicy );
647
    setupSmallButton( stopButton );
648 649 650 651 652 653

    controlButLayout->addWidget( stopButton );

    /* next */
    QPushButton *nextButton = new QPushButton;
    nextButton->setSizePolicy( sizePolicy );
654
    setupSmallButton( nextButton );
655 656 657 658 659

    controlButLayout->addWidget( nextButton );

    /* Add this block to the main layout */

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
660
    BUTTON_SET_ACT_I( playButton, "", play_b, qtr( I_PLAY_TOOLTIP ), play() );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
661
    BUTTON_SET_ACT_I( prevButton, "" , previous_b,
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
662 663 664 665
                      qtr( "Previous media in the playlist" ), prev() );
    BUTTON_SET_ACT_I( nextButton, "", next_b,
                      qtr( "Next media in the playlist" ), next() );
    BUTTON_SET_ACT_I( stopButton, "", stop_b, qtr( "Stop playback" ), stop() );
666 667 668 669

    /*
     * Other first Line buttons
     */
670
    /** Fullscreen/Visualisation **/
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
671
    fullscreenButton = new QPushButton;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
672 673
    BUTTON_SET_ACT_I( fullscreenButton, "", fullscreen,
            qtr( "Toggle the video in fullscreen" ), fullscreen() );
674
    setupSmallButton( fullscreenButton );
675

676 677 678 679 680
    if( !b_fsCreation )
    {
        /** Playlist Button **/
        playlistButton = new QPushButton;
        setupSmallButton( playlistButton );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
681
        BUTTON_SET_IMG( playlistButton, "" , playlist, qtr( "Show playlist" ) );
682 683 684 685
        CONNECT( playlistButton, clicked(), _p_mi, togglePlaylist() );

        /** extended Settings **/
        extSettingsButton = new QPushButton;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
686
        BUTTON_SET_ACT_I( extSettingsButton, "", extended,
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
687
                qtr( "Show extended settings" ), extSettings() );
688 689
        setupSmallButton( extSettingsButton );
    }
690

691
    /* Volume */
Lukas Durfina's avatar
Lukas Durfina committed
692
    hVolLabel = new VolumeClickHandler( p_intf, this );
693

694
    volMuteLabel = new QLabel;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
695
    volMuteLabel->setPixmap( QPixmap( ":/volume-medium" ) );
696
    volMuteLabel->installEventFilter( hVolLabel );
697

698 699 700
    if( b_shiny )
    {
        volumeSlider = new SoundSlider( this,
701
            config_GetInt( p_intf, "volume-step" ),
702 703
            config_GetInt( p_intf, "qt-volume-complete" ),
            config_GetPsz( p_intf, "qt-slider-colours" ) );
704 705 706 707 708 709
    }
    else
    {
        volumeSlider = new QSlider( this );
        volumeSlider->setOrientation( Qt::Horizontal );
    }
710
    volumeSlider->setMaximumSize( QSize( 200, 40 ) );
711
    volumeSlider->setMinimumSize( QSize( 85, 30 ) );
712
    volumeSlider->setFocusPolicy( Qt::NoFocus );
713

714
    /* Set the volume from the config */
715 716
    volumeSlider->setValue( ( config_GetInt( p_intf, "volume" ) ) *
                              VOLUME_MAX / (AOUT_VOLUME_MAX/2) );
717

718 719 720
    /* Force the update at build time in order to have a muted icon if needed */
    updateVolume( volumeSlider->value() );

721 722
    /* Volume control connection */
    CONNECT( volumeSlider, valueChanged( int ), this, updateVolume( int ) );
723
    CONNECT( THEMIM, volumeChanged( void ), this, updateVolume( void ) );
724

725 726 727 728 729 730 731
    if( !b_fsCreation )
    {
        controlLayout = new QGridLayout( this );

        controlLayout->setSpacing( 0 );
        controlLayout->setLayoutMargins( 7, 5, 7, 3, 6 );

732
        controlLayout->addWidget( slider, 0, 1, 1, 18 );
733
        controlLayout->addWidget( slowerButton, 0, 0 );
734
        controlLayout->addWidget( fasterButton, 0, 19 );
735

736 737
        controlLayout->addWidget( discFrame, 1, 8, 2, 3, Qt::AlignBottom );
        controlLayout->addWidget( telexFrame, 1, 8, 2, 5, Qt::AlignBottom );
738

739 740
        controlLayout->addWidget( playButton, 2, 0, 2, 2, Qt::AlignBottom );
        controlLayout->setColumnMinimumWidth( 2, 10 );
741 742
        controlLayout->setColumnStretch( 2, 0 );

743 744 745
        controlLayout->addLayout( controlButLayout, 3, 3, 1, 3, Qt::AlignBottom );
        /* Column 6 is unused */
        controlLayout->setColumnStretch( 6, 0 );
746
        controlLayout->setColumnStretch( 7, 0 );
747
        controlLayout->setColumnMinimumWidth( 7, 10 );
748

749 750 751 752
        controlLayout->addWidget( fullscreenButton, 3, 8, Qt::AlignBottom );
        controlLayout->addWidget( playlistButton, 3, 9, Qt::AlignBottom );
        controlLayout->addWidget( extSettingsButton, 3, 10, Qt::AlignBottom );
        controlLayout->setColumnStretch( 11, 0 ); /* telex alignment */
753

754 755
        controlLayout->setColumnStretch( 12, 0 );
        controlLayout->setColumnMinimumWidth( 12, 10 );
756

757 758 759 760 761 762 763
        controlLayout->addWidget( advControls, 3, 13, 1, 3, Qt::AlignBottom );

        controlLayout->setColumnStretch( 16, 10 );
        controlLayout->setColumnMinimumWidth( 16, 10 );

        controlLayout->addWidget( volMuteLabel, 3, 17, Qt::AlignBottom );
        controlLayout->addWidget( volumeSlider, 3, 18, 1 , 2, Qt::AlignBottom );
764 765
    }

766
    updateInput();
767
}
768

769
ControlsWidget::~ControlsWidget()
770
{}
771

772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787
void ControlsWidget::toggleTeletext()
{
    bool b_enabled = THEMIM->teletextState();
    if( b_telexEnabled )
    {
        telexTransparent->setEnabled( false );
        telexPage->setEnabled( false );
        b_telexEnabled = false;
    }
    else if( b_enabled )
    {
        telexTransparent->setEnabled( true );
        telexPage->setEnabled( true );
        b_telexEnabled = true;
    }
}
788

789 790 791 792 793 794 795 796 797 798
void ControlsWidget::enableTeletext( bool b_enable )
{
    telexFrame->setVisible( b_enable );
    bool b_on = THEMIM->teletextState();

    telexOn->setChecked( b_on );
    telexTransparent->setEnabled( b_on );
    telexPage->setEnabled( b_on );
    b_telexEnabled = b_on;
}
799 800 801 802 803

void ControlsWidget::toggleTeletextTransparency()
{
    if( b_telexTransparent )
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
804
        telexTransparent->setIcon( QIcon( ":/tvtelx" ) );
805 806 807 808 809
        telexTransparent->setToolTip( qtr( "Teletext" ) );
        b_telexTransparent = false;
    }
    else
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
810
        telexTransparent->setIcon( QIcon( ":/tvtelx-transparent" ) );
811 812 813 814 815
        telexTransparent->setToolTip( qtr( "Transparent" ) );
        b_telexTransparent = true;
    }
}

816 817 818 819 820 821 822
void ControlsWidget::stop()
{
    THEMIM->stop();
}

void ControlsWidget::play()
{
823
    if( THEPL->current.i_size == 0 )
824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
    {
        /* The playlist is empty, open a file requester */
        THEDP->openFileDialog();
        setStatus( 0 );
        return;
    }
    THEMIM->togglePlayPause();
}

void ControlsWidget::prev()
{
    THEMIM->prev();
}

void ControlsWidget::next()
{
    THEMIM->next();
}

void ControlsWidget::setNavigation( int navigation )
{
845 846
#define HELP_PCH N_( "Previous chapter" )
#define HELP_NCH N_( "Next chapter" )
847 848 849 850 851 852

    // 1 = chapter, 2 = title, 0 = no
    if( navigation == 0 )
    {
        discFrame->hide();
    } else if( navigation == 1 ) {
853 854
        prevSectionButton->setToolTip( qfu( HELP_PCH ) );
        nextSectionButton->setToolTip( qfu( HELP_NCH ) );
855 856 857
        menuButton->show();
        discFrame->show();
    } else {
858 859
        prevSectionButton->setToolTip( qfu( HELP_PCH ) );
        nextSectionButton->setToolTip( qfu( HELP_NCH ) );
860 861 862 863 864 865
        menuButton->hide();
        discFrame->show();
    }
}

static bool b_my_volume;
866
void ControlsWidget::updateVolume( int i_sliderVolume )
867 868 869