interface_widgets.cpp 48 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
    p_vout = NULL;
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 114 115
    if( !p_vout )
        return;
    if( vout_Control( p_vout, VOUT_CLOSE ) != VLC_SUCCESS )
        vout_Control( p_vout, VOUT_REPARENT );
116 117
}

118
/**
119
 * Request the video to avoid the conflicts
120
 **/
121
void *VideoWidget::request( vout_thread_t *p_nvout, int *pi_x, int *pi_y,
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
122
                            unsigned int *pi_width, unsigned int *pi_height )
123
{
124
    msg_Dbg( p_intf, "Video was requested %i, %i", *pi_x, *pi_y );
125
    emit askVideoWidgetToShow( *pi_width, *pi_height );
126
    if( p_vout )
127 128 129 130
    {
        msg_Dbg( p_intf, "embedded video already in use" );
        return NULL;
    }
131
    p_vout = p_nvout;
132
#ifndef NDEBUG
133
    msg_Dbg( p_intf, "embedded video ready (handle %p)", winId() );
134
#endif
135
    return ( void* )winId();
136 137
}

138
/* Set the Widget to the correct Size */
139 140
/* Function has to be called by the parent
   Parent has to care about resizing himself*/
141
void VideoWidget::SetSizing( unsigned int w, unsigned int h )
142
{
143 144 145
    msg_Dbg( p_intf, "Video is resizing to: %i %i", w, h );
    videoSize.rwidth() = w;
    videoSize.rheight() = h;
146
    if( isHidden() ) show();
147
    updateGeometry(); // Needed for deinterlace
148 149
}

150
void VideoWidget::release( void *p_win )
151
{
Rémi Denis-Courmont's avatar
Typo  
Rémi Denis-Courmont committed
152
    msg_Dbg( p_intf, "Video is not needed anymore" );
153
    p_vout = NULL;
154 155
    videoSize.rwidth() = 0;
    videoSize.rheight() = 0;
156
    updateGeometry();
157
    hide();
158
}
159

160 161 162 163
QSize VideoWidget::sizeHint() const
{
    return videoSize;
}
164

165 166
/**********************************************************************
 * Background Widget. Show a simple image background. Currently,
167
 * it's album art if present or cone.
168
 **********************************************************************/
169 170
#define ICON_SIZE 128
#define MAX_BG_SIZE 400
171
#define MIN_BG_SIZE 128
172

173 174
BackgroundWidget::BackgroundWidget( intf_thread_t *_p_i )
                 :QWidget( NULL ), p_intf( _p_i )
175
{
176
    /* We should use that one to take the more size it can */
177
    setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding);
178

179
    /* A dark background */
Clément Stenac's avatar
Clément Stenac committed
180
    setAutoFillBackground( true );
181
    plt = palette();
182 183 184 185
    plt.setColor( QPalette::Active, QPalette::Window , Qt::black );
    plt.setColor( QPalette::Inactive, QPalette::Window , Qt::black );
    setPalette( plt );

186
    /* A cone in the middle */
187
    label = new QLabel;
188
    label->setMargin( 5 );
189 190
    label->setMaximumHeight( MAX_BG_SIZE );
    label->setMaximumWidth( MAX_BG_SIZE );
191 192
    label->setMinimumHeight( MIN_BG_SIZE );
    label->setMinimumWidth( MIN_BG_SIZE );
193 194 195 196
    if( QDate::currentDate().dayOfYear() >= 354 )
        label->setPixmap( QPixmap( ":/vlc128-christmas.png" ) );
    else
        label->setPixmap( QPixmap( ":/vlc128.png" ) );
197

198 199 200 201
    QGridLayout *backgroundLayout = new QGridLayout( this );
    backgroundLayout->addWidget( label, 0, 1 );
    backgroundLayout->setColumnStretch( 0, 1 );
    backgroundLayout->setColumnStretch( 2, 1 );
202

203 204
    CONNECT( THEMIM->getIM(), artChanged( input_item_t* ),
             this, updateArt( input_item_t* ) );
205 206
}

