interface_widgets.cpp 41.6 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
    vout_thread_t *p_vout = i_vout ?
113
        (vout_thread_t *)vlc_object_get( p_intf->p_libvlc, i_vout ) : NULL;
114

115 116
    if( p_vout )
    {
117 118
        if( vout_Control( p_vout, VOUT_CLOSE ) != VLC_SUCCESS )
            vout_Control( p_vout, VOUT_REPARENT );
119
        vlc_object_release( p_vout );
120 121 122
    }
}

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

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

155
void VideoWidget::release( void *p_win )
156
{
Rémi Denis-Courmont's avatar
Typo  
Rémi Denis-Courmont committed
157
    msg_Dbg( p_intf, "Video is not needed anymore" );
158
    i_vout = 0;
159 160
    videoSize.rwidth() = 0;
    videoSize.rheight() = 0;
161
    updateGeometry();
162
    hide();
163
}
164

165 166 167 168
QSize VideoWidget::sizeHint() const
{
    return videoSize;
}
169

170 171
/**********************************************************************
 * Background Widget. Show a simple image background. Currently,
172
 * it's album art if present or cone.
173
 **********************************************************************/
174 175
#define ICON_SIZE 128
#define MAX_BG_SIZE 400
176
#define MIN_BG_SIZE 128
177

178 179
BackgroundWidget::BackgroundWidget( intf_thread_t *_p_i )
                 :QWidget( NULL ), p_intf( _p_i )
180
{
181
    /* We should use that one to take the more size it can */
182
    setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding);
183

184
    /* A dark background */
Clément Stenac's avatar
Clément Stenac committed
185
    setAutoFillBackground( true );
186
    plt = palette();
187 188 189 190
    plt.setColor( QPalette::Active, QPalette::Window , Qt::black );
    plt.setColor( QPalette::Inactive, QPalette::Window , Qt::black );
    setPalette( plt );

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

203 204 205 206
    QGridLayout *backgroundLayout = new QGridLayout( this );
    backgroundLayout->addWidget( label, 0, 1 );
    backgroundLayout->setColumnStretch( 0, 1 );
    backgroundLayout->setColumnStretch( 2, 1 );
207

208
    CONNECT( THEMIM->getIM(), artChanged( QString ), this, updateArt( QString ) );
209 210
}

Clément Stenac's avatar
Clément Stenac committed
211
BackgroundWidget::~BackgroundWidget()
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
212
{}
Ilkka Ollakka's avatar
 
Ilkka Ollakka committed
213

214 215 216 217 218 219 220 221
void BackgroundWidget::resizeEvent( QResizeEvent * event )
{
    if( event->size().height() <= MIN_BG_SIZE )
        label->hide();
    else
        label->show();
}

222
void BackgroundWidget::updateArt( QString url )
223
{
224
    if( url.isEmpty() )
Ilkka Ollakka's avatar
 
Ilkka Ollakka committed
225
    {
226 227 228 229
        if( QDate::currentDate().dayOfYear() >= 354 )
            label->setPixmap( QPixmap( ":/vlc128-christmas.png" ) );
        else
            label->setPixmap( QPixmap( ":/vlc128.png" ) );
Ilkka Ollakka's avatar
 
Ilkka Ollakka committed
230 231
        return;
    }
232
    else
Ilkka Ollakka's avatar
 
Ilkka Ollakka committed
233
    {
234
        label->setPixmap( QPixmap( url ) );
Ilkka Ollakka's avatar
 
Ilkka Ollakka committed
235
    }
236 237
}

238 239 240 241
void BackgroundWidget::contextMenuEvent( QContextMenuEvent *event )
{
    QVLCMenu::PopupMenu( p_intf, true );
}
242

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

257
    layout->addStretch( 10 );
258
    layout->addWidget( new QLabel( qtr( "Current visualization" ) ) );
Clément Stenac's avatar
Clément Stenac committed
259 260 261 262 263 264 265

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

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

Clément Stenac's avatar
Clément Stenac committed
266
    setLayout( layout );
267
    setMaximumHeight( 35 );
Clément Stenac's avatar
Clément Stenac committed
268 269 270
}

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

Clément Stenac's avatar
Clément Stenac committed
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
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
292
#endif
Clément Stenac's avatar
Clément Stenac committed
293 294

/**********************************************************************
295
 * TEH controls
Clément Stenac's avatar
Clément Stenac committed
296
 **********************************************************************/
