]> git.sesse.net Git - vlc/blob - modules/gui/qt4/components/interface_widgets.cpp
Qt4: wait until the video output is ready
[vlc] / modules / gui / qt4 / components / interface_widgets.cpp
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  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * ( at your option ) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include "dialogs_provider.hpp"
32 #include "components/interface_widgets.hpp"
33 #include "main_interface.hpp"
34 #include "input_manager.hpp"
35 #include "menus.hpp"
36 #include "util/input_slider.hpp"
37 #include "util/customwidgets.hpp"
38 #include <vlc_vout.h>
39
40 #include <QLabel>
41 #include <QSpacerItem>
42 #include <QCursor>
43 #include <QPushButton>
44 #include <QToolButton>
45 #include <QHBoxLayout>
46 #include <QMenu>
47 #include <QPalette>
48 #include <QResizeEvent>
49 #include <QDate>
50
51 /**********************************************************************
52  * Video Widget. A simple frame on which video is drawn
53  * This class handles resize issues
54  **********************************************************************/
55
56 VideoWidget::VideoWidget( intf_thread_t *_p_i ) : QFrame( NULL ), p_intf( _p_i )
57 {
58     /* Init */
59     vlc_mutex_init( &lock );
60     p_vout = NULL;
61     hide(); setMinimumSize( 16, 16 );
62     videoSize.rwidth() = -1;
63     videoSize.rheight() = -1;
64
65     /* Black background is more coherent for a Video Widget IMVHO */
66     QPalette plt =  palette();
67     plt.setColor( QPalette::Active, QPalette::Window , Qt::black );
68     plt.setColor( QPalette::Inactive, QPalette::Window , Qt::black );
69     setPalette( plt );
70
71     /* The core can ask through a callback to show the video.
72      * NOTE: We need to block the video output core until the window handle
73      * is ready for use (otherwise an X11 invalid handle failure may occur).
74      * As a side effect, it is illegal to emit askVideoWidgetToShow from
75      * the same thread as the Qt4 thread that owns this. */
76     QObject::connect( this, SIGNAL(askVideoWidgetToShow()), this, SLOT(show()),
77                       Qt::BlockingQueuedConnection );
78
79     /* The core can ask through a callback to resize the video */
80    // CONNECT( this, askResize( int, int ), this, SetSizing( int, int ) );
81 }
82
83 VideoWidget::~VideoWidget()
84 {
85     vlc_mutex_lock( &lock );
86     if( p_vout )
87     {
88         if( !p_intf->psz_switch_intf )
89         {
90             if( vout_Control( p_vout, VOUT_CLOSE ) != VLC_SUCCESS )
91                 vout_Control( p_vout, VOUT_REPARENT );
92         }
93         else
94         {
95             if( vout_Control( p_vout, VOUT_REPARENT ) != VLC_SUCCESS )
96                 vout_Control( p_vout, VOUT_CLOSE );
97         }
98     }
99     vlc_mutex_unlock( &lock );
100     vlc_mutex_destroy( &lock );
101 }
102
103 /**
104  * Request the video to avoid the conflicts
105  **/
106 void *VideoWidget::request( vout_thread_t *p_nvout, int *pi_x, int *pi_y,
107                            unsigned int *pi_width, unsigned int *pi_height )
108 {
109     msg_Dbg( p_intf, "Video was requested %i, %i", *pi_x, *pi_y );
110     emit askVideoWidgetToShow();
111     if( p_vout )
112     {
113         msg_Dbg( p_intf, "embedded video already in use" );
114         return NULL;
115     }
116     p_vout = p_nvout;
117     msg_Dbg( p_intf, "embedded video handle %p", winId() );
118     return ( void* )winId();
119 }
120
121 /* Set the Widget to the correct Size */
122 /* Function has to be called by the parent
123    Parent has to care about resizing himself*/
124 void VideoWidget::SetSizing( unsigned int w, unsigned int h )
125 {
126     msg_Dbg( p_intf, "Video is resizing to: %i %i", w, h );
127     videoSize.rwidth() = w;
128     videoSize.rheight() = h;
129     updateGeometry(); // Needed for deinterlace
130 }
131
132 void VideoWidget::release( void *p_win )
133 {
134     msg_Dbg( p_intf, "Video is non needed anymore" );
135     p_vout = NULL;
136     videoSize.rwidth() = 0;
137     videoSize.rheight() = 0;
138     hide();
139     updateGeometry(); // Needed for deinterlace
140 }
141
142 QSize VideoWidget::sizeHint() const
143 {
144     return videoSize;
145 }
146
147 /**********************************************************************
148  * Background Widget. Show a simple image background. Currently,
149  * it's album art if present or cone.
150  **********************************************************************/
151 #define ICON_SIZE 128
152 #define MAX_BG_SIZE 400
153 #define MIN_BG_SIZE 64
154
155 BackgroundWidget::BackgroundWidget( intf_thread_t *_p_i )
156                  :QWidget( NULL ), p_intf( _p_i )
157 {
158     /* We should use that one to take the more size it can */
159 //    setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );
160
161     /* A dark background */
162     setAutoFillBackground( true );
163     plt =  palette();
164     plt.setColor( QPalette::Active, QPalette::Window , Qt::black );
165     plt.setColor( QPalette::Inactive, QPalette::Window , Qt::black );
166     setPalette( plt );
167
168     /* A cone in the middle */
169     label = new QLabel;
170     label->setMargin( 5 );
171     label->setMaximumHeight( MAX_BG_SIZE );
172     label->setMaximumWidth( MAX_BG_SIZE );
173     label->setMinimumHeight( MIN_BG_SIZE );
174     label->setMinimumWidth( MIN_BG_SIZE );
175     if( QDate::currentDate().dayOfYear() >= 354 )
176         label->setPixmap( QPixmap( ":/vlc128-christmas.png" ) );
177     else
178         label->setPixmap( QPixmap( ":/vlc128.png" ) );
179
180     QGridLayout *backgroundLayout = new QGridLayout( this );
181     backgroundLayout->addWidget( label, 0, 1 );
182     backgroundLayout->setColumnStretch( 0, 1 );
183     backgroundLayout->setColumnStretch( 2, 1 );
184
185     CONNECT( THEMIM->getIM(), artChanged( QString ), this, updateArt( QString ) );
186 }
187
188 BackgroundWidget::~BackgroundWidget()
189 {
190 }
191
192 void BackgroundWidget::resizeEvent( QResizeEvent * event )
193 {
194     if( event->size().height() <= MIN_BG_SIZE )
195         label->hide();
196     else
197         label->show();
198 }
199
200 void BackgroundWidget::updateArt( QString url )
201 {
202     if( url.isEmpty() )
203     {
204         if( QDate::currentDate().dayOfYear() >= 354 )
205             label->setPixmap( QPixmap( ":/vlc128-christmas.png" ) );
206         else
207             label->setPixmap( QPixmap( ":/vlc128.png" ) );
208         return;
209     }
210     else
211     {
212         label->setPixmap( QPixmap( url ) );
213     }
214 }
215
216 void BackgroundWidget::contextMenuEvent( QContextMenuEvent *event )
217 {
218     QVLCMenu::PopupMenu( p_intf, true );
219 }
220
221 /**********************************************************************
222  * Visualization selector panel
223  **********************************************************************/
224 VisualSelector::VisualSelector( intf_thread_t *_p_i ) :
225                                 QFrame( NULL ), p_intf( _p_i )
226 {
227     QHBoxLayout *layout = new QHBoxLayout( this );
228     layout->setMargin( 0 );
229     QPushButton *prevButton = new QPushButton( "Prev" );
230     QPushButton *nextButton = new QPushButton( "Next" );
231     layout->addWidget( prevButton );
232     layout->addWidget( nextButton );
233
234     layout->addItem( new QSpacerItem( 40,20,
235                               QSizePolicy::Expanding, QSizePolicy::Minimum ) );
236     layout->addWidget( new QLabel( qtr( "Current visualization:" ) ) );
237
238     current = new QLabel( qtr( "None" ) );
239     layout->addWidget( current );
240
241     BUTTONACT( prevButton, prev() );
242     BUTTONACT( nextButton, next() );
243
244     setLayout( layout );
245     setMaximumHeight( 35 );
246 }
247
248 VisualSelector::~VisualSelector()
249 {
250 }
251
252 void VisualSelector::prev()
253 {
254     char *psz_new = aout_VisualPrev( p_intf );
255     if( psz_new )
256     {
257         current->setText( qfu( psz_new ) );
258         free( psz_new );
259     }
260 }
261
262 void VisualSelector::next()
263 {
264     char *psz_new = aout_VisualNext( p_intf );
265     if( psz_new )
266     {
267         current->setText( qfu( psz_new ) );
268         free( psz_new );
269     }
270 }
271
272 /**********************************************************************
273  * TEH controls
274  **********************************************************************/
275
276 #define setupSmallButton( aButton ){  \
277     aButton->setMaximumSize( QSize( 26, 26 ) ); \
278     aButton->setMinimumSize( QSize( 26, 26 ) ); \
279     aButton->setIconSize( QSize( 20, 20 ) ); }
280
281 AdvControlsWidget::AdvControlsWidget( intf_thread_t *_p_i ) :
282                                            QFrame( NULL ), p_intf( _p_i )
283 {
284     QHBoxLayout *advLayout = new QHBoxLayout( this );
285     advLayout->setMargin( 0 );
286     advLayout->setSpacing( 0 );
287     advLayout->setAlignment( Qt::AlignBottom );
288
289     /* A to B Button */
290     ABButton = new QPushButton( "AB" );
291     setupSmallButton( ABButton );
292     advLayout->addWidget( ABButton );
293     BUTTON_SET_ACT( ABButton, "AB", qtr( "A to B" ), fromAtoB() );
294     timeA = timeB = 0;
295     CONNECT( THEMIM->getIM(), positionUpdated( float, int, int ),
296              this, AtoBLoop( float, int, int ) );
297 #if 0
298     frameButton = new QPushButton( "Fr" );
299     frameButton->setMaximumSize( QSize( 26, 26 ) );
300     frameButton->setIconSize( QSize( 20, 20 ) );
301     advLayout->addWidget( frameButton );
302     BUTTON_SET_ACT( frameButton, "Fr", qtr( "Frame by Frame" ), frame() );
303 #endif
304
305     recordButton = new QPushButton( "R" );
306     setupSmallButton( recordButton );
307     advLayout->addWidget( recordButton );
308     BUTTON_SET_ACT_I( recordButton, "", record_16px.png,
309             qtr( "Record" ), record() );
310
311     /* Snapshot Button */
312     snapshotButton = new QPushButton( "S" );
313     setupSmallButton( snapshotButton );
314     advLayout->addWidget( snapshotButton );
315     BUTTON_SET_ACT( snapshotButton, "S", qtr( "Take a snapshot" ), snapshot() );
316 }
317
318 AdvControlsWidget::~AdvControlsWidget()
319 {}
320
321 void AdvControlsWidget::enableInput( bool enable )
322 {
323     ABButton->setEnabled( enable );
324     recordButton->setEnabled( enable );
325 }
326
327 void AdvControlsWidget::enableVideo( bool enable )
328 {
329     snapshotButton->setEnabled( enable );
330 #if 0
331     frameButton->setEnabled( enable );
332 #endif
333 }
334
335 void AdvControlsWidget::snapshot()
336 {
337     vout_thread_t *p_vout =
338         (vout_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_VOUT, FIND_ANYWHERE );
339     if( p_vout ) vout_Control( p_vout, VOUT_SNAPSHOT );
340 }
341
342 /* Function called when the button is clicked() */
343 void AdvControlsWidget::fromAtoB()
344 {
345     if( !timeA )
346     {
347         timeA = var_GetTime( THEMIM->getInput(), "time"  );
348         ABButton->setText( "A->..." );
349         return;
350     }
351     if( !timeB )
352     {
353         timeB = var_GetTime( THEMIM->getInput(), "time"  );
354         var_SetTime( THEMIM->getInput(), "time" , timeA );
355         ABButton->setText( "A<=>B" );
356         return;
357     }
358     timeA = 0;
359     timeB = 0;
360     ABButton->setText( "AB" );
361 }
362
363 /* Function called regularly when in an AtoB loop */
364 void AdvControlsWidget::AtoBLoop( float f_pos, int i_time, int i_length )
365 {
366     if( timeB )
367     {
368         if( i_time >= (int)(timeB/1000000) )
369             var_SetTime( THEMIM->getInput(), "time" , timeA );
370     }
371 }
372
373 /* FIXME Record function */
374 void AdvControlsWidget::record(){}
375
376 #if 0
377 //FIXME Frame by frame function
378 void AdvControlsWidget::frame(){}
379 #endif
380
381 /*****************************
382  * DA Control Widget !
383  *****************************/
384 ControlsWidget::ControlsWidget( intf_thread_t *_p_i,
385                                 MainInterface *_p_mi,
386                                 bool b_advControls,
387                                 bool b_shiny,
388                                 bool b_fsCreation) :
389                                 QFrame( _p_mi ), p_intf( _p_i )
390 {
391     controlLayout = new QGridLayout( );
392
393     controlLayout->setSpacing( 0 );
394     controlLayout->setLayoutMargins( 7, 5, 7, 3, 6 );
395
396     if( !b_fsCreation )
397         setLayout( controlLayout );
398
399     setSizePolicy( QSizePolicy::Preferred , QSizePolicy::Maximum );
400
401     /** The main Slider **/
402     slider = new InputSlider( Qt::Horizontal, NULL );
403     controlLayout->addWidget( slider, 0, 1, 1, 16 );
404     /* Update the position when the IM has changed */
405     CONNECT( THEMIM->getIM(), positionUpdated( float, int, int ),
406              slider, setPosition( float, int, int ) );
407     /* And update the IM, when the position has changed */
408     CONNECT( slider, sliderDragged( float ),
409              THEMIM->getIM(), sliderUpdate( float ) );
410
411     /** Slower and faster Buttons **/
412     slowerButton = new QToolButton;
413     slowerButton->setAutoRaise( true );
414     slowerButton->setMaximumSize( QSize( 26, 20 ) );
415
416     BUTTON_SET_ACT( slowerButton, "-", qtr( "Slower" ), slower() );
417     controlLayout->addWidget( slowerButton, 0, 0 );
418
419     fasterButton = new QToolButton;
420     fasterButton->setAutoRaise( true );
421     fasterButton->setMaximumSize( QSize( 26, 20 ) );
422
423     BUTTON_SET_ACT( fasterButton, "+", qtr( "Faster" ), faster() );
424     controlLayout->addWidget( fasterButton, 0, 17 );
425
426     /* advanced Controls handling */
427     b_advancedVisible = b_advControls;
428
429     advControls = new AdvControlsWidget( p_intf );
430     controlLayout->addWidget( advControls, 1, 3, 2, 4, Qt::AlignBottom );
431     if( !b_advancedVisible ) advControls->hide();
432
433     /** Disc and Menus handling */
434     discFrame = new QWidget( this );
435
436     QHBoxLayout *discLayout = new QHBoxLayout( discFrame );
437     discLayout->setSpacing( 0 );
438     discLayout->setMargin( 0 );
439
440     prevSectionButton = new QPushButton( discFrame );
441     setupSmallButton( prevSectionButton );
442     discLayout->addWidget( prevSectionButton );
443
444     menuButton = new QPushButton( discFrame );
445     setupSmallButton( menuButton );
446     discLayout->addWidget( menuButton );
447
448     nextSectionButton = new QPushButton( discFrame );
449     setupSmallButton( nextSectionButton );
450     discLayout->addWidget( nextSectionButton );
451
452     controlLayout->addWidget( discFrame, 1, 10, 2, 3, Qt::AlignBottom );
453
454     BUTTON_SET_IMG( prevSectionButton, "", previous.png, "" );
455     BUTTON_SET_IMG( nextSectionButton, "", next.png, "" );
456     BUTTON_SET_IMG( menuButton, "", previous.png, "" );
457
458     discFrame->hide();
459
460     /* Change the navigation button display when the IM navigation changes */
461     CONNECT( THEMIM->getIM(), navigationChanged( int ),
462              this, setNavigation( int ) );
463     /* Changes the IM navigation when triggered on the nav buttons */
464     CONNECT( prevSectionButton, clicked(), THEMIM->getIM(),
465              sectionPrev() );
466     CONNECT( nextSectionButton, clicked(), THEMIM->getIM(),
467              sectionNext() );
468     CONNECT( menuButton, clicked(), THEMIM->getIM(),
469              sectionMenu() );
470
471     /**
472      * Telextext QFrame
473      * TODO: Merge with upper menu in a StackLayout
474      **/
475     telexFrame = new QWidget( this );
476     QHBoxLayout *telexLayout = new QHBoxLayout( telexFrame );
477     telexLayout->setSpacing( 0 );
478     telexLayout->setMargin( 0 );
479
480     QPushButton  *telexOn = new QPushButton;
481     setupSmallButton( telexOn );
482     telexLayout->addWidget( telexOn );
483
484     telexTransparent = new QPushButton;
485     setupSmallButton( telexTransparent );
486     telexLayout->addWidget( telexTransparent );
487     b_telexTransparent = false;
488
489     telexPage = new QSpinBox;
490     telexPage->setRange( 0, 999 );
491     telexPage->setValue( 100 );
492     telexPage->setAccelerated( true );
493     telexPage->setWrapping( true );
494     telexPage->setAlignment( Qt::AlignRight );
495     telexPage->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Minimum );
496     telexLayout->addWidget( telexPage );
497
498     controlLayout->addWidget( telexFrame, 1, 10, 2, 3, Qt::AlignBottom );
499     telexFrame->hide(); /* default hidden */
500
501     CONNECT( telexPage, valueChanged( int ), THEMIM->getIM(),
502              telexGotoPage( int ) );
503
504     BUTTON_SET_ACT_I( telexOn, "", tv.png, qtr( "Teletext on" ),
505                       toggleTeletext() );
506     CONNECT( telexOn, clicked( bool ), THEMIM->getIM(),
507              telexToggle( bool ) );
508     telexTransparent->setEnabled( false );
509     telexPage->setEnabled( false );
510
511     BUTTON_SET_ACT_I( telexTransparent, "", tvtelx.png, qtr( "Teletext" ),
512                       toggleTeletextTransparency() );
513     CONNECT( telexTransparent, clicked( bool ),
514              THEMIM->getIM(), telexSetTransparency() );
515     CONNECT( THEMIM->getIM(), teletextEnabled( bool ),
516              telexFrame, setVisible( bool ) );
517
518     /** Play Buttons **/
519     QSizePolicy sizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
520     sizePolicy.setHorizontalStretch( 0 );
521     sizePolicy.setVerticalStretch( 0 );
522
523     /* Play */
524     playButton = new QPushButton;
525     playButton->setSizePolicy( sizePolicy );
526     playButton->setMaximumSize( QSize( 36, 36 ) );
527     playButton->setMinimumSize( QSize( 36, 36 ) );
528     playButton->setIconSize( QSize( 30, 30 ) );
529
530     controlLayout->addWidget( playButton, 2, 0, 2, 2 );
531
532     controlLayout->setColumnMinimumWidth( 2, 20 );
533     controlLayout->setColumnStretch( 2, 0 );
534
535     /** Prev + Stop + Next Block **/
536     controlButLayout = new QHBoxLayout;
537     controlButLayout->setSpacing( 0 ); /* Don't remove that, will be useful */
538
539     /* Prev */
540     QPushButton *prevButton = new QPushButton;
541     prevButton->setSizePolicy( sizePolicy );
542     setupSmallButton( prevButton );
543
544     controlButLayout->addWidget( prevButton );
545
546     /* Stop */
547     QPushButton *stopButton = new QPushButton;
548     stopButton->setSizePolicy( sizePolicy );
549     setupSmallButton( stopButton );
550
551     controlButLayout->addWidget( stopButton );
552
553     /* next */
554     QPushButton *nextButton = new QPushButton;
555     nextButton->setSizePolicy( sizePolicy );
556     setupSmallButton( nextButton );
557
558     controlButLayout->addWidget( nextButton );
559
560     /* Add this block to the main layout */
561     if( !b_fsCreation )
562         controlLayout->addLayout( controlButLayout, 3, 3, 1, 3 );
563
564     BUTTON_SET_ACT_I( playButton, "", play.png, qtr( "Play" ), play() );
565     BUTTON_SET_ACT_I( prevButton, "" , previous.png,
566                       qtr( "Previous" ), prev() );
567     BUTTON_SET_ACT_I( nextButton, "", next.png, qtr( "Next" ), next() );
568     BUTTON_SET_ACT_I( stopButton, "", stop.png, qtr( "Stop" ), stop() );
569
570     controlLayout->setColumnMinimumWidth( 7, 20 );
571     controlLayout->setColumnStretch( 7, 0 );
572     controlLayout->setColumnStretch( 8, 0 );
573     controlLayout->setColumnStretch( 9, 0 );
574
575     /*
576      * Other first Line buttons
577      */
578     /** Fullscreen/Visualisation **/
579     fullscreenButton = new QPushButton( "F" );
580     BUTTON_SET_ACT( fullscreenButton, "F", qtr( "Fullscreen" ), fullscreen() );
581     setupSmallButton( fullscreenButton );
582     controlLayout->addWidget( fullscreenButton, 3, 10, Qt::AlignBottom );
583
584     /** Playlist Button **/
585     playlistButton = new QPushButton;
586     setupSmallButton( playlistButton );
587     controlLayout->addWidget( playlistButton, 3, 11, Qt::AlignBottom );
588     BUTTON_SET_IMG( playlistButton, "" , playlist.png, qtr( "Show playlist" ) );
589     CONNECT( playlistButton, clicked(), _p_mi, togglePlaylist() );
590
591     /** extended Settings **/
592     extSettingsButton = new QPushButton;
593     BUTTON_SET_ACT( extSettingsButton, "Ex", qtr( "Extended Settings" ),
594             extSettings() );
595     setupSmallButton( extSettingsButton );
596     controlLayout->addWidget( extSettingsButton, 3, 12, Qt::AlignBottom );
597
598     controlLayout->setColumnStretch( 13, 0 );
599     controlLayout->setColumnMinimumWidth( 13, 24 );
600     controlLayout->setColumnStretch( 14, 5 );
601
602     /* Volume */
603     hVolLabel = new VolumeClickHandler( p_intf, this );
604
605     volMuteLabel = new QLabel;
606     volMuteLabel->setPixmap( QPixmap( ":/pixmaps/volume-medium.png" ) );
607     volMuteLabel->setToolTip( qtr( "Mute" ) );
608     volMuteLabel->installEventFilter( hVolLabel );
609     controlLayout->addWidget( volMuteLabel, 3, 15, Qt::AlignBottom );
610
611     if( b_shiny )
612     {
613         volumeSlider = new SoundSlider( this,
614             config_GetInt( p_intf, "volume-step" ),
615             config_GetInt( p_intf, "qt-volume-complete" ),
616             config_GetPsz( p_intf, "qt-slider-colours" ) );
617     }
618     else
619     {
620         volumeSlider = new QSlider( this );
621         volumeSlider->setOrientation( Qt::Horizontal );
622     }
623     volumeSlider->setMaximumSize( QSize( 200, 40 ) );
624     volumeSlider->setMinimumSize( QSize( 106, 30 ) );
625     volumeSlider->setFocusPolicy( Qt::NoFocus );
626     controlLayout->addWidget( volumeSlider, 2, 16, 2 , 2, Qt::AlignBottom );
627
628     /* Set the volume from the config */
629     volumeSlider->setValue( ( config_GetInt( p_intf, "volume" ) ) *
630                               VOLUME_MAX / (AOUT_VOLUME_MAX/2) );
631
632     /* Force the update at build time in order to have a muted icon if needed */
633     updateVolume( volumeSlider->value() );
634
635     /* Volume control connection */
636     CONNECT( volumeSlider, valueChanged( int ), this, updateVolume( int ) );
637     CONNECT( THEMIM, volumeChanged( void ), this, updateVolume( void ) );
638
639     updateInput();
640 }
641
642 ControlsWidget::~ControlsWidget()
643 {}
644
645 void ControlsWidget::toggleTeletext()
646 {
647     bool b_enabled = THEMIM->teletextState();
648     if( b_telexEnabled )
649     {
650         telexTransparent->setEnabled( false );
651         telexPage->setEnabled( false );
652         b_telexEnabled = false;
653     }
654     else if( b_enabled )
655     {
656         telexTransparent->setEnabled( true );
657         telexPage->setEnabled( true );
658         b_telexEnabled = true;
659     }
660 }
661
662 void ControlsWidget::toggleTeletextTransparency()
663 {
664     if( b_telexTransparent )
665     {
666         telexTransparent->setIcon( QIcon( ":/pixmaps/tvtelx.png" ) );
667         telexTransparent->setToolTip( qtr( "Teletext" ) );
668         b_telexTransparent = false;
669     }
670     else
671     {
672         telexTransparent->setIcon( QIcon( ":/pixmaps/tvtelx-transparent.png" ) );
673         telexTransparent->setToolTip( qtr( "Transparent" ) );
674         b_telexTransparent = true;
675     }
676 }
677
678 void ControlsWidget::stop()
679 {
680     THEMIM->stop();
681 }
682
683 void ControlsWidget::play()
684 {
685     if( THEPL->current.i_size == 0 )
686     {
687         /* The playlist is empty, open a file requester */
688         THEDP->openFileDialog();
689         setStatus( 0 );
690         return;
691     }
692     THEMIM->togglePlayPause();
693 }
694
695 void ControlsWidget::prev()
696 {
697     THEMIM->prev();
698 }
699
700 void ControlsWidget::next()
701 {
702     THEMIM->next();
703 }
704
705 void ControlsWidget::setNavigation( int navigation )
706 {
707 #define HELP_MENU N_( "Menu" )
708 #define HELP_PCH N_( "Previous chapter" )
709 #define HELP_NCH N_( "Next chapter" )
710 #define HELP_PTR N_( "Previous track" )
711 #define HELP_NTR N_( "Next track" )
712
713     // 1 = chapter, 2 = title, 0 = no
714     if( navigation == 0 )
715     {
716         discFrame->hide();
717     } else if( navigation == 1 ) {
718         prevSectionButton->setToolTip( qfu( HELP_PCH ) );
719         nextSectionButton->setToolTip( qfu( HELP_NCH ) );
720         menuButton->show();
721         discFrame->show();
722     } else {
723         prevSectionButton->setToolTip( qfu( HELP_PCH ) );
724         nextSectionButton->setToolTip( qfu( HELP_NCH ) );
725         menuButton->hide();
726         discFrame->show();
727     }
728 }
729
730 static bool b_my_volume;
731 void ControlsWidget::updateVolume( int i_sliderVolume )
732 {
733     if( !b_my_volume )
734     {
735         int i_res = i_sliderVolume  * (AOUT_VOLUME_MAX / 2) / VOLUME_MAX;
736         aout_VolumeSet( p_intf, i_res );
737     }
738     if( i_sliderVolume == 0 )
739         volMuteLabel->setPixmap( QPixmap(":/pixmaps/volume-muted.png" ) );
740     else if( i_sliderVolume < VOLUME_MAX / 3 )
741         volMuteLabel->setPixmap( QPixmap( ":/pixmaps/volume-low.png" ) );
742     else if( i_sliderVolume > (VOLUME_MAX * 2 / 3 ) )
743         volMuteLabel->setPixmap( QPixmap( ":/pixmaps/volume-high.png" ) );
744     else volMuteLabel->setPixmap( QPixmap( ":/pixmaps/volume-medium.png" ) );
745 }
746
747 void ControlsWidget::updateVolume()
748 {
749     /* Audio part */
750     audio_volume_t i_volume;
751     aout_VolumeGet( p_intf, &i_volume );
752     i_volume = ( i_volume *  VOLUME_MAX )/ (AOUT_VOLUME_MAX/2);
753     int i_gauge = volumeSlider->value();
754     b_my_volume = false;
755     if( i_volume - i_gauge > 1 || i_gauge - i_volume > 1 )
756     {
757         b_my_volume = true;
758         volumeSlider->setValue( i_volume );
759         b_my_volume = false;
760     }
761 }
762
763 void ControlsWidget::updateInput()
764 {
765     /* Activate the interface buttons according to the presence of the input */
766     enableInput( THEMIM->getIM()->hasInput() );
767     enableVideo( THEMIM->getIM()->hasVideo() && THEMIM->getIM()->hasInput() );
768 }
769
770 void ControlsWidget::setStatus( int status )
771 {
772     if( status == PLAYING_S ) /* Playing */
773     {
774         playButton->setIcon( QIcon( ":/pixmaps/pause.png" ) );
775         playButton->setToolTip( qtr( "Pause" ) );
776     }
777     else
778     {
779         playButton->setIcon( QIcon( ":/pixmaps/play.png" ) );
780         playButton->setToolTip( qtr( "Play" ) );
781     }
782 }
783
784 /**
785  * TODO
786  * This functions toggle the fullscreen mode
787  * If there is no video, it should first activate Visualisations...
788  *  This has also to be fixed in enableVideo()
789  */
790 void ControlsWidget::fullscreen()
791 {
792     vout_thread_t *p_vout =
793         (vout_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_VOUT, FIND_ANYWHERE );
794     if( p_vout)
795     {
796         var_SetBool( p_vout, "fullscreen", !var_GetBool( p_vout, "fullscreen" ) );
797         vlc_object_release( p_vout );
798     }
799 }
800
801 void ControlsWidget::extSettings()
802 {
803     THEDP->extendedDialog();
804 }
805
806 void ControlsWidget::slower()
807 {
808     THEMIM->getIM()->slower();
809 }
810
811 void ControlsWidget::faster()
812 {
813     THEMIM->getIM()->faster();
814 }
815
816 void ControlsWidget::enableInput( bool enable )
817 {
818     slowerButton->setEnabled( enable );
819     slider->setEnabled( enable );
820     fasterButton->setEnabled( enable );
821
822     /* Advanced Buttons too */
823     advControls->enableInput( enable );
824 }
825
826 void ControlsWidget::enableVideo( bool enable )
827 {
828     // TODO Later make the fullscreenButton toggle Visualisation and so on.
829     fullscreenButton->setEnabled( enable );
830
831     /* Advanced Buttons too */
832     advControls->enableVideo( enable );
833 }
834
835 void ControlsWidget::toggleAdvanced()
836 {
837     if( !VISIBLE( advControls ) )
838     {
839         advControls->show();
840         b_advancedVisible = true;
841     }
842     else
843     {
844         advControls->hide();
845         b_advancedVisible = false;
846     }
847     emit advancedControlsToggled( b_advancedVisible );
848 }
849
850
851 /**********************************************************************
852  * Fullscrenn control widget
853  **********************************************************************/
854 FullscreenControllerWidget::FullscreenControllerWidget( intf_thread_t *_p_i,
855         MainInterface *_p_mi, bool b_advControls, bool b_shiny )
856         : ControlsWidget( _p_i, _p_mi, b_advControls, b_shiny, true ),
857         i_lastPosX( -1 ), i_lastPosY( -1 ), i_hideTimeout( 1 ),
858         b_mouseIsOver( false )
859 {
860     setWindowFlags( Qt::ToolTip );
861
862     setFrameShape( QFrame::StyledPanel );
863     setFrameStyle( QFrame::Sunken );
864     setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
865
866     QGridLayout *fsLayout = new QGridLayout( this );
867     controlLayout->setSpacing( 0 );
868     controlLayout->setLayoutMargins( 5, 1, 5, 1, 5 );
869
870     fsLayout->addWidget( slowerButton, 0, 0 );
871     slider->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum);
872     fsLayout->addWidget( slider, 0, 1, 1, 6 );
873     fsLayout->addWidget( fasterButton, 0, 7 );
874
875     fsLayout->addWidget( volMuteLabel, 1, 0);
876     fsLayout->addWidget( volumeSlider, 1, 1 );
877
878     fsLayout->addLayout( controlButLayout, 1, 2 );
879
880     fsLayout->addWidget( playButton, 1, 3 );
881
882     fsLayout->addWidget( discFrame, 1, 4 );
883
884     fsLayout->addWidget( telexFrame, 1, 5 );
885
886     fsLayout->addWidget( advControls, 1, 6, Qt::AlignVCenter );
887
888     fsLayout->addWidget( fullscreenButton, 1, 7 );
889
890     /* hiding timer */
891     p_hideTimer = new QTimer( this );
892     CONNECT( p_hideTimer, timeout(), this, hideFSControllerWidget() );
893     p_hideTimer->setSingleShot( true );
894
895     /* slow hiding timer */
896 #if HAVE_TRANSPARENCY
897     p_slowHideTimer = new QTimer( this );
898     CONNECT( p_slowHideTimer, timeout(), this, slowHideFSC() );
899 #endif
900
901     adjustSize ();  /* need to get real width and height for moving */
902
903     /* center down */
904     QDesktopWidget * p_desktop = QApplication::desktop();
905
906     move( p_desktop->width() / 2 - width() / 2,
907           p_desktop->height() - height() );
908
909     #ifdef WIN32TRICK
910     setWindowOpacity( 0.0 );
911     fscHidden = true;
912     show();
913     #endif
914 }
915
916 FullscreenControllerWidget::~FullscreenControllerWidget()
917 {
918 }
919
920 /**
921  * Hide fullscreen controller
922  * FIXME: under windows it have to be done by moving out of screen
923  *        because hide() doesnt work
924  */
925 void FullscreenControllerWidget::hideFSControllerWidget()
926 {
927     #ifdef WIN32TRICK
928     fscHidden = true;
929     setWindowOpacity( 0.0 );    // simulate hidding
930     #else
931     hide();
932     #endif
933 }
934
935 /**
936  * Hidding fullscreen controller slowly
937  * Linux: need composite manager
938  * Windows: it is blinking, so it can be enabled by define TRASPARENCY
939  */
940 void FullscreenControllerWidget::slowHideFSC()
941 {
942 #if HAVE_TRANSPARENCY
943     static bool first_call = true;
944
945     if ( first_call )
946     {
947         first_call = false;
948
949         p_slowHideTimer->stop();
950         /* the last part of time divided to 100 pieces */
951         p_slowHideTimer->start(
952             (int) ( i_hideTimeout / 2 / ( windowOpacity() * 100 ) ) );
953     }
954     else
955     {
956          if ( windowOpacity() > 0.0 )
957          {
958              /* we should use 0.01 because of 100 pieces ^^^
959                 but than it cannt be done in time */
960              setWindowOpacity( windowOpacity() - 0.02 );
961          }
962
963          if ( windowOpacity() == 0.0 )
964          {
965              first_call = true;
966              p_slowHideTimer->stop();
967          }
968     }
969 #endif
970 }
971
972 /**
973  * Get state of visibility of FS controller on screen
974  * On windows control if it is on hidden position
975  */
976 bool FullscreenControllerWidget::isFSCHidden()
977 {
978     #ifdef WIN32TRICK
979     return fscHidden;
980     #endif
981
982     return isHidden();
983 }
984
985 /**
986  * event handling
987  * events: show, hide, start timer for hidding
988  */
989 void FullscreenControllerWidget::customEvent( QEvent *event )
990 {
991     int type = event->type();
992
993     if ( type == FullscreenControlShow_Type )
994     {
995         #ifdef WIN32TRICK
996         // after quiting and going to fs, we need to call show()
997         if ( isHidden() )
998             show();
999
1000         if ( fscHidden )
1001         {
1002             fscHidden = false;
1003             setWindowOpacity( 1.0 );
1004         }
1005         #else
1006         show();
1007         #endif
1008
1009 #if HAVE_TRANSPARENCY
1010         setWindowOpacity( DEFAULT_OPACITY );
1011 #endif
1012     }
1013     else if ( type == FullscreenControlHide_Type )
1014     {
1015         hideFSControllerWidget();
1016     }
1017     else if ( type == FullscreenControlPlanHide_Type && !b_mouseIsOver )
1018     {
1019         p_hideTimer->start( i_hideTimeout );
1020 #if HAVE_TRANSPARENCY
1021         p_slowHideTimer->start( i_hideTimeout / 2 );
1022 #endif
1023     }
1024 }
1025
1026 /**
1027  * On mouse move
1028  * moving with FSC
1029  */
1030 void FullscreenControllerWidget::mouseMoveEvent( QMouseEvent *event )
1031 {
1032     if ( event->buttons() == Qt::LeftButton )
1033     {
1034         int i_moveX = event->globalX() - i_lastPosX;
1035         int i_moveY = event->globalY() - i_lastPosY;
1036
1037         move( x() + i_moveX, y() + i_moveY );
1038
1039         i_lastPosX = event->globalX();
1040         i_lastPosY = event->globalY();
1041     }
1042 }
1043
1044 /**
1045  * On mouse press
1046  * store position of cursor
1047  */
1048 void FullscreenControllerWidget::mousePressEvent( QMouseEvent *event )
1049 {
1050     i_lastPosX = event->globalX();
1051     i_lastPosY = event->globalY();
1052 }
1053
1054 /**
1055  * On mouse go above FSC
1056  */
1057 void FullscreenControllerWidget::enterEvent( QEvent *event )
1058 {
1059     p_hideTimer->stop();
1060 #if HAVE_TRANSPARENCY
1061     p_slowHideTimer->stop();
1062 #endif
1063     b_mouseIsOver = true;
1064 }
1065
1066 /**
1067  * On mouse go out from FSC
1068  */
1069 void FullscreenControllerWidget::leaveEvent( QEvent *event )
1070 {
1071     p_hideTimer->start( i_hideTimeout );
1072 #if HAVE_TRANSPARENCY
1073     p_slowHideTimer->start( i_hideTimeout / 2 );
1074 #endif
1075     b_mouseIsOver = false;
1076 }
1077
1078 /**
1079  * When you get pressed key, send it to video output
1080  * FIXME: clearing focus by clearFocus() to not getting
1081  * key press events didnt work
1082  */
1083 void FullscreenControllerWidget::keyPressEvent( QKeyEvent *event )
1084 {
1085     int i_vlck = qtEventToVLCKey( event );
1086     if( i_vlck > 0 )
1087     {
1088         var_SetInteger( p_intf->p_libvlc, "key-pressed", i_vlck );
1089         event->accept();
1090     }
1091     else
1092         event->ignore();
1093 }
1094
1095 /**
1096  * It is called when video start
1097  */
1098 void FullscreenControllerWidget::regFullscreenCallback( vout_thread_t *p_vout )
1099 {
1100     if ( p_vout )
1101     {
1102         var_AddCallback( p_vout, "fullscreen", regMouseMoveCallback, this );
1103     }
1104 }
1105
1106 /**
1107  * It is called after turn off video, because p_vout is NULL now
1108  * we cannt delete callback, just hide if FScontroller is visible
1109  */
1110 void FullscreenControllerWidget::unregFullscreenCallback()
1111 {
1112     if ( isVisible() )
1113         hide();
1114 }
1115
1116 /**
1117  * Register and unregister callback for mouse moving
1118  */
1119 static int regMouseMoveCallback( vlc_object_t *vlc_object, const char *variable,
1120                                  vlc_value_t old_val, vlc_value_t new_val,
1121                                  void *data )
1122 {
1123     vout_thread_t *p_vout = (vout_thread_t *) vlc_object;
1124
1125     static bool b_registered = false;
1126     FullscreenControllerWidget *p_fs = (FullscreenControllerWidget *) data;
1127
1128     if ( var_GetBool( p_vout, "fullscreen" ) && !b_registered )
1129     {
1130         p_fs->SetHideTimeout( var_GetInteger( p_vout, "mouse-hide-timeout" ) );
1131         var_AddCallback( p_vout, "mouse-moved",
1132                         showFullscreenControllCallback, (void *) p_fs );
1133         b_registered = true;
1134     }
1135
1136     if ( !var_GetBool( p_vout, "fullscreen" ) && b_registered )
1137     {
1138         var_DelCallback( p_vout, "mouse-moved",
1139                         showFullscreenControllCallback, (void *) p_fs );
1140         b_registered = false;
1141         p_fs->hide();
1142     }
1143
1144     return VLC_SUCCESS;
1145 }
1146
1147 /**
1148  * Show fullscreen controller after mouse move
1149  * after show immediately plan hide event
1150  */
1151 static int showFullscreenControllCallback( vlc_object_t *vlc_object, const char *variable,
1152                                            vlc_value_t old_val, vlc_value_t new_val,
1153                                            void *data )
1154 {
1155     FullscreenControllerWidget *p_fs = (FullscreenControllerWidget *) data;
1156
1157     if ( p_fs->isFSCHidden() || p_fs->windowOpacity() < DEFAULT_OPACITY )
1158     {
1159         IMEvent *event = new IMEvent( FullscreenControlShow_Type, 0 );
1160         QApplication::postEvent( p_fs, static_cast<QEvent *>(event) );
1161     }
1162
1163     IMEvent *e = new IMEvent( FullscreenControlPlanHide_Type, 0 );
1164     QApplication::postEvent( p_fs, static_cast<QEvent *>(e) );
1165
1166     return VLC_SUCCESS;
1167 }
1168
1169 /**********************************************************************
1170  * Speed control widget
1171  **********************************************************************/
1172 SpeedControlWidget::SpeedControlWidget( intf_thread_t *_p_i ) :
1173                              QFrame( NULL ), p_intf( _p_i )
1174 {
1175     QSizePolicy sizePolicy( QSizePolicy::Maximum, QSizePolicy::Fixed );
1176     sizePolicy.setHorizontalStretch( 0 );
1177     sizePolicy.setVerticalStretch( 0 );
1178
1179     speedSlider = new QSlider;
1180     speedSlider->setSizePolicy( sizePolicy );
1181     speedSlider->setMaximumSize( QSize( 80, 200 ) );
1182     speedSlider->setOrientation( Qt::Vertical );
1183     speedSlider->setTickPosition( QSlider::TicksRight );
1184
1185     speedSlider->setRange( -100, 100 );
1186     speedSlider->setSingleStep( 10 );
1187     speedSlider->setPageStep( 20 );
1188     speedSlider->setTickInterval( 20 );
1189
1190     CONNECT( speedSlider, valueChanged( int ), this, updateRate( int ) );
1191
1192     QToolButton *normalSpeedButton = new QToolButton( this );
1193     normalSpeedButton->setMaximumSize( QSize( 26, 20 ) );
1194     normalSpeedButton->setAutoRaise( true );
1195     normalSpeedButton->setText( "N" );
1196     normalSpeedButton->setToolTip( qtr( "Revert to normal play speed" ) );
1197
1198     CONNECT( normalSpeedButton, clicked(), this, resetRate() );
1199
1200     QVBoxLayout *speedControlLayout = new QVBoxLayout;
1201     speedControlLayout->addWidget( speedSlider );
1202     speedControlLayout->addWidget( normalSpeedButton );
1203     setLayout( speedControlLayout );
1204 }
1205
1206 SpeedControlWidget::~SpeedControlWidget()
1207 {}
1208
1209 void SpeedControlWidget::setEnable( bool b_enable )
1210 {
1211     speedSlider->setEnabled( b_enable );
1212 }
1213
1214 #define RATE_SLIDER_MAXIMUM 3.0
1215 #define RATE_SLIDER_MINIMUM 0.3
1216 #define RATE_SLIDER_LENGTH 100.0
1217
1218 void SpeedControlWidget::updateControls( int rate )
1219 {
1220     if( speedSlider->isSliderDown() )
1221     {
1222         //We don't want to change anything if the user is using the slider
1223         return;
1224     }
1225
1226     int sliderValue;
1227     double speed = INPUT_RATE_DEFAULT / (double)rate;
1228
1229     if( rate >= INPUT_RATE_DEFAULT )
1230     {
1231         if( speed < RATE_SLIDER_MINIMUM )
1232         {
1233             sliderValue = speedSlider->minimum();
1234         }
1235         else
1236         {
1237             sliderValue = (int)( ( speed - 1.0 ) * RATE_SLIDER_LENGTH
1238                                         / ( 1.0 - RATE_SLIDER_MAXIMUM ) );
1239         }
1240     }
1241     else
1242     {
1243         if( speed > RATE_SLIDER_MAXIMUM )
1244         {
1245             sliderValue = speedSlider->maximum();
1246         }
1247         else
1248         {
1249             sliderValue = (int)( ( speed - 1.0 ) * RATE_SLIDER_LENGTH
1250                                         / ( RATE_SLIDER_MAXIMUM - 1.0 ) );
1251         }
1252     }
1253
1254     //Block signals to avoid feedback loop
1255     speedSlider->blockSignals( true );
1256     speedSlider->setValue( sliderValue );
1257     speedSlider->blockSignals( false );
1258 }
1259
1260 void SpeedControlWidget::updateRate( int sliderValue )
1261 {
1262     int rate;
1263
1264     if( sliderValue < 0.0 )
1265     {
1266         rate = (int)(INPUT_RATE_DEFAULT* RATE_SLIDER_LENGTH /
1267             ( sliderValue * ( 1.0 - RATE_SLIDER_MINIMUM ) + RATE_SLIDER_LENGTH ));
1268     }
1269     else
1270     {
1271         rate = (int)(INPUT_RATE_DEFAULT* RATE_SLIDER_LENGTH /
1272             ( sliderValue * ( RATE_SLIDER_MAXIMUM - 1.0 ) + RATE_SLIDER_LENGTH ));
1273     }
1274
1275     THEMIM->getIM()->setRate(rate);
1276 }
1277
1278 void SpeedControlWidget::resetRate()
1279 {
1280     THEMIM->getIM()->setRate(INPUT_RATE_DEFAULT);
1281 }