interface_widgets.cpp 41.4 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
    hide(); setMinimumSize( 16, 16 );
71 72
    videoSize.rwidth() = -1;
    videoSize.rheight() = -1;
73
    setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
74

75 76 77 78 79
    /* Black background is more coherent for a Video Widget IMVHO */
    QPalette plt =  palette();
    plt.setColor( QPalette::Active, QPalette::Window , Qt::black );
    plt.setColor( QPalette::Inactive, QPalette::Window , Qt::black );
    setPalette( plt );
80
    setAttribute( Qt::WA_PaintOnScreen, true );
81

82
    /* The core can ask through a callback to show the video. */
83
#if HAS_QT43
84 85 86
    connect( this, SIGNAL(askVideoWidgetToShow( unsigned int, unsigned int)),
             this, SLOT(SetSizing(unsigned int, unsigned int )),
             Qt::BlockingQueuedConnection );
87 88 89 90 91
#else
#error This is broken. Fix it with a QEventLoop with a processEvents () 
    connect( this, SIGNAL(askVideoWidgetToShow( unsigned int, unsigned int)),
             this, SLOT(SetSizing(unsigned int, unsigned int )) );
#endif
92 93
}

94 95 96
void VideoWidget::paintEvent(QPaintEvent *ev)
{
    QFrame::paintEvent(ev);
97 98 99
#ifdef Q_WS_X11
    XFlush( QX11Info::display() );
#endif
100 101
}

102 103
VideoWidget::~VideoWidget()
{
104 105 106
    vout_thread_t *p_vout = i_vout
        ? (vout_thread_t *)vlc_object_get( i_vout ) : NULL;

107 108 109 110 111 112 113 114 115 116 117 118
    if( p_vout )
    {
        if( !p_intf->psz_switch_intf )
        {
            if( vout_Control( p_vout, VOUT_CLOSE ) != VLC_SUCCESS )
                vout_Control( p_vout, VOUT_REPARENT );
        }
        else
        {
            if( vout_Control( p_vout, VOUT_REPARENT ) != VLC_SUCCESS )
                vout_Control( p_vout, VOUT_CLOSE );
        }
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
    msg_Dbg( p_intf, "embedded video ready (handle %p)", winId() );
138
    return ( void* )winId();
139 140
}

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

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

163 164 165 166
QSize VideoWidget::sizeHint() const
{
    return videoSize;
}
167

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

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

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

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

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

206
    CONNECT( THEMIM->getIM(), artChanged( QString ), this, updateArt( QString ) );
207 208
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
339
    recordButton = new QPushButton;
Lukas Durfina's avatar
Lukas Durfina committed
340
    setupSmallButton( recordButton );
341
    advLayout->addWidget( recordButton );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
342
    BUTTON_SET_ACT_I( recordButton, "", record,
343
            qtr( "Record" ), record() );
344

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

353
AdvControlsWidget::~AdvControlsWidget()
354
{}
Clément Stenac's avatar
Clément Stenac committed
355

356
void AdvControlsWidget::enableInput( bool enable )
Clément Stenac's avatar
Clément Stenac committed
357
{
358 359 360 361 362 363
    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;
    }
364 365
    ABButton->setEnabled( enable );
    recordButton->setEnabled( enable );
366 367 368 369 370 371 372

    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
373
}
374

375
void AdvControlsWidget::enableVideo( bool enable )
Clément Stenac's avatar
Clément Stenac committed
376 377
{
    snapshotButton->setEnabled( enable );
378
#if 0
379
    frameButton->setEnabled( enable );
380
#endif
Clément Stenac's avatar
Clément Stenac committed
381 382
}

383
void AdvControlsWidget::snapshot()
Clément Stenac's avatar
Clément Stenac committed
384
{
385 386
    vout_thread_t *p_vout =
        (vout_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_VOUT, FIND_ANYWHERE );
387
    if( p_vout ) vout_Control( p_vout, VOUT_SNAPSHOT );
Clément Stenac's avatar
Clément Stenac committed
388 389
}

