]> git.sesse.net Git - vlc/blob - modules/gui/qt4/components/controller.cpp
[Qt] Rewrite of the controller and ToolBar edition.
[vlc] / modules / gui / qt4 / components / controller.cpp
1 /*****************************************************************************
2  * Controller.cpp : Controller for the main interface
3  ****************************************************************************
4  * Copyright ( C ) 2006-2008 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 <vlc_vout.h>
32
33 #include "dialogs_provider.hpp"
34 #include "components/interface_widgets.hpp"
35 #include "main_interface.hpp"
36 #include "input_manager.hpp"
37 #include "menus.hpp"
38 #include "util/input_slider.hpp"
39 #include "util/customwidgets.hpp"
40
41 #include <QLabel>
42 #include <QSpacerItem>
43 #include <QCursor>
44 #include <QToolButton>
45 #include <QHBoxLayout>
46 #include <QMenu>
47 #include <QPalette>
48 #include <QResizeEvent>
49 #include <QDate>
50 #include <QSignalMapper>
51 #include <QTimer>
52
53 /**********************************************************************
54  * TEH controls
55  **********************************************************************/
56
57 /******
58  * This is an abstract Toolbar/Controller
59  * This has helper to create any toolbar, any buttons and to manage the actions
60  *
61  *****/
62 AbstractController::AbstractController( intf_thread_t * _p_i ) : QFrame( NULL )
63 {
64     p_intf = _p_i;
65     advControls = NULL;
66
67     /* Main action provider */
68     toolbarActionsMapper = new QSignalMapper( this );
69     CONNECT( toolbarActionsMapper, mapped( int ), this, doAction( int ) );
70     CONNECT( THEMIM->getIM(), statusChanged( int ), this, setStatus( int ) );
71 }
72
73 /* Reemit some signals on status Change to activate some buttons */
74 void AbstractController::setStatus( int status )
75 {
76     bool b_hasInput = THEMIM->getIM()->hasInput();
77     /* Activate the interface buttons according to the presence of the input */
78     emit inputExists( b_hasInput );
79
80     emit inputPlaying( status == PLAYING_S );
81
82     emit inputIsRecordable( b_hasInput &&
83                             var_GetBool( THEMIM->getInput(), "can-record" ) );
84 }
85
86 /* Generic button setup */
87 void AbstractController::setupButton( QAbstractButton *aButton )
88 {
89     static QSizePolicy sizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
90     sizePolicy.setHorizontalStretch( 0 );
91     sizePolicy.setVerticalStretch( 0 );
92
93     aButton->setSizePolicy( sizePolicy );
94     aButton->setFixedSize( QSize( 26, 26 ) );
95     aButton->setIconSize( QSize( 20, 20 ) );
96     aButton->setFocusPolicy( Qt::NoFocus );
97 }
98
99 /* Open the generic config line for the toolbar, parse it
100  * and create the widgets accordingly */
101 void AbstractController::parseAndCreate( QString config,
102                                          QBoxLayout *controlLayout )
103 {
104     QStringList list = config.split( ";" ) ;
105     for( int i = 0; i < list.size(); i++ )
106     {
107         QStringList list2 = list.at( i ).split( "-" );
108         if( list2.size() < 1 )
109         {
110             msg_Warn( p_intf, "Parsing error. Report this" );
111             continue;
112         }
113
114         bool ok;
115         int i_option = WIDGET_NORMAL;
116         buttonType_e i_type = (buttonType_e)list2.at( 0 ).toInt( &ok );
117         if( !ok )
118         {
119             msg_Warn( p_intf, "Parsing error 0. Please report this" );
120             continue;
121         }
122
123         if( list2.size() > 1 )
124         {
125             i_option = list2.at( 1 ).toInt( &ok );
126             if( !ok )
127             {
128                 msg_Warn( p_intf, "Parsing error 1. Please report this" );
129                 continue;
130             }
131         }
132
133         createAndAddWidget( controlLayout, -1, i_type, i_option );
134     }
135 }
136
137 void AbstractController::createAndAddWidget( QBoxLayout *controlLayout,
138                                              int i_index,
139                                              buttonType_e i_type,
140                                              int i_option )
141 {
142     /* Special case for SPACERS, who aren't QWidgets */
143     if( i_type == WIDGET_SPACER )
144     {
145         controlLayout->insertSpacing( i_index, 16 );
146         return;
147     }
148
149     if(  i_type == WIDGET_SPACER_EXTEND )
150     {
151         controlLayout->insertSpacing( i_index, 10 );
152         return;
153     }
154
155     QWidget *widg = createWidget( i_type, i_option );
156     if( !widg ) return;
157
158     controlLayout->insertWidget( i_index, widg );
159 }
160
161
162 #define CONNECT_MAP( a ) CONNECT( a, clicked(),  toolbarActionsMapper, map() )
163 #define SET_MAPPING( a, b ) toolbarActionsMapper->setMapping( a , b )
164 #define CONNECT_MAP_SET( a, b ) \
165     CONNECT_MAP( a ); \
166     SET_MAPPING( a, b );
167 #define BUTTON_SET_BAR( a_button ) \
168     a_button->setToolTip( tooltipL[button] );          \
169     a_button->setIcon( QIcon( iconL[button] ) );
170 #define BUTTON_SET_BAR2( button, image, tooltip ) \
171     button->setToolTip( tooltip );          \
172     button->setIcon( QIcon( ":/"#image ) );
173
174
175 #define ENABLE_ON_VIDEO( a ) \
176     CONNECT( THEMIM->getIM(), voutChanged( bool ), a, setEnabled( bool ) ); \
177     a->setEnabled( THEMIM->getIM()->hasVideo() ); /* TODO: is this necessary? when input is started before the interface? */
178
179 #define ENABLE_ON_INPUT( a ) \
180     CONNECT( this, inputExists( bool ), a, setEnabled( bool ) ); \
181     a->setEnabled( THEMIM->getIM()->hasInput() ); /* TODO: is this necessary? when input is started before the interface? */
182
183 QWidget *AbstractController::createWidget( buttonType_e button, int options )
184 {
185
186     bool b_flat = options & WIDGET_FLAT;
187     bool b_big = options & WIDGET_BIG;
188     bool b_shiny = options & WIDGET_SHINY;
189
190     QWidget *widget = NULL;
191     switch( button )
192     {
193     case PLAY_BUTTON: {
194         PlayButton *playButton = new PlayButton;
195         setupButton( playButton );
196         BUTTON_SET_BAR(  playButton );
197         CONNECT_MAP_SET( playButton, PLAY_ACTION );
198         CONNECT( this, inputPlaying( bool ),
199                  playButton, updateButton( bool ));
200         widget = playButton;
201         }
202         break;
203     case STOP_BUTTON:{
204         QToolButton *stopButton = new QToolButton;
205         setupButton( stopButton );
206         CONNECT_MAP_SET( stopButton, STOP_ACTION );
207         BUTTON_SET_BAR(  stopButton );
208         widget = stopButton;
209         }
210         break;
211     case PREVIOUS_BUTTON:{
212         QToolButton *prevButton = new QToolButton;
213         setupButton( prevButton );
214         CONNECT_MAP_SET( prevButton, PREVIOUS_ACTION );
215         BUTTON_SET_BAR( prevButton );
216         widget = prevButton;
217         }
218         break;
219     case NEXT_BUTTON:
220         {
221         QToolButton *nextButton = new QToolButton;
222         setupButton( nextButton );
223         CONNECT_MAP_SET( nextButton, NEXT_ACTION );
224         BUTTON_SET_BAR( nextButton );
225         widget = nextButton;
226         }
227         break;
228     case SLOWER_BUTTON:{
229         QToolButton *slowerButton = new QToolButton;
230         setupButton( slowerButton );
231         CONNECT_MAP_SET( slowerButton, SLOWER_ACTION );
232         BUTTON_SET_BAR(  slowerButton );
233         ENABLE_ON_INPUT( slowerButton );
234         widget = slowerButton;
235         }
236         break;
237     case FASTER_BUTTON:{
238         QToolButton *fasterButton = new QToolButton;
239         setupButton( fasterButton );
240         CONNECT_MAP_SET( fasterButton, FASTER_ACTION );
241         BUTTON_SET_BAR(  fasterButton );
242         ENABLE_ON_INPUT( fasterButton );
243         widget = fasterButton;
244         }
245         break;
246     case FRAME_BUTTON: {
247         QToolButton *frameButton = new QToolButton;
248         setupButton( frameButton );
249         CONNECT_MAP_SET( frameButton, FRAME_ACTION );
250         BUTTON_SET_BAR(  frameButton );
251         ENABLE_ON_VIDEO( frameButton );
252         widget = frameButton;
253         }
254         break;
255     case FULLSCREEN_BUTTON:{
256         QToolButton *fullscreenButton = new QToolButton;
257         setupButton( fullscreenButton );
258         CONNECT_MAP_SET( fullscreenButton, FULLSCREEN_ACTION );
259         BUTTON_SET_BAR( fullscreenButton );
260         ENABLE_ON_VIDEO( fullscreenButton );
261         widget = fullscreenButton;
262         }
263         break;
264     case DEFULLSCREEN_BUTTON:{
265         QToolButton *fullscreenButton = new QToolButton;
266         setupButton( fullscreenButton );
267         CONNECT_MAP_SET( fullscreenButton, FULLSCREEN_ACTION );
268         BUTTON_SET_BAR( fullscreenButton )
269         ENABLE_ON_VIDEO( fullscreenButton );
270         widget = fullscreenButton;
271         }
272         break;
273     case EXTENDED_BUTTON:{
274         QToolButton *extSettingsButton = new QToolButton;
275         setupButton( extSettingsButton );
276         CONNECT_MAP_SET( extSettingsButton, EXTENDED_ACTION );
277         BUTTON_SET_BAR( extSettingsButton )
278         widget = extSettingsButton;
279         }
280         break;
281     case PLAYLIST_BUTTON:{
282         QToolButton *playlistButton = new QToolButton;
283         setupButton( playlistButton );
284         CONNECT_MAP_SET( playlistButton, PLAYLIST_ACTION );
285         BUTTON_SET_BAR( playlistButton );
286         widget = playlistButton;
287         }
288         break;
289     case SNAPSHOT_BUTTON:{
290         QToolButton *snapshotButton = new QToolButton;
291         setupButton( snapshotButton );
292         CONNECT_MAP_SET( snapshotButton, SNAPSHOT_ACTION );
293         BUTTON_SET_BAR(  snapshotButton );
294         ENABLE_ON_VIDEO( snapshotButton );
295         widget = snapshotButton;
296         }
297         break;
298     case RECORD_BUTTON:{
299         QToolButton *recordButton = new QToolButton;
300         setupButton( recordButton );
301         CONNECT_MAP_SET( recordButton, RECORD_ACTION );
302         BUTTON_SET_BAR(  recordButton );
303         ENABLE_ON_INPUT( recordButton );
304         widget = recordButton;
305         }
306         break;
307     case ATOB_BUTTON: {
308         AtoB_Button *ABButton = new AtoB_Button;
309         setupButton( ABButton );
310         BUTTON_SET_BAR( ABButton );
311         ENABLE_ON_INPUT( ABButton );
312         CONNECT_MAP_SET( ABButton, ATOB_ACTION );
313         CONNECT( THEMIM->getIM(), AtoBchanged( bool, bool),
314                  ABButton, setIcons( bool, bool ) );
315         widget = ABButton;
316         }
317         break;
318     case INPUT_SLIDER: {
319         InputSlider *slider = new InputSlider( Qt::Horizontal, NULL );
320
321         /* Update the position when the IM has changed */
322         CONNECT( THEMIM->getIM(), positionUpdated( float, int, int ),
323                 slider, setPosition( float, int, int ) );
324         /* And update the IM, when the position has changed */
325         CONNECT( slider, sliderDragged( float ),
326                  THEMIM->getIM(), sliderUpdate( float ) );
327         widget = slider;
328         }
329         break;
330     case MENU_BUTTONS:
331         widget = discFrame();
332 //        widget->hide();
333         break;
334     case TELETEXT_BUTTONS:
335         widget = telexFrame();
336 //        widget->hide();
337         break;
338     case VOLUME:
339         {
340             SoundWidget *snd = new SoundWidget( this, p_intf, b_shiny );
341             widget = snd;
342         }
343         break;
344     case TIME_LABEL:
345         {
346             TimeLabel *timeLabel = new TimeLabel( p_intf );
347             widget = timeLabel;
348         }
349         break;
350     case SPLITTER:
351         {
352             QFrame *line = new QFrame;
353             line->setFrameShape( QFrame::VLine );
354             line->setFrameShadow( QFrame::Raised );
355             line->setLineWidth( 0 );
356             line->setMidLineWidth( 1 );
357             widget = line;
358         }
359         break;
360     case ADVANCED_CONTROLLER:
361         {
362             advControls = new AdvControlsWidget( p_intf );
363             widget = advControls;
364         }
365         break;
366     case REVERSE_BUTTON:{
367         QToolButton *reverseButton = new QToolButton;
368         setupButton( reverseButton );
369         CONNECT_MAP_SET( reverseButton, REVERSE_ACTION );
370         BUTTON_SET_BAR(  reverseButton );
371         ENABLE_ON_INPUT( reverseButton );
372         widget = reverseButton;
373         }
374         break;
375     default:
376         msg_Warn( p_intf, "This should not happen" );
377         break;
378     }
379
380     /* Customize Buttons */
381     if( b_flat || b_big )
382     {
383         QToolButton *tmpButton = qobject_cast<QToolButton *>(widget);
384         if( tmpButton )
385         {
386             if( b_flat )
387                 tmpButton->setAutoRaise( b_flat );
388             if( b_big )
389             {
390                 tmpButton->setFixedSize( QSize( 32, 32 ) );
391                 tmpButton->setIconSize( QSize( 26, 26 ) );
392             }
393         }
394     }
395     return widget;
396 }
397
398 QWidget *AbstractController::discFrame()
399 {
400     /** Disc and Menus handling */
401     QWidget *discFrame = new QWidget( this );
402
403     QHBoxLayout *discLayout = new QHBoxLayout( discFrame );
404     discLayout->setSpacing( 0 ); discLayout->setMargin( 0 );
405
406     QToolButton *prevSectionButton = new QToolButton( discFrame );
407     setupButton( prevSectionButton );
408     BUTTON_SET_BAR2( prevSectionButton, dvd_prev,
409             qtr("Previous Chapter/Title" ) );
410     discLayout->addWidget( prevSectionButton );
411
412     QToolButton *menuButton = new QToolButton( discFrame );
413     setupButton( menuButton );
414     discLayout->addWidget( menuButton );
415     BUTTON_SET_BAR2( menuButton, dvd_menu, qtr( "Menu" ) );
416
417     QToolButton *nextSectionButton = new QToolButton( discFrame );
418     setupButton( nextSectionButton );
419     discLayout->addWidget( nextSectionButton );
420     BUTTON_SET_BAR2( nextSectionButton, dvd_next,
421             qtr("Next Chapter/Title" ) );
422
423     /* Change the navigation button display when the IM
424        navigation changes */
425     CONNECT( THEMIM->getIM(), titleChanged( bool ),
426             discFrame, setVisible( bool ) );
427     CONNECT( THEMIM->getIM(), chapterChanged( bool ),
428             menuButton, setVisible( bool ) );
429     /* Changes the IM navigation when triggered on the nav buttons */
430     CONNECT( prevSectionButton, clicked(), THEMIM->getIM(),
431             sectionPrev() );
432     CONNECT( nextSectionButton, clicked(), THEMIM->getIM(),
433             sectionNext() );
434     CONNECT( menuButton, clicked(), THEMIM->getIM(),
435             sectionMenu() );
436
437     return discFrame;
438 }
439
440 QWidget *AbstractController::telexFrame()
441 {
442     /**
443      * Telextext QFrame
444      **/
445     TeletextController *telexFrame = new TeletextController;
446     QHBoxLayout *telexLayout = new QHBoxLayout( telexFrame );
447     telexLayout->setSpacing( 0 ); telexLayout->setMargin( 0 );
448     CONNECT( THEMIM->getIM(), teletextPossible( bool ),
449              telexFrame, setVisible( bool ) );
450
451     /* On/Off button */
452     QToolButton *telexOn = new QToolButton;
453     telexFrame->telexOn = telexOn;
454     setupButton( telexOn );
455     BUTTON_SET_BAR2( telexOn, tv, qtr( "Teletext Activation" ) );
456     telexLayout->addWidget( telexOn );
457
458     /* Teletext Activation and set */
459     CONNECT( telexOn, clicked( bool ),
460              THEMIM->getIM(), activateTeletext( bool ) );
461     CONNECT( THEMIM->getIM(), teletextActivated( bool ),
462              telexFrame, enableTeletextButtons( bool ) );
463
464
465     /* Transparency button */
466     QToolButton *telexTransparent = new QToolButton;
467     telexFrame->telexTransparent = telexTransparent;
468     setupButton( telexTransparent );
469     BUTTON_SET_BAR2( telexTransparent, tvtelx,
470                      qtr( "Toggle Transparency " ) );
471     telexTransparent->setEnabled( false );
472     telexLayout->addWidget( telexTransparent );
473
474     /* Transparency change and set */
475     CONNECT( telexTransparent, clicked( bool ),
476             THEMIM->getIM(), telexSetTransparency( bool ) );
477     CONNECT( THEMIM->getIM(), teletextTransparencyActivated( bool ),
478             telexFrame, toggleTeletextTransparency( bool ) );
479
480
481     /* Page setting */
482     QSpinBox *telexPage = new QSpinBox;
483     telexFrame->telexPage = telexPage;
484     telexPage->setRange( 0, 999 );
485     telexPage->setValue( 100 );
486     telexPage->setAccelerated( true );
487     telexPage->setWrapping( true );
488     telexPage->setAlignment( Qt::AlignRight );
489     telexPage->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Minimum );
490     telexPage->setEnabled( false );
491     telexLayout->addWidget( telexPage );
492
493     /* Page change and set */
494     CONNECT( telexPage, valueChanged( int ),
495             THEMIM->getIM(), telexSetPage( int ) );
496     CONNECT( THEMIM->getIM(), newTelexPageSet( int ),
497             telexPage, setValue( int ) );
498
499     return telexFrame;
500 }
501 #undef CONNECT_MAP
502 #undef SET_MAPPING
503 #undef CONNECT_MAP_SET
504 #undef BUTTON_SET_BAR
505 #undef ENABLE_ON_VIDEO
506 #undef ENABLE_ON_INPUT
507
508 SoundWidget::SoundWidget( QWidget *_parent, intf_thread_t * _p_intf,
509                           bool b_shiny )
510                          : b_my_volume( false ), QWidget( _parent )
511 {
512     p_intf = _p_intf;
513     QHBoxLayout *layout = new QHBoxLayout( this );
514     layout->setSpacing( 0 ); layout->setMargin( 0 );
515     hVolLabel = new VolumeClickHandler( p_intf, this );
516
517     volMuteLabel = new QLabel;
518     volMuteLabel->setPixmap( QPixmap( ":/volume-medium" ) );
519     volMuteLabel->installEventFilter( hVolLabel );
520     layout->addWidget( volMuteLabel );
521
522     if( b_shiny )
523     {
524         volumeSlider = new SoundSlider( this,
525             config_GetInt( p_intf, "volume-step" ),
526             config_GetInt( p_intf, "qt-volume-complete" ),
527             config_GetPsz( p_intf, "qt-slider-colours" ) );
528     }
529     else
530     {
531         volumeSlider = new QSlider( this );
532         volumeSlider->setOrientation( Qt::Horizontal );
533     }
534     volumeSlider->setMaximumSize( QSize( 200, 40 ) );
535     volumeSlider->setMinimumSize( QSize( 85, 30 ) );
536     volumeSlider->setFocusPolicy( Qt::NoFocus );
537     layout->addWidget( volumeSlider );
538
539     /* Set the volume from the config */
540     volumeSlider->setValue( ( config_GetInt( p_intf, "volume" ) ) *
541                               VOLUME_MAX / (AOUT_VOLUME_MAX/2) );
542
543     /* Force the update at build time in order to have a muted icon if needed */
544     updateVolume( volumeSlider->value() );
545
546     /* Volume control connection */
547     CONNECT( volumeSlider, valueChanged( int ), this, updateVolume( int ) );
548     CONNECT( THEMIM, volumeChanged( void ), this, updateVolume( void ) );
549 }
550
551 void SoundWidget::updateVolume( int i_sliderVolume )
552 {
553     if( !b_my_volume )
554     {
555         int i_res = i_sliderVolume  * (AOUT_VOLUME_MAX / 2) / VOLUME_MAX;
556         aout_VolumeSet( p_intf, i_res );
557     }
558     if( i_sliderVolume == 0 )
559     {
560         volMuteLabel->setPixmap( QPixmap(":/volume-muted" ) );
561         volMuteLabel->setToolTip( qtr( "Unmute" ) );
562         return;
563     }
564
565     if( i_sliderVolume < VOLUME_MAX / 3 )
566         volMuteLabel->setPixmap( QPixmap( ":/volume-low" ) );
567     else if( i_sliderVolume > (VOLUME_MAX * 2 / 3 ) )
568         volMuteLabel->setPixmap( QPixmap( ":/volume-high" ) );
569     else volMuteLabel->setPixmap( QPixmap( ":/volume-medium" ) );
570     volMuteLabel->setToolTip( qtr( "Mute" ) );
571 }
572
573 void SoundWidget::updateVolume()
574 {
575     /* Audio part */
576     audio_volume_t i_volume;
577     aout_VolumeGet( p_intf, &i_volume );
578     i_volume = ( i_volume *  VOLUME_MAX )/ (AOUT_VOLUME_MAX/2);
579     int i_gauge = volumeSlider->value();
580     b_my_volume = false;
581     if( i_volume - i_gauge > 1 || i_gauge - i_volume > 1 )
582     {
583         b_my_volume = true;
584         volumeSlider->setValue( i_volume );
585         b_my_volume = false;
586     }
587 }
588
589 void TeletextController::toggleTeletextTransparency( bool b_transparent )
590 {
591     telexTransparent->setIcon( b_transparent ? QIcon( ":/tvtelx" )
592                                              : QIcon( ":/tvtelx-trans" ) );
593 }
594
595 void TeletextController::enableTeletextButtons( bool b_enabled )
596 {
597     telexOn->setChecked( b_enabled );
598     telexTransparent->setEnabled( b_enabled );
599     telexPage->setEnabled( b_enabled );
600 }
601
602 void PlayButton::updateButton( bool b_playing )
603 {
604     setIcon( b_playing ? QIcon( ":/pause_b" ) : QIcon( ":/play_b" ) );
605     setToolTip( b_playing ? qtr( "Pause the playback" )
606                           : qtr( I_PLAY_TOOLTIP ) );
607 }
608
609 void AtoB_Button::setIcons( bool timeA, bool timeB )
610 {
611     if( !timeA && !timeB)
612     {
613         setIcon( QIcon( ":/atob_nob" ) );
614         setToolTip( qtr( "Loop from point A to point B continuously\n"
615                          "Click to set point A" ) );
616     }
617     else if( timeA && !timeB )
618     {
619         setIcon( QIcon( ":/atob_noa" ) );
620         setToolTip( qtr( "Click to set point B" ) );
621     }
622     else if( timeA && timeB )
623     {
624         setIcon( QIcon( ":/atob" ) );
625         setToolTip( qtr( "Stop the A to B loop" ) );
626     }
627 }
628
629
630 //* Actions */
631 void AbstractController::doAction( int id_action )
632 {
633     switch( id_action )
634     {
635         case PLAY_ACTION:
636             play(); break;
637         case PREVIOUS_ACTION:
638             prev(); break;
639         case NEXT_ACTION:
640             next(); break;
641         case STOP_ACTION:
642             stop(); break;
643         case SLOWER_ACTION:
644             slower(); break;
645         case FASTER_ACTION:
646             faster(); break;
647         case FULLSCREEN_ACTION:
648             fullscreen(); break;
649         case EXTENDED_ACTION:
650             extSettings(); break;
651         case PLAYLIST_ACTION:
652             playlist(); break;
653         case SNAPSHOT_ACTION:
654             snapshot(); break;
655         case RECORD_ACTION:
656             record(); break;
657         case ATOB_ACTION:
658             THEMIM->getIM()->setAtoB(); break;
659         case FRAME_ACTION:
660             frame(); break;
661         case REVERSE_ACTION:
662             reverse(); break;
663         default:
664             msg_Dbg( p_intf, "Action: %i", id_action );
665             break;
666     }
667 }
668
669 void AbstractController::stop()
670 {
671     THEMIM->stop();
672 }
673
674 void AbstractController::play()
675 {
676     if( THEPL->current.i_size == 0 )
677     {
678         /* The playlist is empty, open a file requester */
679         THEDP->openFileDialog();
680         return;
681     }
682     THEMIM->togglePlayPause();
683 }
684
685 void AbstractController::prev()
686 {
687     THEMIM->prev();
688 }
689
690 void AbstractController::next()
691 {
692     THEMIM->next();
693 }
694
695 /**
696   * TODO
697  * This functions toggle the fullscreen mode
698  * If there is no video, it should first activate Visualisations...
699  *  This has also to be fixed in enableVideo()
700  */
701 void AbstractController::fullscreen()
702 {
703     vout_thread_t *p_vout =
704       (vout_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_VOUT, FIND_ANYWHERE );
705     if( p_vout)
706     {
707         var_SetBool( p_vout, "fullscreen", !var_GetBool( p_vout, "fullscreen" ) );
708         vlc_object_release( p_vout );
709     }
710 }
711
712 void AbstractController::snapshot()
713 {
714     vout_thread_t *p_vout =
715       (vout_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_VOUT, FIND_ANYWHERE );
716     if( p_vout )
717     {
718         vout_Control( p_vout, VOUT_SNAPSHOT );
719         vlc_object_release( p_vout );
720     }
721 }
722
723 void AbstractController::extSettings()
724 {
725     THEDP->extendedDialog();
726 }
727
728 void AbstractController::reverse()
729 {
730     THEMIM->getIM()->reverse();
731 }
732
733 void AbstractController::slower()
734 {
735     THEMIM->getIM()->slower();
736 }
737
738 void AbstractController::faster()
739 {
740     THEMIM->getIM()->faster();
741 }
742
743 void AbstractController::playlist()
744 {
745     if( p_intf->p_sys->p_mi ) p_intf->p_sys->p_mi->togglePlaylist();
746 }
747
748 void AbstractController::record()
749 {
750     input_thread_t *p_input = THEMIM->getInput();
751     if( p_input )
752     {
753         /* This method won't work fine if the stream can't be cut anywhere */
754         const bool b_recording = var_GetBool( p_input, "record" );
755         var_SetBool( p_input, "record", !b_recording );
756 #if 0
757         else
758         {
759             /* 'record' access-filter is not loaded, we open Save dialog */
760             input_item_t *p_item = input_GetItem( p_input );
761             if( !p_item )
762                 return;
763
764             char *psz = input_item_GetURI( p_item );
765             if( psz )
766                 THEDP->streamingDialog( NULL, psz, true );
767         }
768 #endif
769     }
770 }
771
772 void AbstractController::frame()
773 {
774     input_thread_t *p_input = THEMIM->getInput();
775     if( p_input )
776         var_SetVoid( p_input, "frame-next" );
777 }
778 #include <QHBoxLayout>
779 /*****************************
780  * DA Control Widget !
781  *****************************/
782 ControlsWidget::ControlsWidget( intf_thread_t *_p_i,
783                                 bool b_advControls ) :
784                                 AbstractController( _p_i )
785 {
786     setSizePolicy( QSizePolicy::Preferred , QSizePolicy::Maximum );
787
788     /* advanced Controls handling */
789     b_advancedVisible = b_advControls;
790
791     QVBoxLayout *controlLayout = new QVBoxLayout( this );
792     controlLayout->setLayoutMargins( 6, 4, 6, 2, 5 );
793     controlLayout->setSpacing( 0 );
794     QHBoxLayout *controlLayout1 = new QHBoxLayout;
795     controlLayout1->setSpacing( 0 );
796
797     QString line1 = getSettings()->value( "MainWindow/Controls1",
798             "18;19;25" ).toString();
799     parseAndCreate( line1, controlLayout1 );
800
801 /*    QString line2 = QString( "%1-2;%2;%3;%4;%5;%6;%6;%7;%8;%9;%6;%10;%11-4")
802         .arg( PLAY_BUTTON )         .arg( WIDGET_SPACER )
803         .arg( PREVIOUS_BUTTON )         .arg( STOP_BUTTON )
804         .arg( NEXT_BUTTON )        .arg( WIDGET_SPACER )
805         .arg( FULLSCREEN_BUTTON )        .arg( PLAYLIST_BUTTON )
806         .arg( EXTENDED_BUTTON )        .arg( WIDGET_SPACER_EXTEND )
807         .arg( VOLUME ); */
808
809     QHBoxLayout *controlLayout2 = new QHBoxLayout;
810     controlLayout2->setSpacing( 0 );
811     QString line2 = getSettings()->value( "MainWindow/Controls2",
812             "0-2;21;4;2;5;21;8;11;10;21;22;20-4" ).toString();
813     parseAndCreate( line2, controlLayout2 );
814
815     if( !b_advancedVisible && advControls ) advControls->hide();
816
817     controlLayout->addLayout( controlLayout1 );
818     controlLayout->addLayout( controlLayout2 );
819 }
820
821 ControlsWidget::~ControlsWidget()
822 {}
823
824 void ControlsWidget::toggleAdvanced()
825 {
826     if( !advControls ) return;
827
828     if( !b_advancedVisible )
829     {
830         advControls->show();
831         b_advancedVisible = true;
832     }
833     else
834     {
835         advControls->hide();
836         b_advancedVisible = false;
837     }
838     emit advancedControlsToggled( b_advancedVisible );
839 }
840
841 AdvControlsWidget::AdvControlsWidget( intf_thread_t *_p_i ) :
842                                      AbstractController( _p_i )
843 {
844     controlLayout = new QHBoxLayout( this );
845     controlLayout->setMargin( 0 );
846     controlLayout->setSpacing( 0 );
847
848     /* QString line = QString( "%1;%2;%3").arg( RECORD_BUTTON )
849         .arg( SNAPSHOT_BUTTON )
850         .arg( ATOB_BUTTON )
851         .arg( FRAME_BUTTON ); */
852
853     QString line = getSettings()->value( "MainWindow/AdvControl",
854             "12;13;14;15" ).toString();
855     parseAndCreate( line, controlLayout );
856 }
857
858 InputControlsWidget::InputControlsWidget( intf_thread_t *_p_i ) :
859                                      AbstractController( _p_i )
860 {
861     controlLayout = new QHBoxLayout( this );
862     controlLayout->setMargin( 0 );
863     controlLayout->setSpacing( 0 );
864
865     /*    QString line = QString( "%1-%2;%3;%4-%2")
866         .arg( SLOWER_BUTTON ).arg( WIDGET_FLAT )
867         .arg( INPUT_SLIDER )
868         .arg( FASTER_BUTTON ); */
869     QString line = getSettings()->value( "MainWindow/InputControl",
870                    "6-1;16;7-1" ).toString();
871     parseAndCreate( line, controlLayout );
872 }
873 /**********************************************************************
874  * Fullscrenn control widget
875  **********************************************************************/
876 FullscreenControllerWidget::FullscreenControllerWidget( intf_thread_t *_p_i )
877                            : AbstractController( _p_i )
878 {
879     i_mouse_last_x      = -1;
880     i_mouse_last_y      = -1;
881     b_mouse_over        = false;
882     i_mouse_last_move_x = -1;
883     i_mouse_last_move_y = -1;
884 #if HAVE_TRANSPARENCY
885     b_slow_hide_begin   = false;
886     i_slow_hide_timeout = 1;
887 #endif
888     b_fullscreen        = false;
889     i_hide_timeout      = 1;
890     p_vout              = NULL;
891     i_screennumber      = -1;
892
893     setWindowFlags( Qt::ToolTip );
894     setMinimumWidth( 600 );
895
896     setFrameShape( QFrame::StyledPanel );
897     setFrameStyle( QFrame::Sunken );
898     setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
899
900     controlLayout = new QHBoxLayout( this );
901     controlLayout->setLayoutMargins( 5, 2, 5, 2, 5 );
902
903     /* First line */
904     InputControlsWidget *inputC = new InputControlsWidget( p_intf );
905 //    controlLayout->addWidget( inputC, 0, 0, 1, -1 );
906
907     /* Second line */
908     /* QString line2 = QString( "%1-2;%2;%3;%4;%5;%2;%6;%2;%7;%2;%8;%9;%10-4")
909         .arg( PLAY_BUTTON )
910         .arg( WIDGET_SPACER )
911         .arg( PREVIOUS_BUTTON )
912         .arg( STOP_BUTTON )
913         .arg( NEXT_BUTTON )
914         .arg( MENU_BUTTONS )
915         .arg( TELETEXT_BUTTONS )
916         .arg( DEFULLSCREEN_BUTTON )
917         .arg( WIDGET_SPACER_EXTEND )
918         .arg( TIME_LABEL )
919         .arg( VOLUME ); */
920
921     QString line = getSettings()->value( "MainWindow/FSCline",
922             "0-2;21;4;2;5;21;18;21;19;21;9;22;23-4" ).toString();
923     parseAndCreate( line, controlLayout );
924
925     /* hiding timer */
926     p_hideTimer = new QTimer( this );
927     CONNECT( p_hideTimer, timeout(), this, hideFSC() );
928     p_hideTimer->setSingleShot( true );
929
930     /* slow hiding timer */
931 #if HAVE_TRANSPARENCY
932     p_slowHideTimer = new QTimer( this );
933     CONNECT( p_slowHideTimer, timeout(), this, slowHideFSC() );
934 #endif
935
936     adjustSize ();  /* need to get real width and height for moving */
937
938 #ifdef WIN32TRICK
939     setWindowOpacity( 0.0 );
940     b_fscHidden = true;
941     adjustSize();
942     show();
943 #endif
944
945     vlc_mutex_init_recursive( &lock );
946 }
947
948 FullscreenControllerWidget::~FullscreenControllerWidget()
949 {
950     getSettings()->setValue( "FullScreen/pos", pos() );
951     detachVout();
952     vlc_mutex_destroy( &lock );
953 }
954
955 /**
956  * Show fullscreen controller
957  */
958 void FullscreenControllerWidget::showFSC()
959 {
960     adjustSize();
961     /* center down */
962     int number = QApplication::desktop()->screenNumber( p_intf->p_sys->p_mi );
963     if( number != i_screennumber )
964     {
965         msg_Dbg( p_intf, "Calculation fullscreen controllers center");
966         /* screen has changed, calculate new position */
967         QRect screenRes = QApplication::desktop()->screenGeometry(number);
968         QPoint pos = QPoint( screenRes.x() + (screenRes.width() / 2) - (width() / 2),
969                              screenRes.y() + screenRes.height() - height());
970         move( pos );
971         i_screennumber = number;
972     }
973 #ifdef WIN32TRICK
974     // after quiting and going to fs, we need to call show()
975     if( isHidden() )
976         show();
977     if( b_fscHidden )
978     {
979         b_fscHidden = false;
980         setWindowOpacity( 1.0 );
981     }
982 #else
983     show();
984 #endif
985
986 #if HAVE_TRANSPARENCY
987     setWindowOpacity( DEFAULT_OPACITY );
988 #endif
989 }
990
991 /**
992  * Hide fullscreen controller
993  * FIXME: under windows it have to be done by moving out of screen
994  *        because hide() doesnt work
995  */
996 void FullscreenControllerWidget::hideFSC()
997 {
998 #ifdef WIN32TRICK
999     b_fscHidden = true;
1000     setWindowOpacity( 0.0 );    // simulate hidding
1001 #else
1002     hide();
1003 #endif
1004 }
1005
1006 /**
1007  * Plane to hide fullscreen controller
1008  */
1009 void FullscreenControllerWidget::planHideFSC()
1010 {
1011     vlc_mutex_lock( &lock );
1012     int i_timeout = i_hide_timeout;
1013     vlc_mutex_unlock( &lock );
1014
1015     p_hideTimer->start( i_timeout );
1016
1017 #if HAVE_TRANSPARENCY
1018     b_slow_hide_begin = true;
1019     i_slow_hide_timeout = i_timeout;
1020     p_slowHideTimer->start( i_slow_hide_timeout / 2 );
1021 #endif
1022 }
1023
1024 /**
1025  * Hidding fullscreen controller slowly
1026  * Linux: need composite manager
1027  * Windows: it is blinking, so it can be enabled by define TRASPARENCY
1028  */
1029 void FullscreenControllerWidget::slowHideFSC()
1030 {
1031 #if HAVE_TRANSPARENCY
1032     if( b_slow_hide_begin )
1033     {
1034         b_slow_hide_begin = false;
1035
1036         p_slowHideTimer->stop();
1037         /* the last part of time divided to 100 pieces */
1038         p_slowHideTimer->start( (int)( i_slow_hide_timeout / 2 / ( windowOpacity() * 100 ) ) );
1039
1040     }
1041     else
1042     {
1043 #ifdef WIN32TRICK
1044          if ( windowOpacity() > 0.0 && !b_fscHidden )
1045 #else
1046          if ( windowOpacity() > 0.0 )
1047 #endif
1048          {
1049              /* we should use 0.01 because of 100 pieces ^^^
1050                 but than it cannt be done in time */
1051              setWindowOpacity( windowOpacity() - 0.02 );
1052          }
1053
1054          if ( windowOpacity() <= 0.0 )
1055              p_slowHideTimer->stop();
1056     }
1057 #endif
1058 }
1059
1060 /**
1061  * event handling
1062  * events: show, hide, start timer for hidding
1063  */
1064 void FullscreenControllerWidget::customEvent( QEvent *event )
1065 {
1066     bool b_fs;
1067
1068     switch( event->type() )
1069     {
1070         case FullscreenControlToggle_Type:
1071             vlc_mutex_lock( &lock );
1072             b_fs = b_fullscreen;
1073             vlc_mutex_unlock( &lock );
1074             if( b_fs )
1075 #ifdef WIN32TRICK
1076                 if( b_fscHidden )
1077 #else
1078                 if( isHidden() )
1079 #endif
1080                 {
1081                     p_hideTimer->stop();
1082                     showFSC();
1083                 }
1084                 else
1085                     hideFSC();
1086             break;
1087         case FullscreenControlShow_Type:
1088             vlc_mutex_lock( &lock );
1089             b_fs = b_fullscreen;
1090             vlc_mutex_unlock( &lock );
1091
1092 #ifdef WIN32TRICK
1093             if( b_fs && b_fscHidden )
1094 #else
1095             if( b_fs && !isVisible() )
1096 #endif
1097                 showFSC();
1098             break;
1099         case FullscreenControlHide_Type:
1100             hideFSC();
1101             break;
1102         case FullscreenControlPlanHide_Type:
1103             if( !b_mouse_over ) // Only if the mouse is not over FSC
1104                 planHideFSC();
1105             break;
1106     }
1107 }
1108
1109 /**
1110  * On mouse move
1111  * moving with FSC
1112  */
1113 void FullscreenControllerWidget::mouseMoveEvent( QMouseEvent *event )
1114 {
1115     if ( event->buttons() == Qt::LeftButton )
1116     {
1117         int i_moveX = event->globalX() - i_mouse_last_x;
1118         int i_moveY = event->globalY() - i_mouse_last_y;
1119
1120         move( x() + i_moveX, y() + i_moveY );
1121
1122         i_mouse_last_x = event->globalX();
1123         i_mouse_last_y = event->globalY();
1124     }
1125 }
1126
1127 /**
1128  * On mouse press
1129  * store position of cursor
1130  */
1131 void FullscreenControllerWidget::mousePressEvent( QMouseEvent *event )
1132 {
1133     i_mouse_last_x = event->globalX();
1134     i_mouse_last_y = event->globalY();
1135 }
1136
1137 /**
1138  * On mouse go above FSC
1139  */
1140 void FullscreenControllerWidget::enterEvent( QEvent *event )
1141 {
1142     b_mouse_over = true;
1143
1144     p_hideTimer->stop();
1145 #if HAVE_TRANSPARENCY
1146     p_slowHideTimer->stop();
1147 #endif
1148 }
1149
1150 /**
1151  * On mouse go out from FSC
1152  */
1153 void FullscreenControllerWidget::leaveEvent( QEvent *event )
1154 {
1155     planHideFSC();
1156
1157     b_mouse_over = false;
1158 }
1159
1160 /**
1161  * When you get pressed key, send it to video output
1162  * FIXME: clearing focus by clearFocus() to not getting
1163  * key press events didnt work
1164  */
1165 void FullscreenControllerWidget::keyPressEvent( QKeyEvent *event )
1166 {
1167     int i_vlck = qtEventToVLCKey( event );
1168     if( i_vlck > 0 )
1169     {
1170         var_SetInteger( p_intf->p_libvlc, "key-pressed", i_vlck );
1171         event->accept();
1172     }
1173     else
1174         event->ignore();
1175 }
1176
1177 /* */
1178 static int FullscreenControllerWidgetFullscreenChanged( vlc_object_t *vlc_object,
1179                 const char *variable, vlc_value_t old_val,
1180                 vlc_value_t new_val,  void *data )
1181 {
1182     vout_thread_t *p_vout = (vout_thread_t *) vlc_object;
1183     msg_Dbg( p_vout, "Qt4: Fullscreen state changed" );
1184     FullscreenControllerWidget *p_fs = (FullscreenControllerWidget *)data;
1185
1186     p_fs->fullscreenChanged( p_vout, new_val.b_bool,
1187             var_GetInteger( p_vout, "mouse-hide-timeout" ) );
1188
1189     return VLC_SUCCESS;
1190 }
1191 /* */
1192 static int FullscreenControllerWidgetMouseMoved( vlc_object_t *vlc_object, const char *variable,
1193                                                  vlc_value_t old_val, vlc_value_t new_val,
1194                                                  void *data )
1195 {
1196     FullscreenControllerWidget *p_fs = (FullscreenControllerWidget *)data;
1197
1198     int i_mousex, i_mousey;
1199     bool b_toShow = false;
1200
1201     /* Get the value from the Vout - Trust the vout more than Qt */
1202     i_mousex = var_GetInteger( p_fs->p_vout, "mouse-x" );
1203     i_mousey = var_GetInteger( p_fs->p_vout, "mouse-y" );
1204
1205     /* First time */
1206     if( p_fs->i_mouse_last_move_x == -1 || p_fs->i_mouse_last_move_y == -1 )
1207     {
1208         p_fs->i_mouse_last_move_x = i_mousex;
1209         p_fs->i_mouse_last_move_y = i_mousey;
1210         b_toShow = true;
1211     }
1212     /* All other times */
1213     else
1214     {
1215         /* Trigger only if move > 3 px dans une direction */
1216         if( abs( p_fs->i_mouse_last_move_x - i_mousex ) > 2 ||
1217             abs( p_fs->i_mouse_last_move_y - i_mousey ) > 2 )
1218         {
1219             b_toShow = true;
1220             p_fs->i_mouse_last_move_x = i_mousex;
1221             p_fs->i_mouse_last_move_y = i_mousey;
1222         }
1223     }
1224
1225     if( b_toShow )
1226     {
1227         /* Show event */
1228         IMEvent *eShow = new IMEvent( FullscreenControlShow_Type, 0 );
1229         QApplication::postEvent( p_fs, static_cast<QEvent *>(eShow) );
1230
1231         /* Plan hide event */
1232         IMEvent *eHide = new IMEvent( FullscreenControlPlanHide_Type, 0 );
1233         QApplication::postEvent( p_fs, static_cast<QEvent *>(eHide) );
1234     }
1235
1236     return VLC_SUCCESS;
1237 }
1238
1239 /**
1240  * It is called when video start
1241  */
1242 void FullscreenControllerWidget::attachVout( vout_thread_t *p_nvout )
1243 {
1244     assert( p_nvout && !p_vout );
1245
1246     p_vout = p_nvout;
1247
1248     msg_Dbg( p_vout, "Qt FS: Attaching Vout" );
1249     vlc_mutex_lock( &lock );
1250
1251     var_AddCallback( p_vout, "fullscreen",
1252             FullscreenControllerWidgetFullscreenChanged, this );
1253             /* I miss a add and fire */
1254     fullscreenChanged( p_vout, var_GetBool( p_vout, "fullscreen" ),
1255                        var_GetInteger( p_vout, "mouse-hide-timeout" ) );
1256     vlc_mutex_unlock( &lock );
1257 }
1258
1259 /**
1260  * It is called after turn off video.
1261  */
1262 void FullscreenControllerWidget::detachVout()
1263 {
1264     if( p_vout )
1265     {
1266         msg_Dbg( p_vout, "Qt FS: Detaching Vout" );
1267         var_DelCallback( p_vout, "fullscreen",
1268                 FullscreenControllerWidgetFullscreenChanged, this );
1269         vlc_mutex_lock( &lock );
1270         fullscreenChanged( p_vout, false, 0 );
1271         vlc_mutex_unlock( &lock );
1272         p_vout = NULL;
1273     }
1274 }
1275
1276 /**
1277  * Register and unregister callback for mouse moving
1278  */
1279 void FullscreenControllerWidget::fullscreenChanged( vout_thread_t *p_vout,
1280         bool b_fs, int i_timeout )
1281 {
1282     msg_Dbg( p_vout, "Qt: Entering Fullscreen" );
1283
1284     vlc_mutex_lock( &lock );
1285     /* Entering fullscreen, register callback */
1286     if( b_fs && !b_fullscreen )
1287     {
1288         b_fullscreen = true;
1289         i_hide_timeout = i_timeout;
1290         var_AddCallback( p_vout, "mouse-moved",
1291                 FullscreenControllerWidgetMouseMoved, this );
1292     }
1293     /* Quitting fullscreen, unregistering callback */
1294     else if( !b_fs && b_fullscreen )
1295     {
1296         b_fullscreen = false;
1297         i_hide_timeout = i_timeout;
1298         var_DelCallback( p_vout, "mouse-moved",
1299                 FullscreenControllerWidgetMouseMoved, this );
1300
1301         /* Force fs hidding */
1302         IMEvent *eHide = new IMEvent( FullscreenControlHide_Type, 0 );
1303         QApplication::postEvent( this, static_cast<QEvent *>(eHide) );
1304     }
1305     vlc_mutex_unlock( &lock );
1306 }