interface_widgets.cpp 45.5 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 314 315
static void setupSmallButton( QPushButton *aButton )
{
    aButton->setMaximumSize( QSize( 26, 26 ) );
    aButton->setMinimumSize( QSize( 26, 26 ) );
    aButton->setIconSize( QSize( 20, 20 ) );
    aButton->setFocusPolicy( Qt::NoFocus );
}
316

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
317 318 319 320
/* init static variables in advanced controls */
mtime_t AdvControlsWidget::timeA = 0;
mtime_t AdvControlsWidget::timeB = 0;

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

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

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

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

370
AdvControlsWidget::~AdvControlsWidget()
371
{}
Clément Stenac's avatar
Clément Stenac committed
372

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

381
        recordButton->setVisible( var_GetBool( THEMIM->getInput(), "can-record" ) );
382
    }
383
    else
384
    {
385
        recordButton->setVisible( false );
386
    }
387

388 389
    ABButton->setEnabled( enable );
    recordButton->setEnabled( enable );
390 391 392 393 394 395 396

    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
397
}
398

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

407
void AdvControlsWidget::snapshot()
Clément Stenac's avatar
Clément Stenac committed
408
{
409 410
    vout_thread_t *p_vout =
        (vout_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_VOUT, FIND_ANYWHERE );
411 412 413 414 415
    if( p_vout )
    {
        vout_Control( p_vout, VOUT_SNAPSHOT );
        vlc_object_release( p_vout );
    }
Clément Stenac's avatar
Clément Stenac committed
416 417
}

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

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

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

494
#if 0
495
//FIXME Frame by frame function
496
void AdvControlsWidget::frame(){}
497
#endif
498

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

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

520
    /** Slower and faster Buttons **/
521 522
    slowerButton = new QToolButton;
    slowerButton->setAutoRaise( true );
523
    slowerButton->setMaximumSize( QSize( 26, 20 ) );
524
    slowerButton->setFocusPolicy( Qt::NoFocus );
525

526
    BUTTON_SET_ACT( slowerButton, "-", qtr( "Slower" ), slower() );
527

528 529
    fasterButton = new QToolButton;
    fasterButton->setAutoRaise( true );
530
    fasterButton->setMaximumSize( QSize( 26, 20 ) );
531
    fasterButton->setFocusPolicy( Qt::NoFocus );
532

533
    BUTTON_SET_ACT( fasterButton, "+", qtr( "Faster" ), faster() );
534

535 536 537
    /* advanced Controls handling */
    b_advancedVisible = b_advControls;

538
    advControls = new AdvControlsWidget( p_intf, b_fsCreation );
539 540
    if( !b_advancedVisible ) advControls->hide();

541
    /** Disc and Menus handling */
542
    discFrame = new QWidget( this );
543

544 545 546
    QHBoxLayout *discLayout = new QHBoxLayout( discFrame );
    discLayout->setSpacing( 0 );
    discLayout->setMargin( 0 );
547

548
    prevSectionButton = new QPushButton( discFrame );
549
    setupSmallButton( prevSectionButton );
550 551
    discLayout->addWidget( prevSectionButton );

552
    menuButton = new QPushButton( discFrame );
553 554 555
    setupSmallButton( menuButton );
    discLayout->addWidget( menuButton );

556
    nextSectionButton = new QPushButton( discFrame );
557
    setupSmallButton( nextSectionButton );
558 559
    discLayout->addWidget( nextSectionButton );

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
560 561 562
    BUTTON_SET_IMG( prevSectionButton, "", dvd_prev, "" );
    BUTTON_SET_IMG( nextSectionButton, "", dvd_next, "" );
    BUTTON_SET_IMG( menuButton, "", dvd_menu, qtr( "Menu" ) );
563

564
    discFrame->hide();
565 566 567

    /* Change the navigation button display when the IM navigation changes */
    CONNECT( THEMIM->getIM(), navigationChanged( int ),
568
             this, setNavigation( int ) );
569 570 571 572 573 574 575
    /* 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() );
576

577
    /**
578
     * Telextext QFrame
579
     * TODO: Merge with upper menu in a StackLayout
580
     **/