297 298 299 300 301 302

#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
303 304 305 306
/* init static variables in advanced controls */
mtime_t AdvControlsWidget::timeA = 0;
mtime_t AdvControlsWidget::timeB = 0;

307
AdvControlsWidget::AdvControlsWidget( intf_thread_t *_p_i, bool b_fsCreation = false ) :
Clément Stenac's avatar
Clément Stenac committed
308 309
                                           QFrame( NULL ), p_intf( _p_i )
{
310 311 312
    QHBoxLayout *advLayout = new QHBoxLayout( this );
    advLayout->setMargin( 0 );
    advLayout->setSpacing( 0 );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
313
    advLayout->setAlignment( Qt::AlignBottom );
Clément Stenac's avatar
Clément Stenac committed
314

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

341
    /* Record Button */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
342
    recordButton = new QPushButton;
Lukas Durfina's avatar
Lukas Durfina committed
343
    setupSmallButton( recordButton );
344
    advLayout->addWidget( recordButton );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
345
    BUTTON_SET_ACT_I( recordButton, "", record,
346
            qtr( "Record" ), record() );
347

348
    /* Snapshot Button */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
349
    snapshotButton = new QPushButton;
Lukas Durfina's avatar
Lukas Durfina committed
350
    setupSmallButton( snapshotButton );
351
    advLayout->addWidget( snapshotButton );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
352 353
    BUTTON_SET_ACT_I( snapshotButton, "", snapshot,
            qtr( "Take a snapshot" ), snapshot() );
Clément Stenac's avatar
Clément Stenac committed
354 355
}

356
AdvControlsWidget::~AdvControlsWidget()
357
{}
Clément Stenac's avatar
Clément Stenac committed
358

359
void AdvControlsWidget::enableInput( bool enable )
Clément Stenac's avatar
Clément Stenac committed
360
{
361 362 363 364 365
    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;
366

367
        recordButton->setVisible( var_GetBool( THEMIM->getInput(), "can-record" ) );
368
    }
369
    else
370
    {
371
        recordButton->setVisible( false );
372
    }
373

374 375
    ABButton->setEnabled( enable );
    recordButton->setEnabled( enable );
376 377 378 379 380 381 382

    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
383
}
384

385
void AdvControlsWidget::enableVideo( bool enable )
Clément Stenac's avatar
Clément Stenac committed
386 387
{
    snapshotButton->setEnabled( enable );
388
#if 0
389
    frameButton->setEnabled( enable );
390
#endif
Clément Stenac's avatar
Clément Stenac committed
391 392
}

393
void AdvControlsWidget::snapshot()
Clément Stenac's avatar
Clément Stenac committed
394
{
395 396
    vout_thread_t *p_vout =
        (vout_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_VOUT, FIND_ANYWHERE );
397
    if( p_vout ) vout_Control( p_vout, VOUT_SNAPSHOT );
Clément Stenac's avatar
Clément Stenac committed
398 399
}

400
/* Function called when the button is clicked() */
401 402 403 404 405
void AdvControlsWidget::fromAtoB()
{
    if( !timeA )
    {
        timeA = var_GetTime( THEMIM->getInput(), "time"  );
406
        emit timeChanged();
407 408 409 410 411 412
        return;
    }
    if( !timeB )
    {
        timeB = var_GetTime( THEMIM->getInput(), "time"  );
        var_SetTime( THEMIM->getInput(), "time" , timeA );
413
        emit timeChanged();
414 415 416 417
        return;
    }
    timeA = 0;
    timeB = 0;
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
    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" ) );
    }
439
}
440

441
/* Function called regularly when in an AtoB loop */
442 443 444 445
void AdvControlsWidget::AtoBLoop( float f_pos, int i_time, int i_length )
{
    if( timeB )
    {
446 447
        if( ( i_time >= (int)( timeB/1000000 ) )
            || ( i_time < (int)( timeA/1000000 ) ) )
448 449 450
            var_SetTime( THEMIM->getInput(), "time" , timeA );
    }
}
451

452 453 454 455 456 457
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 */
458 459
        const bool b_recording = var_GetBool( p_input, "record" );
        var_SetBool( p_input, "record", !b_recording );
460
#if 0
461 462 463 464 465 466 467 468 469 470 471
        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 );
        }
472
#endif
473 474
    }
}
475

476
#if 0
477
//FIXME Frame by frame function
478
void AdvControlsWidget::frame(){}
479
#endif
480