Clément Stenac's avatar
Clément Stenac committed
207
BackgroundWidget::~BackgroundWidget()
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
208
{}
Ilkka Ollakka's avatar
 
Ilkka Ollakka committed
209

210 211 212 213 214 215 216 217
void BackgroundWidget::resizeEvent( QResizeEvent * event )
{
    if( event->size().height() <= MIN_BG_SIZE )
        label->hide();
    else
        label->show();
}

218
void BackgroundWidget::updateArt( input_item_t *p_item )
219
{
220 221 222 223 224 225 226 227
    QString url;
    if( p_item )
    {
        char *psz_art = input_item_GetArtURL( p_item );
        url = psz_art;
        free( psz_art );
    }

228
    if( url.isEmpty() )
Ilkka Ollakka's avatar
 
Ilkka Ollakka committed
229
    {
230 231 232 233
        if( QDate::currentDate().dayOfYear() >= 354 )
            label->setPixmap( QPixmap( ":/vlc128-christmas.png" ) );
        else
            label->setPixmap( QPixmap( ":/vlc128.png" ) );
Ilkka Ollakka's avatar
 
Ilkka Ollakka committed
234
    }
235
    else
Ilkka Ollakka's avatar
 
Ilkka Ollakka committed
236
    {
237 238 239
        url = url.replace( "file://", QString("" ) );
        /* Taglib seems to define a attachment://, It won't work yet */
        url = url.replace( "attachment://", QString("" ) );
240
        label->setPixmap( QPixmap( url ) );
Ilkka Ollakka's avatar
 
Ilkka Ollakka committed
241
    }
242 243
}

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

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

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

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

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

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

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

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

/**********************************************************************
301
 * TEH controls
Clément Stenac's avatar
Clément Stenac committed
302
 **********************************************************************/
303

304 305 306 307 308 309 310
static void setupSmallButton( QPushButton *aButton )
{
    aButton->setMaximumSize( QSize( 26, 26 ) );
    aButton->setMinimumSize( QSize( 26, 26 ) );
    aButton->setIconSize( QSize( 20, 20 ) );
    aButton->setFocusPolicy( Qt::NoFocus );
}
311

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

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

324
    /* A to B Button */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
325
    ABButton = new QPushButton;
Lukas Durfina's avatar
Lukas Durfina committed
326
    setupSmallButton( ABButton );
327
    advLayout->addWidget( ABButton );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
328
    BUTTON_SET_ACT_I( ABButton, "", atob_nob,
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
329 330
      qtr( "Loop from point A to point B continuously.\nClick to set point A" ),
      fromAtoB() );
331
    timeA = timeB = 0;
332
    i_last_input_id = 0;
333 334 335 336 337 338 339 340 341
    /* 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()));
342
#if 0
343 344 345 346
    frameButton = new QPushButton( "Fr" );
    frameButton->setMaximumSize( QSize( 26, 26 ) );
    frameButton->setIconSize( QSize( 20, 20 ) );
    advLayout->addWidget( frameButton );
347
    BUTTON_SET_ACT( frameButton, "Fr", qtr( "Frame by frame" ), frame() );
348
#endif
349

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

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

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

368
void AdvControlsWidget::enableInput( bool enable )
Clément Stenac's avatar
Clément Stenac committed
369
{
370 371 372 373 374
    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;
375

376
        recordButton->setVisible( var_GetBool( THEMIM->getInput(), "can-record" ) );
377
    }
378
    else
379
    {
380
        recordButton->setVisible( false );
381
    }
382

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 407 408 409 410
    if( p_vout )
    {
        vout_Control( p_vout, VOUT_SNAPSHOT );
        vlc_object_release( p_vout );
    }
Clément Stenac's avatar
Clément Stenac committed
411 412
}

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

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

465 466 467 468 469 470
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 */
471 472
        const bool b_recording = var_GetBool( p_input, "record" );
        var_SetBool( p_input, "record", !b_recording );
473
#if 0
474 475 476 477 478 479 480 481 482 483 484
        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 );
        }
485
#endif
486 487
    }
}
488

489
#if 0
490
//FIXME Frame by frame function
491
void AdvControlsWidget::frame(){}
492
#endif
493

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

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