390
/* Function called when the button is clicked() */
391 392 393 394 395
void AdvControlsWidget::fromAtoB()
{
    if( !timeA )
    {
        timeA = var_GetTime( THEMIM->getInput(), "time"  );
396
        emit timeChanged();
397 398 399 400 401 402
        return;
    }
    if( !timeB )
    {
        timeB = var_GetTime( THEMIM->getInput(), "time"  );
        var_SetTime( THEMIM->getInput(), "time" , timeA );
403
        emit timeChanged();
404 405 406 407
        return;
    }
    timeA = 0;
    timeB = 0;
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
    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" ) );
    }
429
}
430

431
/* Function called regularly when in an AtoB loop */
432 433 434 435
void AdvControlsWidget::AtoBLoop( float f_pos, int i_time, int i_length )
{
    if( timeB )
    {
436 437
        if( ( i_time >= (int)( timeB/1000000 ) )
            || ( i_time < (int)( timeA/1000000 ) ) )
438 439 440
            var_SetTime( THEMIM->getInput(), "time" , timeA );
    }
}
441

442
/* FIXME Record function */
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
void AdvControlsWidget::record()
{
    input_thread_t *p_input = THEMIM->getInput();
    if( p_input )
    {
        /* This method won't work fine if the stream can't be cut anywhere */
        if( var_Type( p_input, "record-toggle" ) == VLC_VAR_VOID )
            var_TriggerCallback( p_input, "record-toggle" );
        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 );
        }
    }
}
464

465
#if 0
466
//FIXME Frame by frame function
467
void AdvControlsWidget::frame(){}
468
#endif
469

470 471 472
/*****************************
 * DA Control Widget !
 *****************************/
473
ControlsWidget::ControlsWidget( intf_thread_t *_p_i,
474
                                MainInterface *_p_mi,
475
                                bool b_advControls,
Lukas Durfina's avatar
Lukas Durfina committed
476 477 478
                                bool b_shiny,
                                bool b_fsCreation) :
                                QFrame( _p_mi ), p_intf( _p_i )
479
{
480
    setSizePolicy( QSizePolicy::Preferred , QSizePolicy::Maximum );
481

482 483 484 485
    /** The main Slider **/
    slider = new InputSlider( Qt::Horizontal, NULL );
    /* Update the position when the IM has changed */
    CONNECT( THEMIM->getIM(), positionUpdated( float, int, int ),
486
             slider, setPosition( float, int, int ) );
487 488 489 490
    /* And update the IM, when the position has changed */
    CONNECT( slider, sliderDragged( float ),
             THEMIM->getIM(), sliderUpdate( float ) );

491
    /** Slower and faster Buttons **/
492 493
    slowerButton = new QToolButton;
    slowerButton->setAutoRaise( true );
494
    slowerButton->setMaximumSize( QSize( 26, 20 ) );
495

496
    BUTTON_SET_ACT( slowerButton, "-", qtr( "Slower" ), slower() );
497

498 499
    fasterButton = new QToolButton;
    fasterButton->setAutoRaise( true );
500
    fasterButton->setMaximumSize( QSize( 26, 20 ) );
501

502
    BUTTON_SET_ACT( fasterButton, "+", qtr( "Faster" ), faster() );
503

504 505 506
    /* advanced Controls handling */
    b_advancedVisible = b_advControls;

507
    advControls = new AdvControlsWidget( p_intf, b_fsCreation );
508 509
    if( !b_advancedVisible ) advControls->hide();

510
    /** Disc and Menus handling */
511
    discFrame = new QWidget( this );
512

513 514 515
    QHBoxLayout *discLayout = new QHBoxLayout( discFrame );
    discLayout->setSpacing( 0 );
    discLayout->setMargin( 0 );
516

517
    prevSectionButton = new QPushButton( discFrame );
518
    setupSmallButton( prevSectionButton );
519 520
    discLayout->addWidget( prevSectionButton );

521
    menuButton = new QPushButton( discFrame );
522 523 524
    setupSmallButton( menuButton );
    discLayout->addWidget( menuButton );

525
    nextSectionButton = new QPushButton( discFrame );
526
    setupSmallButton( nextSectionButton );
527 528
    discLayout->addWidget( nextSectionButton );

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
529 530 531
    BUTTON_SET_IMG( prevSectionButton, "", dvd_prev, "" );
    BUTTON_SET_IMG( nextSectionButton, "", dvd_next, "" );
    BUTTON_SET_IMG( menuButton, "", dvd_menu, qtr( "Menu" ) );
532

533
    discFrame->hide();
534 535 536

    /* Change the navigation button display when the IM navigation changes */
    CONNECT( THEMIM->getIM(), navigationChanged( int ),
537
             this, setNavigation( int ) );
538 539 540 541 542 543 544
    /* 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() );
545

546
    /**
547
     * Telextext QFrame
548
     * TODO: Merge with upper menu in a StackLayout
549
     **/
550 551 552 553 554
    telexFrame = new QWidget( this );
    QHBoxLayout *telexLayout = new QHBoxLayout( telexFrame );
    telexLayout->setSpacing( 0 );
    telexLayout->setMargin( 0 );

Lukas Durfina's avatar
Lukas Durfina committed
555
    telexOn = new QPushButton;
556 557 558
    setupSmallButton( telexOn );
    telexLayout->addWidget( telexOn );

559
    telexTransparent = new QPushButton;
560 561
    setupSmallButton( telexTransparent );
    telexLayout->addWidget( telexTransparent );
562
    b_telexTransparent = false;
563

564
    telexPage = new QSpinBox;
565
    telexPage->setRange( 0, 999 );
566
    telexPage->setValue( 100 );
567 568
    telexPage->setAccelerated( true );
    telexPage->setWrapping( true );
569
    telexPage->setAlignment( Qt::AlignRight );
570
    telexPage->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Minimum );