481 482 483
/*****************************
 * DA Control Widget !
 *****************************/
484
ControlsWidget::ControlsWidget( intf_thread_t *_p_i,
485
                                MainInterface *_p_mi,
486
                                bool b_advControls,
Lukas Durfina's avatar
Lukas Durfina committed
487 488 489
                                bool b_shiny,
                                bool b_fsCreation) :
                                QFrame( _p_mi ), p_intf( _p_i )
490
{
491
    setSizePolicy( QSizePolicy::Preferred , QSizePolicy::Maximum );
492

493 494 495 496
    /** The main Slider **/
    slider = new InputSlider( Qt::Horizontal, NULL );
    /* Update the position when the IM has changed */
    CONNECT( THEMIM->getIM(), positionUpdated( float, int, int ),
497
             slider, setPosition( float, int, int ) );
498 499 500 501
    /* And update the IM, when the position has changed */
    CONNECT( slider, sliderDragged( float ),
             THEMIM->getIM(), sliderUpdate( float ) );

502
    /** Slower and faster Buttons **/
503 504
    slowerButton = new QToolButton;
    slowerButton->setAutoRaise( true );
505
    slowerButton->setMaximumSize( QSize( 26, 20 ) );
506

507
    BUTTON_SET_ACT( slowerButton, "-", qtr( "Slower" ), slower() );
508

509 510
    fasterButton = new QToolButton;
    fasterButton->setAutoRaise( true );
511
    fasterButton->setMaximumSize( QSize( 26, 20 ) );
512

513
    BUTTON_SET_ACT( fasterButton, "+", qtr( "Faster" ), faster() );
514

515 516 517
    /* advanced Controls handling */
    b_advancedVisible = b_advControls;

518
    advControls = new AdvControlsWidget( p_intf, b_fsCreation );
519 520
    if( !b_advancedVisible ) advControls->hide();

521
    /** Disc and Menus handling */
522
    discFrame = new QWidget( this );
523

524 525 526
    QHBoxLayout *discLayout = new QHBoxLayout( discFrame );
    discLayout->setSpacing( 0 );
    discLayout->setMargin( 0 );
527

528
    prevSectionButton = new QPushButton( discFrame );
529
    setupSmallButton( prevSectionButton );
530 531
    discLayout->addWidget( prevSectionButton );

532
    menuButton = new QPushButton( discFrame );
533 534 535
    setupSmallButton( menuButton );
    discLayout->addWidget( menuButton );

536
    nextSectionButton = new QPushButton( discFrame );
537
    setupSmallButton( nextSectionButton );
538 539
    discLayout->addWidget( nextSectionButton );

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
540 541 542
    BUTTON_SET_IMG( prevSectionButton, "", dvd_prev, "" );
    BUTTON_SET_IMG( nextSectionButton, "", dvd_next, "" );
    BUTTON_SET_IMG( menuButton, "", dvd_menu, qtr( "Menu" ) );
543

544
    discFrame->hide();
545 546 547

    /* Change the navigation button display when the IM navigation changes */
    CONNECT( THEMIM->getIM(), navigationChanged( int ),
548
             this, setNavigation( int ) );
549 550 551 552 553 554 555
    /* 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() );
556

557
    /**
558
     * Telextext QFrame
559
     * TODO: Merge with upper menu in a StackLayout
560
     **/
561 562 563 564 565
    telexFrame = new QWidget( this );
    QHBoxLayout *telexLayout = new QHBoxLayout( telexFrame );
    telexLayout->setSpacing( 0 );
    telexLayout->setMargin( 0 );

Lukas Durfina's avatar
Lukas Durfina committed
566
    telexOn = new QPushButton;
567 568 569
    setupSmallButton( telexOn );
    telexLayout->addWidget( telexOn );

570
    telexTransparent = new QPushButton;
571 572
    setupSmallButton( telexTransparent );
    telexLayout->addWidget( telexTransparent );
573
    b_telexTransparent = false;
574

575
    telexPage = new QSpinBox;
576
    telexPage->setRange( 0, 999 );
577
    telexPage->setValue( 100 );
578 579
    telexPage->setAccelerated( true );
    telexPage->setWrapping( true );
580
    telexPage->setAlignment( Qt::AlignRight );
581
    telexPage->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Minimum );
582 583
    telexLayout->addWidget( telexPage );

584
    telexFrame->hide(); /* default hidden */