515
    /** Slower and faster Buttons **/
516 517
    slowerButton = new QToolButton;
    slowerButton->setAutoRaise( true );
518
    slowerButton->setMaximumSize( QSize( 26, 20 ) );
519
    slowerButton->setFocusPolicy( Qt::NoFocus );
520

521
    BUTTON_SET_ACT_I( slowerButton, "", slower, qtr( "Slower" ), slower() );
522

523 524
    fasterButton = new QToolButton;
    fasterButton->setAutoRaise( true );
525
    fasterButton->setMaximumSize( QSize( 26, 20 ) );
526
    fasterButton->setFocusPolicy( Qt::NoFocus );
527

528
    BUTTON_SET_ACT_I( fasterButton, "", faster, qtr( "Faster" ), faster() );
529

530 531 532
    /* advanced Controls handling */
    b_advancedVisible = b_advControls;

533
    advControls = new AdvControlsWidget( p_intf, b_fsCreation );
534 535
    if( !b_advancedVisible ) advControls->hide();

536
    /** Disc and Menus handling */
537
    discFrame = new QWidget( this );
538

539 540 541
    QHBoxLayout *discLayout = new QHBoxLayout( discFrame );
    discLayout->setSpacing( 0 );
    discLayout->setMargin( 0 );
542

543
    prevSectionButton = new QPushButton( discFrame );
544
    setupSmallButton( prevSectionButton );
545 546
    discLayout->addWidget( prevSectionButton );

547
    menuButton = new QPushButton( discFrame );
548 549 550
    setupSmallButton( menuButton );
    discLayout->addWidget( menuButton );

551
    nextSectionButton = new QPushButton( discFrame );
552
    setupSmallButton( nextSectionButton );
553 554
    discLayout->addWidget( nextSectionButton );

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
555 556 557
    BUTTON_SET_IMG( prevSectionButton, "", dvd_prev, "" );
    BUTTON_SET_IMG( nextSectionButton, "", dvd_next, "" );
    BUTTON_SET_IMG( menuButton, "", dvd_menu, qtr( "Menu" ) );
558

559
    discFrame->hide();
560 561 562

    /* Change the navigation button display when the IM navigation changes */
    CONNECT( THEMIM->getIM(), navigationChanged( int ),
563
             this, setNavigation( int ) );
564 565 566 567 568 569 570
    /* 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() );
571

572
    /**
573
     * Telextext QFrame
574
     * TODO: Merge with upper menu in a StackLayout
575
     **/
576 577 578 579 580
    telexFrame = new QWidget( this );
    QHBoxLayout *telexLayout = new QHBoxLayout( telexFrame );
    telexLayout->setSpacing( 0 );
    telexLayout->setMargin( 0 );

Lukas Durfina's avatar
Lukas Durfina committed
581
    telexOn = new QPushButton;
582 583 584
    setupSmallButton( telexOn );
    telexLayout->addWidget( telexOn );

585
    telexTransparent = new QPushButton;
586 587
    setupSmallButton( telexTransparent );
    telexLayout->addWidget( telexTransparent );
588
    b_telexTransparent = false;
589

590
    telexPage = new QSpinBox;
591
    telexPage->setRange( 0, 999 );
592
    telexPage->setValue( 100 );
593 594
    telexPage->setAccelerated( true );
    telexPage->setWrapping( true );
595
    telexPage->setAlignment( Qt::AlignRight );
596
    telexPage->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Minimum );
597 598
    telexLayout->addWidget( telexPage );

599
    telexFrame->hide(); /* default hidden */
600 601 602

    CONNECT( telexPage, valueChanged( int ), THEMIM->getIM(),
             telexGotoPage( int ) );
Lukas Durfina's avatar
Lukas Durfina committed
603 604 605
    CONNECT( THEMIM->getIM(), setNewTelexPage( int ),
              telexPage, setValue( int ) );

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

Lukas Durfina's avatar
Lukas Durfina committed
608 609
    CONNECT( telexOn, clicked(), THEMIM->getIM(),
             telexToggleButtons() );
610 611
    CONNECT( telexOn, clicked( bool ), THEMIM->getIM(),
             telexToggle( bool ) );