571 572
    telexLayout->addWidget( telexPage );

573
    telexFrame->hide(); /* default hidden */
574 575 576

    CONNECT( telexPage, valueChanged( int ), THEMIM->getIM(),
             telexGotoPage( int ) );
Lukas Durfina's avatar
Lukas Durfina committed
577 578 579
    CONNECT( THEMIM->getIM(), setNewTelexPage( int ),
              telexPage, setValue( int ) );

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

Lukas Durfina's avatar
Lukas Durfina committed
582 583
    CONNECT( telexOn, clicked(), THEMIM->getIM(),
             telexToggleButtons() );
584 585
    CONNECT( telexOn, clicked( bool ), THEMIM->getIM(),
             telexToggle( bool ) );
Lukas Durfina's avatar
Lukas Durfina committed
586 587 588
    CONNECT( THEMIM->getIM(), toggleTelexButtons(),
              this, toggleTeletext() );
    b_telexEnabled = false;
589 590 591
    telexTransparent->setEnabled( false );
    telexPage->setEnabled( false );

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
592
    BUTTON_SET_IMG( telexTransparent, "", tvtelx, qtr( "Teletext" ) );
593
    CONNECT( telexTransparent, clicked( bool ),
594
             THEMIM->getIM(), telexSetTransparency() );
Lukas Durfina's avatar
Lukas Durfina committed
595 596
    CONNECT( THEMIM->getIM(), toggleTelexTransparency(),
              this, toggleTeletextTransparency() );
597
    CONNECT( THEMIM->getIM(), teletextEnabled( bool ),
598
             this, enableTeletext( bool ) );
599

600
    /** Play Buttons **/
601
    QSizePolicy sizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
602 603 604 605
    sizePolicy.setHorizontalStretch( 0 );
    sizePolicy.setVerticalStretch( 0 );

    /* Play */
606
    playButton = new QPushButton;
607
    playButton->setSizePolicy( sizePolicy );
608 609
    playButton->setMaximumSize( QSize( 36, 36 ) );
    playButton->setMinimumSize( QSize( 36, 36 ) );
610
    playButton->setIconSize( QSize( 30, 30 ) );
611

612

613
    /** Prev + Stop + Next Block **/
Lukas Durfina's avatar
Lukas Durfina committed
614
    controlButLayout = new QHBoxLayout;
615 616 617 618 619
    controlButLayout->setSpacing( 0 ); /* Don't remove that, will be useful */

    /* Prev */
    QPushButton *prevButton = new QPushButton;
    prevButton->setSizePolicy( sizePolicy );
620
    setupSmallButton( prevButton );
