interface_widgets.cpp 44.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 209
    CONNECT( THEMIM->getIM(), artChanged( input_item_t* ),
             this, updateArt( input_item_t* ) );
210 211
}

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

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

223
void BackgroundWidget::updateArt( input_item_t *p_item )
224
{
225 226 227 228 229 230 231 232
    QString url;
    if( p_item )
    {
        char *psz_art = input_item_GetArtURL( p_item );
        url = psz_art;
        free( psz_art );
    }

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

249 250 251 252
void BackgroundWidget::contextMenuEvent( QContextMenuEvent *event )
{
    QVLCMenu::PopupMenu( p_intf, true );
}
253

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

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

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

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

Clément Stenac's avatar
Clément Stenac committed
277
    setLayout( layout );
278
    setMaximumHeight( 35 );
Clément Stenac's avatar
Clément Stenac committed
279 280 281
}

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

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

/**********************************************************************
306
 * TEH controls
Clément Stenac's avatar
Clément Stenac committed
307
 **********************************************************************/
308 309 310 311 312 313

#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
314 315 316 317
/* init static variables in advanced controls */
mtime_t AdvControlsWidget::timeA = 0;
mtime_t AdvControlsWidget::timeB = 0;

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

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

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

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

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

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

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

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

    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
394
}
395

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

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

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

452
/* Function called regularly when in an AtoB loop */
453 454 455 456
void AdvControlsWidget::AtoBLoop( float f_pos, int i_time, int i_length )
{
    if( timeB )
    {
457 458
        if( ( i_time >= (int)( timeB/1000000 ) )
            || ( i_time < (int)( timeA/1000000 ) ) )
459 460 461
            var_SetTime( THEMIM->getInput(), "time" , timeA );
    }
}
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 */
469 470
        const bool b_recording = var_GetBool( p_input, "record" );
        var_SetBool( p_input, "record", !b_recording );
471
#if 0
472 473 474 475 476 477 478 479 480 481 482
        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 );
        }
483
#endif
484 485
    }
}
486

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

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

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

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

518
    BUTTON_SET_ACT( slowerButton, "-", qtr( "Slower" ), slower() );
519

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

524
    BUTTON_SET_ACT( fasterButton, "+", qtr( "Faster" ), faster() );
525

526 527 528
    /* advanced Controls handling */
    b_advancedVisible = b_advControls;

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

532
    /** Disc and Menus handling */
533
    discFrame = new QWidget( this );
534

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

539
    prevSectionButton = new QPushButton( discFrame );
540
    setupSmallButton( prevSectionButton );
541 542
    discLayout->addWidget( prevSectionButton );

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

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

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

555
    discFrame->hide();
556 557 558

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

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

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

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

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

595
    telexFrame->hide(); /* default hidden */
596 597 598

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

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

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

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

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

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

634

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

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

    controlButLayout->addWidget( prevButton );

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

    controlButLayout->addWidget( stopButton );

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

    controlButLayout->addWidget( nextButton );

    /* Add this block to the main layout */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

751 752 753 754
        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 */
755

756 757
        controlLayout->setColumnStretch( 12, 0 );
        controlLayout->setColumnMinimumWidth( 12, 10 );
758

759 760 761 762 763 764 765
        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 );
766 767
    }

768
    updateInput();
769
}
770

771
ControlsWidget::~ControlsWidget()
772
{}
773

774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789
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;
    }
}
790

791 792 793 794 795 796 797 798 799 800
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;
}
801 802 803 804 805

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

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

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

    // 1 = chapter, 2 = title, 0 = no
    if( navigation == 0 )
    {
        discFrame