585 586 587

    CONNECT( telexPage, valueChanged( int ), THEMIM->getIM(),
             telexGotoPage( int ) );
Lukas Durfina's avatar
Lukas Durfina committed
588 589 590
    CONNECT( THEMIM->getIM(), setNewTelexPage( int ),
              telexPage, setValue( int ) );

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

Lukas Durfina's avatar
Lukas Durfina committed
593 594
    CONNECT( telexOn, clicked(), THEMIM->getIM(),
             telexToggleButtons() );
595 596
    CONNECT( telexOn, clicked( bool ), THEMIM->getIM(),
             telexToggle( bool ) );
Lukas Durfina's avatar
Lukas Durfina committed
597 598 599
    CONNECT( THEMIM->getIM(), toggleTelexButtons(),
              this, toggleTeletext() );
    b_telexEnabled = false;
600 601 602
    telexTransparent->setEnabled( false );
    telexPage->setEnabled( false );

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
603
    BUTTON_SET_IMG( telexTransparent, "", tvtelx, qtr( "Teletext" ) );
604
    CONNECT( telexTransparent, clicked( bool ),
605
             THEMIM->getIM(), telexSetTransparency() );
Lukas Durfina's avatar
Lukas Durfina committed
606 607
    CONNECT( THEMIM->getIM(), toggleTelexTransparency(),
              this, toggleTeletextTransparency() );
608
    CONNECT( THEMIM->getIM(), teletextEnabled( bool ),
609
             this, enableTeletext( bool ) );
610

611
    /** Play Buttons **/
612
    QSizePolicy sizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
613 614 615 616
    sizePolicy.setHorizontalStretch( 0 );
    sizePolicy.setVerticalStretch( 0 );

    /* Play */
617
    playButton = new QPushButton;
618
    playButton->setSizePolicy( sizePolicy );
619 620
    playButton->setMaximumSize( QSize( 36, 36 ) );
    playButton->setMinimumSize( QSize( 36, 36 ) );
621
    playButton->setIconSize( QSize( 30, 30 ) );
622

623

624
    /** Prev + Stop + Next Block **/
Lukas Durfina's avatar
Lukas Durfina committed
625
    controlButLayout = new QHBoxLayout;
626 627 628 629 630
    controlButLayout->setSpacing( 0 ); /* Don't remove that, will be useful */

    /* Prev */
    QPushButton *prevButton = new QPushButton;
    prevButton->setSizePolicy( sizePolicy );
631
    setupSmallButton( prevButton );
632 633 634 635 636 637

    controlButLayout->addWidget( prevButton );

    /* Stop */
    QPushButton *stopButton = new QPushButton;
    stopButton->setSizePolicy( sizePolicy );
638
    setupSmallButton( stopButton );
639 640 641 642 643 644

    controlButLayout->addWidget( stopButton );

    /* next */
    QPushButton *nextButton = new QPushButton;
    nextButton->setSizePolicy( sizePolicy );
645
    setupSmallButton( nextButton );
646 647 648 649 650

    controlButLayout->addWidget( nextButton );

    /* Add this block to the main layout */

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
651
    BUTTON_SET_ACT_I( playButton, "", play_b, qtr( I_PLAY_TOOLTIP ), play() );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
652
    BUTTON_SET_ACT_I( prevButton, "" , previous_b,
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
653 654 655 656
                      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() );
657 658 659 660

    /*
     * Other first Line buttons
     */
661
    /** Fullscreen/Visualisation **/
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
662
    fullscreenButton = new QPushButton;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
663 664
    BUTTON_SET_ACT_I( fullscreenButton, "", fullscreen,
            qtr( "Toggle the video in fullscreen" ), fullscreen() );
665
    setupSmallButton( fullscreenButton );
666

667 668 669 670 671
    if( !b_fsCreation )
    {
        /** Playlist Button **/
        playlistButton = new QPushButton;
        setupSmallButton( playlistButton );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
672
        BUTTON_SET_IMG( playlistButton, "" , playlist, qtr( "Show playlist" ) );
673 674 675 676
        CONNECT( playlistButton, clicked(), _p_mi, togglePlaylist() );

        /** extended Settings **/
        extSettingsButton = new QPushButton;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
677
        BUTTON_SET_ACT_I( extSettingsButton, "", extended,
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
678
                qtr( "Show extended settings" ), extSettings() );
679 680
        setupSmallButton( extSettingsButton );
    }
681

682
    /* Volume */