621 622 623 624 625 626

    controlButLayout->addWidget( prevButton );

    /* Stop */
    QPushButton *stopButton = new QPushButton;
    stopButton->setSizePolicy( sizePolicy );
627
    setupSmallButton( stopButton );
628 629 630 631 632 633

    controlButLayout->addWidget( stopButton );

    /* next */
    QPushButton *nextButton = new QPushButton;
    nextButton->setSizePolicy( sizePolicy );
634
    setupSmallButton( nextButton );
635 636 637 638 639

    controlButLayout->addWidget( nextButton );

    /* Add this block to the main layout */

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
640
    BUTTON_SET_ACT_I( playButton, "", play_b, qtr( I_PLAY_TOOLTIP ), play() );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
641
    BUTTON_SET_ACT_I( prevButton, "" , previous_b,
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
642 643 644 645
                      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() );
646 647 648 649

    /*
     * Other first Line buttons
     */
650
    /** Fullscreen/Visualisation **/
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
651
    fullscreenButton = new QPushButton;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
652 653
    BUTTON_SET_ACT_I( fullscreenButton, "", fullscreen,
            qtr( "Toggle the video in fullscreen" ), fullscreen() );
654
    setupSmallButton( fullscreenButton );
655

656 657 658 659 660
    if( !b_fsCreation )
    {
        /** Playlist Button **/
        playlistButton = new QPushButton;
        setupSmallButton( playlistButton );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
661
        BUTTON_SET_IMG( playlistButton, "" , playlist, qtr( "Show playlist" ) );
662 663 664 665
        CONNECT( playlistButton, clicked(), _p_mi, togglePlaylist() );

        /** extended Settings **/
        extSettingsButton = new QPushButton;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
666
        BUTTON_SET_ACT_I( extSettingsButton, "", extended,
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
667
                qtr( "Show extended settings" ), extSettings() );
668 669
        setupSmallButton( extSettingsButton );
    }
670

671
    /* Volume */
Lukas Durfina's avatar
Lukas Durfina committed
672
    hVolLabel = new VolumeClickHandler( p_intf, this );
673

674
    volMuteLabel = new QLabel;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
675
    volMuteLabel->setPixmap( QPixmap( ":/volume-medium" ) );
676
    volMuteLabel->installEventFilter( hVolLabel );
677

678 679 680
    if( b_shiny )
    {
        volumeSlider = new SoundSlider( this,
681
            config_GetInt( p_intf, "volume-step" ),
682 683
            config_GetInt( p_intf, "qt-volume-complete" ),
            config_GetPsz( p_intf, "qt-slider-colours" ) );
684 685 686 687 688 689
    }
    else
    {
        volumeSlider = new QSlider( this );
        volumeSlider->setOrientation( Qt::Horizontal );
    }
690
    volumeSlider->setMaximumSize( QSize( 200, 40 ) );
691
    volumeSlider->setMinimumSize( QSize( 85, 30 ) );
692
    volumeSlider->setFocusPolicy( Qt::NoFocus );
693

694
    /* Set the volume from the config */
695 696
    volumeSlider->setValue( ( config_GetInt( p_intf, "volume" ) ) *
                              VOLUME_MAX / (AOUT_VOLUME_MAX/2) );
697

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

701 702
    /* Volume control connection */
    CONNECT( volumeSlider, valueChanged( int ), this, updateVolume( int ) );
703
    CONNECT( THEMIM, volumeChanged( void ), this, updateVolume( void ) );
704

705 706 707 708 709 710 711
    if( !b_fsCreation )
    {
        controlLayout = new QGridLayout( this );

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

712
        controlLayout->addWidget( slider, 0, 1, 1, 18 );
713
        controlLayout->addWidget( slowerButton, 0, 0 );
714
        controlLayout->addWidget( fasterButton, 0, 19 );
715

716 717
        controlLayout->addWidget( discFrame, 1, 8, 2, 3, Qt::AlignBottom );
        controlLayout->addWidget( telexFrame, 1, 8, 2, 5, Qt::AlignBottom );
718

719 720
        controlLayout->addWidget( playButton, 2, 0, 2, 2, Qt::AlignBottom );
        controlLayout->setColumnMinimumWidth( 2, 10 );
721 722
        controlLayout->setColumnStretch( 2, 0 );

723 724 725
        controlLayout->addLayout( controlButLayout, 3, 3, 1, 3, Qt::AlignBottom );
        /* Column 6 is unused */
        controlLayout->setColumnStretch( 6, 0 );
726
        controlLayout->setColumnStretch( 7, 0 );
727
        controlLayout->setColumnMinimumWidth( 7, 10 );
728

729 730 731 732
        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 */
733

734 735
        controlLayout->setColumnStretch( 12, 0 );
        controlLayout->setColumnMinimumWidth( 12, 10 );
736

737 738 739 740 741 742 743
        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 );