581 582 583 584 585
    telexFrame = new QWidget( this );
    QHBoxLayout *telexLayout = new QHBoxLayout( telexFrame );
    telexLayout->setSpacing( 0 );
    telexLayout->setMargin( 0 );

Lukas Durfina's avatar
Lukas Durfina committed
586
    telexOn = new QPushButton;
587 588 589
    setupSmallButton( telexOn );
    telexLayout->addWidget( telexOn );

590
    telexTransparent = new QPushButton;
591 592
    setupSmallButton( telexTransparent );
    telexLayout->addWidget( telexTransparent );
593
    b_telexTransparent = false;
594

595
    telexPage = new QSpinBox;
596
    telexPage->setRange( 0, 999 );
597
    telexPage->setValue( 100 );
598 599
    telexPage->setAccelerated( true );
    telexPage->setWrapping( true );
600
    telexPage->setAlignment( Qt::AlignRight );
601
    telexPage->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Minimum );
602 603
    telexLayout->addWidget( telexPage );

604
    telexFrame->hide(); /* default hidden */
605 606 607

    CONNECT( telexPage, valueChanged( int ), THEMIM->getIM(),
             telexGotoPage( int ) );
Lukas Durfina's avatar
Lukas Durfina committed
608 609 610
    CONNECT( THEMIM->getIM(), setNewTelexPage( int ),
              telexPage, setValue( int ) );

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

Lukas Durfina's avatar
Lukas Durfina committed
613 614
    CONNECT( telexOn, clicked(), THEMIM->getIM(),
             telexToggleButtons() );
615 616
    CONNECT( telexOn, clicked( bool ), THEMIM->getIM(),
             telexToggle( bool ) );
Lukas Durfina's avatar
Lukas Durfina committed
617 618 619
    CONNECT( THEMIM->getIM(), toggleTelexButtons(),
              this, toggleTeletext() );
    b_telexEnabled = false;
620 621 622
    telexTransparent->setEnabled( false );
    telexPage->setEnabled( false );

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
623
    BUTTON_SET_IMG( telexTransparent, "", tvtelx, qtr( "Teletext" ) );
624
    CONNECT( telexTransparent, clicked( bool ),
625
             THEMIM->getIM(), telexSetTransparency() );
Lukas Durfina's avatar
Lukas Durfina committed
626 627
    CONNECT( THEMIM->getIM(), toggleTelexTransparency(),
              this, toggleTeletextTransparency() );
628
    CONNECT( THEMIM->getIM(), teletextEnabled( bool ),
629
             this, enableTeletext( bool ) );
630

631
    /** Play Buttons **/
632
    QSizePolicy sizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
633 634 635 636
    sizePolicy.setHorizontalStretch( 0 );
    sizePolicy.setVerticalStretch( 0 );

    /* Play */
637
    playButton = new QPushButton;
638
    playButton->setSizePolicy( sizePolicy );
639 640 641
    playButton->setMaximumSize( QSize( 32, 32 ) );
    playButton->setMinimumSize( QSize( 32, 32 ) );
    playButton->setIconSize( QSize( 26, 26 ) );
642
    playButton->setFocusPolicy( Qt::NoFocus );
643

644
    /** Prev + Stop + Next Block **/
Lukas Durfina's avatar
Lukas Durfina committed
645
    controlButLayout = new QHBoxLayout;
646 647 648 649 650
    controlButLayout->setSpacing( 0 ); /* Don't remove that, will be useful */

    /* Prev */
    QPushButton *prevButton = new QPushButton;
    prevButton->setSizePolicy( sizePolicy );
651
    setupSmallButton( prevButton );
652 653 654 655 656 657

    controlButLayout->addWidget( prevButton );

    /* Stop */
    QPushButton *stopButton = new QPushButton;
    stopButton->setSizePolicy( sizePolicy );
658
    setupSmallButton( stopButton );
659 660 661 662 663 664

    controlButLayout->addWidget( stopButton );

    /* next */
    QPushButton *nextButton = new QPushButton;
    nextButton->setSizePolicy( sizePolicy );
665
    setupSmallButton( nextButton );