Lukas Durfina's avatar
Lukas Durfina committed
683
    hVolLabel = new VolumeClickHandler( p_intf, this );
684

685
    volMuteLabel = new QLabel;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
686
    volMuteLabel->setPixmap( QPixmap( ":/volume-medium" ) );
687
    volMuteLabel->installEventFilter( hVolLabel );
688

689 690 691
    if( b_shiny )
    {
        volumeSlider = new SoundSlider( this,
692
            config_GetInt( p_intf, "volume-step" ),
693 694
            config_GetInt( p_intf, "qt-volume-complete" ),
            config_GetPsz( p_intf, "qt-slider-colours" ) );
695 696 697 698 699 700
    }
    else
    {
        volumeSlider = new QSlider( this );
        volumeSlider->setOrientation( Qt::Horizontal );
    }
701
    volumeSlider->setMaximumSize( QSize( 200, 40 ) );
702
    volumeSlider->setMinimumSize( QSize( 85, 30 ) );
703
    volumeSlider->setFocusPolicy( Qt::NoFocus );
704

705
    /* Set the volume from the config */
706 707
    volumeSlider->setValue( ( config_GetInt( p_intf, "volume" ) ) *
                              VOLUME_MAX / (AOUT_VOLUME_MAX/2) );
708

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

712 713
    /* Volume control connection */
    CONNECT( volumeSlider, valueChanged( int ), this, updateVolume( int ) );
714
    CONNECT( THEMIM, volumeChanged( void ), this, updateVolume( void ) );
715

716 717 718 719 720 721 722
    if( !b_fsCreation )
    {
        controlLayout = new QGridLayout( this );

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

723
        controlLayout->addWidget( slider, 0, 1, 1, 18 );
724
        controlLayout->addWidget( slowerButton, 0, 0 );
725
        controlLayout->addWidget( fasterButton, 0, 19 );
726

727 728
        controlLayout->addWidget( discFrame, 1, 8, 2, 3, Qt::AlignBottom );
        controlLayout->addWidget( telexFrame, 1, 8, 2, 5, Qt::AlignBottom );
729

730 731
        controlLayout->addWidget( playButton, 2, 0, 2, 2, Qt::AlignBottom );
        controlLayout->setColumnMinimumWidth( 2, 10 );
732 733
        controlLayout->setColumnStretch( 2, 0 );

734 735 736
        controlLayout->addLayout( controlButLayout, 3, 3, 1, 3, Qt::AlignBottom );
        /* Column 6 is unused */
        controlLayout->setColumnStretch( 6, 0 );
737
        controlLayout->setColumnStretch( 7, 0 );
738
        controlLayout->setColumnMinimumWidth( 7, 10 );
739

740 741 742 743
        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 */
744

745 746
        controlLayout->setColumnStretch( 12, 0 );
        controlLayout->setColumnMinimumWidth( 12, 10 );
747

748 749 750 751 752 753 754
        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 );
755 756
    }

757
    updateInput();
758
}
759

760
ControlsWidget::~ControlsWidget()
761
{}
762

763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
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;
    }
}
779

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

void ControlsWidget::toggleTeletextTransparency()
{
    if( b_telexTransparent )
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
795
        telexTransparent->setIcon( QIcon( ":/tvtelx" ) );
796 797 798 799 800
        telexTransparent->setToolTip( qtr( "Teletext" ) );
        b_telexTransparent = false;
    }
    else
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
801
        telexTransparent->setIcon( QIcon( ":/tvtelx-transparent" ) );
802 803 804 805 806
        telexTransparent->setToolTip( qtr( "Transparent" ) );
        b_telexTransparent = true;
    }
}

807 808 809 810 811 812 813
void ControlsWidget::stop()
{
    THEMIM->stop();
}

void ControlsWidget::play()
{
814
    if( THEPL->current.i_size == 0 )
815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835
    {
        /* 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 )
{
836 837
#define HELP_PCH N_( "Previous chapter" )
#define HELP_NCH N_( "Next chapter" )
838 839 840 841 842 843

    // 1 = chapter, 2 = title, 0 = no
    if( navigation == 0 )
    {
        discFrame->hide();
    } else if( navigation == 1 ) {
844 845
        prevSectionButton->setToolTip( qtr( HELP_PCH ) );
        nextSectionButton->setToolTip( qtr( HELP_NCH ) );
846 847 848
        menuButton->show();
        discFrame->show();
    } else {
849