744 745
    }

746
    updateInput();
747
}
748

749
ControlsWidget::~ControlsWidget()
750
{}
751

752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
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;
    }
}
768

769 770 771 772 773 774 775 776 777 778
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;
}
779 780 781 782 783

void ControlsWidget::toggleTeletextTransparency()
{
    if( b_telexTransparent )
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
784
        telexTransparent->setIcon( QIcon( ":/tvtelx" ) );
785 786 787 788 789
        telexTransparent->setToolTip( qtr( "Teletext" ) );
        b_telexTransparent = false;
    }
    else
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
790
        telexTransparent->setIcon( QIcon( ":/tvtelx-transparent" ) );
791 792 793 794 795
        telexTransparent->setToolTip( qtr( "Transparent" ) );
        b_telexTransparent = true;
    }
}

796 797 798 799 800 801 802
void ControlsWidget::stop()
{
    THEMIM->stop();
}

void ControlsWidget::play()
{
803
    if( THEPL->current.i_size == 0 )
804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824
    {
        /* 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 )
{
825 826
#define HELP_PCH N_( "Previous chapter" )
#define HELP_NCH N_( "Next chapter" )
827 828 829 830 831 832

    // 1 = chapter, 2 = title, 0 = no
    if( navigation == 0 )
    {
        discFrame->hide();
    } else if( navigation == 1 ) {
833 834
        prevSectionButton->setToolTip( qfu( HELP_PCH ) );
        nextSectionButton->setToolTip( qfu( HELP_NCH ) );
835 836 837
        menuButton->show();
        discFrame->show();
    } else {
838 839
        prevSectionButton->setToolTip( qfu( HELP_PCH ) );
        nextSectionButton->setToolTip( qfu( HELP_NCH ) );
840 841 842 843 844 845
        menuButton->hide();
        discFrame->show();
    }
}

static bool b_my_volume;
846
void ControlsWidget::updateVolume( int i_sliderVolume )
847 848 849
{
    if( !b_my_volume )
    {
850
        int i_res = i_sliderVolume  * (AOUT_VOLUME_MAX / 2) / VOLUME_MAX;
851 852
        aout_VolumeSet( p_intf, i_res );
    }
853
    if( i_sliderVolume == 0 )
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
854
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
855
        volMuteLabel->setPixmap( QPixmap(":/volume-muted" ) );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
856 857 858 859 860
        volMuteLabel->setToolTip( qtr( "Unmute" ) );
        return;
    }

    if( i_sliderVolume < VOLUME_MAX / 3 )
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
861
        volMuteLabel->setPixmap( QPixmap( ":/volume-low" ) );
862
    else if( i_sliderVolume > (VOLUME_MAX * 2 / 3 ) )
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
863 864
        volMuteLabel->setPixmap( QPixmap( ":/volume-high" ) );
    else volMuteLabel->setPixmap( QPixmap( ":/volume-medium" ) );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
865
    volMuteLabel->setToolTip( qtr( "Mute" ) );
866 867
}

868
void ControlsWidget::updateVolume()
869
{
870
    /* Audio part */
871 872
    audio_volume_t i_volume;
    aout_VolumeGet( p_intf, &i_volume );
873
    i_volume = ( i_volume *  VOLUME_MAX )/ (AOUT_VOLUME_MAX/2);
874 875 876 877 878 879 880 881
    int i_gauge = volumeSlider->value();
    b_my_volume = false;
    if( i_volume - i_gauge > 1 || i_gauge - i_volume > 1