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