Lukas Durfina's avatar
Lukas Durfina committed
612 613 614
    CONNECT( THEMIM->getIM(), toggleTelexButtons(),
              this, toggleTeletext() );
    b_telexEnabled = false;
615 616 617
    telexTransparent->setEnabled( false );
    telexPage->setEnabled( false );

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
618
    BUTTON_SET_IMG( telexTransparent, "", tvtelx, qtr( "Teletext" ) );
619
    CONNECT( telexTransparent, clicked( bool ),
620
             THEMIM->getIM(), telexSetTransparency() );
Lukas Durfina's avatar
Lukas Durfina committed
621 622
    CONNECT( THEMIM->getIM(), toggleTelexTransparency(),
              this, toggleTeletextTransparency() );
623
    CONNECT( THEMIM->getIM(), teletextEnabled( bool ),
624
             this, enableTeletext( bool ) );
625

626
    /** Play Buttons **/
627
    QSizePolicy sizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
628 629 630 631
    sizePolicy.setHorizontalStretch( 0 );
    sizePolicy.setVerticalStretch( 0 );

    /* Play */
632
    playButton = new QPushButton;
633
    playButton->setSizePolicy( sizePolicy );
634 635 636
    playButton->setMaximumSize( QSize( 32, 32 ) );
    playButton->setMinimumSize( QSize( 32, 32 ) );
    playButton->setIconSize( QSize( 26, 26 ) );
637
    playButton->setFocusPolicy( Qt::NoFocus );
638

639
    /** Prev + Stop + Next Block **/
Lukas Durfina's avatar
Lukas Durfina committed
640
    controlButLayout = new QHBoxLayout;
641 642 643 644 645
    controlButLayout->setSpacing( 0 ); /* Don't remove that, will be useful */

    /* Prev */
    QPushButton *prevButton = new QPushButton;
    prevButton->setSizePolicy( sizePolicy );
646
    setupSmallButton( prevButton );
647 648 649 650 651 652

    controlButLayout->addWidget( prevButton );

    /* Stop */
    QPushButton *stopButton = new QPushButton;
    stopButton->setSizePolicy( sizePolicy );
653
    setupSmallButton( stopButton );
654 655 656 657 658 659

    controlButLayout->addWidget( stopButton );

    /* next */
    QPushButton *nextButton = new QPushButton;
    nextButton->setSizePolicy( sizePolicy );
660
    setupSmallButton( nextButton );
661 662 663 664 665

    controlButLayout->addWidget( nextButton );

    /* Add this block to the main layout */

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
666
    BUTTON_SET_ACT_I( playButton, "", play_b, qtr( I_PLAY_TOOLTIP ), play() );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
667
    BUTTON_SET_ACT_I( prevButton, "" , previous_b,
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
668 669 670 671
                      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() );
672 673 674 675

    /*
     * Other first Line buttons
     */
676 677 678
    /* */
    CONNECT( THEMIM->getIM(), voutChanged(bool), this, enableVideo(bool) );

679
    /** Fullscreen/Visualisation **/
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
680
    fullscreenButton = new QPushButton;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
681 682
    BUTTON_SET_ACT_I( fullscreenButton, "", fullscreen,
            qtr( "Toggle the video in fullscreen" ), fullscreen() );
683
    setupSmallButton( fullscreenButton );
684

685 686 687 688 689
    if( !b_fsCreation )
    {
        /** Playlist Button **/
        playlistButton = new QPushButton;
        setupSmallButton( playlistButton );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
690
        BUTTON_SET_IMG( playlistButton, "" , playlist, qtr( "Show playlist" ) );
691 692 693 694
        CONNECT( playlistButton, clicked(), _p_mi, togglePlaylist() );

        /** extended Settings **/
        extSettingsButton = new QPushButton;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
695
        BUTTON_SET_ACT_I( extSettingsButton, "", extended,
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
696
                qtr( "Show extended settings" ), extSettings() );
697 698
        setupSmallButton( extSettingsButton );
    }
699

700
    /* Volume */