666 667 668 669 670

    controlButLayout->addWidget( nextButton );

    /* Add this block to the main layout */

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
671
    BUTTON_SET_ACT_I( playButton, "", play_b, qtr( I_PLAY_TOOLTIP ), play() );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
672
    BUTTON_SET_ACT_I( prevButton, "" , previous_b,
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
673 674 675 676
                      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() );
677 678 679 680

    /*
     * Other first Line buttons
     */
681 682 683
    /* */
    CONNECT( THEMIM->getIM(), voutChanged(bool), this, enableVideo(bool) );

684
    /** Fullscreen/Visualisation **/
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
685
    fullscreenButton = new QPushButton;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
686 687
    BUTTON_SET_ACT_I( fullscreenButton, "", fullscreen,
            qtr( "Toggle the video in fullscreen" ), fullscreen() );
688
    setupSmallButton( fullscreenButton );
689

690 691 692 693 694
    if( !b_fsCreation )
    {
        /** Playlist Button **/
        playlistButton = new QPushButton;
        setupSmallButton( playlistButton );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
695
        BUTTON_SET_IMG( playlistButton, "" , playlist, qtr( "Show playlist" ) );
696 697 698 699
        CONNECT( playlistButton, clicked(), _p_mi, togglePlaylist() );

        /** extended Settings **/
        extSettingsButton = new QPushButton;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
700
        BUTTON_SET_ACT_I( extSettingsButton, "", extended,
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
701
                qtr( "Show extended settings" ), extSettings() );
702 703
        setupSmallButton( extSettingsButton );
    }
704

705
    /* Volume */
Lukas Durfina's avatar
Lukas Durfina committed
706
    hVolLabel = new VolumeClickHandler( p_intf, this );
707

708
    volMuteLabel = new QLabel;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
709
    volMuteLabel->setPixmap( QPixmap( ":/volume-medium" ) );
710
    volMuteLabel->installEventFilter( hVolLabel );
711

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

728
    /* Set the volume from the config */
729 730
    volumeSlider->setValue( ( config_GetInt( p_intf, "volume" ) ) *
                              VOLUME_MAX / (AOUT_VOLUME_MAX/2) );
731

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

735 736
    /* Volume control connection */
    CONNECT( volumeSlider, valueChanged( int ), this, updateVolume( int ) );
737
    CONNECT( THEMIM, volumeChanged( void ), this, updateVolume( void ) );
738

739 740 741 742 743 744 745
    if( !b_fsCreation )
    {
        controlLayout = new QGridLayout( this );

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

746
        controlLayout->addWidget( slider, 0, 1, 1, 18 );
747
        controlLayout->addWidget( slowerButton, 0, 0 );
748
        controlLayout->addWidget( fasterButton, 0, 19 );
749

750 751
        controlLayout->addWidget( discFrame, 1, 8, 2, 3, Qt::AlignBottom );
        controlLayout->addWidget( telexFrame, 1, 8, 2, 5, Qt::AlignBottom );
752

753 754
        controlLayout->addWidget( playButton, 2, 0, 2, 2, Qt::AlignBottom );
        controlLayout->setColumnMinimumWidth( 2, 10 );
755 756
        controlLayout->setColumnStretch( 2, 0 );

757 758 759
        controlLayout->addLayout( controlButLayout, 3, 3, 1, 3, Qt::AlignBottom );
        /* Column 6 is unused */
        controlLayout->setColumnStretch( 6, 0 );
760
        controlLayout->setColumnStretch( 7, 0 );
761
        controlLayout->setColumnMinimumWidth( 7, 10 );
762

763 764 765 766
        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 */
767

768 769
        controlLayout->setColumnStretch( 12, 0 );
        controlLayout->setColumnMinimumWidth( 12, 10 );
770

771 772 773 774 775 776 777
        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 );
778 779
    }

780
    updateInput();
781
}
782

783
ControlsWidget::~ControlsWidget()
784
{}
785

786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801
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;
    }
}
802

803 804 805 806 807 808 809 810 811 812
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;
}
813 814 815 816 817

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

830 831 832 833 834 835 836
void ControlsWidget::stop()
{
    THEMIM->stop();
}

void ControlsWidget::play()
{
837
    if( THEPL->current.i_size == 0 )
838 839 840 841