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