Lukas Durfina's avatar
Lukas Durfina committed
701
    hVolLabel = new VolumeClickHandler( p_intf, this );
702

703
    volMuteLabel = new QLabel;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
704
    volMuteLabel->setPixmap( QPixmap( ":/volume-medium" ) );
705
    volMuteLabel->installEventFilter( hVolLabel );
706

707 708 709
    if( b_shiny )
    {
        volumeSlider = new SoundSlider( this,
710
            config_GetInt( p_intf, "volume-step" ),
711 712
            config_GetInt( p_intf, "qt-volume-complete" ),
            config_GetPsz( p_intf, "qt-slider-colours" ) );
713 714 715 716 717 718
    }
    else
    {
        volumeSlider = new QSlider( this );
        volumeSlider->setOrientation( Qt::Horizontal );
    }
719
    volumeSlider->setMaximumSize( QSize( 200, 40 ) );
720
    volumeSlider->setMinimumSize( QSize( 85, 30 ) );
721
    volumeSlider->setFocusPolicy( Qt::NoFocus );
722

723
    /* Set the volume from the config */
724 725
    volumeSlider->setValue( ( config_GetInt( p_intf, "volume" ) ) *
                              VOLUME_MAX / (AOUT_VOLUME_MAX/2) );
726

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

730 731
    /* Volume control connection */
    CONNECT( volumeSlider, valueChanged( int ), this, updateVolume( int ) );
732
    CONNECT( THEMIM, volumeChanged( void ), this, updateVolume( void ) );
733

734 735 736 737 738 739 740
    if( !b_fsCreation )
    {
        controlLayout = new QGridLayout( this );

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

741
        controlLayout->addWidget( slider, 0, 1, 1, 18 );
742
        controlLayout->addWidget( slowerButton, 0, 0 );
743
        controlLayout->addWidget( fasterButton, 0, 19 );
744

745 746
        controlLayout->addWidget( discFrame, 1, 8, 2, 3, Qt::AlignBottom );
        controlLayout->addWidget( telexFrame, 1, 8, 2, 5, Qt::AlignBottom );
747

748 749
        controlLayout->addWidget( playButton, 2, 0, 2, 2, Qt::AlignBottom );
        controlLayout->setColumnMinimumWidth( 2, 10 );
750 751
        controlLayout->setColumnStretch( 2, 0 );

752 753 754
        controlLayout->addLayout( controlButLayout, 3, 3, 1, 3, Qt::AlignBottom );
        /* Column 6 is unused */
        controlLayout->setColumnStretch( 6, 0 );
755
        controlLayout->setColumnStretch( 7, 0 );
756
        controlLayout->setColumnMinimumWidth( 7, 10 );
757

758 759 760 761
        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 */
762

763 764
        controlLayout->setColumnStretch( 12, 0 );
        controlLayout->setColumnMinimumWidth( 12, 10 );
765

766 767 768 769 770 771 772
        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 );
773 774
    }

775
    updateInput();
776
}
777

778
ControlsWidget::~ControlsWidget()
779
{}
780

781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796
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;
    }
}
797

798 799 800 801 802 803 804 805 806 807
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;
}
808 809 810 811 812

void ControlsWidget::toggleTeletextTransparency()
{
    if( b_telexTransparent )
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
813
        telexTransparent->setIcon( QIcon( ":/tvtelx" ) );
814 815 816 817 818
        telexTransparent->setToolTip( qtr( "Teletext" ) );
        b_telexTransparent = false;
    }
    else
    {
Christophe Mutricy's avatar
Christophe Mutricy committed
819
        telexTransparent->setIcon( QIcon( ":/tvtelx-trans" ) );
820 821 822 823 824
        telexTransparent->setToolTip( qtr( "Transparent" ) );
        b_telexTransparent = true;
    }
}

825 826 827 828 829 830 831
void ControlsWidget::stop()
{
    THEMIM->stop();
}

void ControlsWidget::play()
{
832
    if( THEPL->current.i_size == 0 )
833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853
    {
        /* 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 )
{
854 855
#define HELP_PCH N_( "Previous chapter" )
#define HELP_NCH N_( "Next chapter" )
856 857 858 859 860