]> git.sesse.net Git - vlc/blob - modules/gui/qt4/components/controller.cpp
Qt: simplify and solve a leak and make a previous commit unecessary.
[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: Jean-Baptiste Kempf <jb@videolan.org>
8  *          Ilkka Ollakka <ileoo@videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * ( at your option ) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <vlc_vout.h>
30 #include <vlc_keys.h>
31
32 #include "components/controller.hpp"
33 #include "components/controller_widget.hpp"
34 #include "components/interface_widgets.hpp"
35
36 #include "dialogs_provider.hpp" /* Opening Dialogs */
37 #include "input_manager.hpp"
38 #include "actions_manager.hpp"
39
40 #include "util/input_slider.hpp" /* InputSlider */
41 #include "util/customwidgets.hpp" /* qEventToKey */
42
43 #include <QSpacerItem>
44 #include <QToolButton>
45 #include <QHBoxLayout>
46 #include <QSignalMapper>
47 #include <QTimer>
48
49 /**********************************************************************
50  * TEH controls
51  **********************************************************************/
52
53 /******
54  * This is an abstract Toolbar/Controller
55  * This has helper to create any toolbar, any buttons and to manage the actions
56  *
57  *****/
58 AbstractController::AbstractController( intf_thread_t * _p_i, QWidget *_parent )
59                    : QFrame( _parent )
60 {
61     p_intf = _p_i;
62     advControls = NULL;
63
64     /* Main action provider */
65     toolbarActionsMapper = new QSignalMapper( this );
66     CONNECT( toolbarActionsMapper, mapped( int ),
67              ActionsManager::getInstance( p_intf  ), doAction( int ) );
68     CONNECT( THEMIM->getIM(), statusChanged( int ), this, setStatus( int ) );
69 }
70
71 /* Reemit some signals on status Change to activate some buttons */
72 void AbstractController::setStatus( int status )
73 {
74     bool b_hasInput = THEMIM->getIM()->hasInput();
75     /* Activate the interface buttons according to the presence of the input */
76     emit inputExists( b_hasInput );
77
78     emit inputPlaying( status == PLAYING_S );
79
80     emit inputIsRecordable( b_hasInput &&
81                             var_GetBool( THEMIM->getInput(), "can-record" ) );
82 }
83
84 /* Generic button setup */
85 void AbstractController::setupButton( QAbstractButton *aButton )
86 {
87     static QSizePolicy sizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
88     sizePolicy.setHorizontalStretch( 0 );
89     sizePolicy.setVerticalStretch( 0 );
90
91     aButton->setSizePolicy( sizePolicy );
92     aButton->setFixedSize( QSize( 26, 26 ) );
93     aButton->setIconSize( QSize( 20, 20 ) );
94     aButton->setFocusPolicy( Qt::NoFocus );
95 }
96
97 /* Open the generic config line for the toolbar, parse it
98  * and create the widgets accordingly */
99 void AbstractController::parseAndCreate( QString config,
100                                          QBoxLayout *controlLayout )
101 {
102     QStringList list = config.split( ";", QString::SkipEmptyParts ) ;
103     for( int i = 0; i < list.size(); i++ )
104     {
105         QStringList list2 = list.at( i ).split( "-" );
106         if( list2.size() < 1 )
107         {
108             msg_Warn( p_intf, "Parsing error. Report this" );
109             continue;
110         }
111
112         bool ok;
113         int i_option = WIDGET_NORMAL;
114         buttonType_e i_type = (buttonType_e)list2.at( 0 ).toInt( &ok );
115         if( !ok )
116         {
117             msg_Warn( p_intf, "Parsing error 0. Please report this" );
118             continue;
119         }
120
121         if( list2.size() > 1 )
122         {
123             i_option = list2.at( 1 ).toInt( &ok );
124             if( !ok )
125             {
126                 msg_Warn( p_intf, "Parsing error 1. Please report this" );
127                 continue;
128             }
129         }
130
131         createAndAddWidget( controlLayout, -1, i_type, i_option );
132     }
133 }
134
135 void AbstractController::createAndAddWidget( QBoxLayout *controlLayout,
136                                              int i_index,
137                                              buttonType_e i_type,
138                                              int i_option )
139 {
140     /* Special case for SPACERS, who aren't QWidgets */
141     if( i_type == WIDGET_SPACER )
142     {
143         controlLayout->insertSpacing( i_index, 16 );
144         return;
145     }
146
147     if(  i_type == WIDGET_SPACER_EXTEND )
148     {
149         controlLayout->insertStretch( i_index, 16 );
150         return;
151     }
152
153     QWidget *widg = createWidget( i_type, i_option );
154     if( !widg ) return;
155
156     controlLayout->insertWidget( i_index, widg );
157 }
158
159
160 #define CONNECT_MAP( a ) CONNECT( a, clicked(),  toolbarActionsMapper, map() )
161 #define SET_MAPPING( a, b ) toolbarActionsMapper->setMapping( a , b )
162 #define CONNECT_MAP_SET( a, b ) \
163     CONNECT_MAP( a ); \
164     SET_MAPPING( a, b );
165 #define BUTTON_SET_BAR( a_button ) \
166     a_button->setToolTip( tooltipL[button] );          \
167     a_button->setIcon( QIcon( iconL[button] ) );
168 #define BUTTON_SET_BAR2( button, image, tooltip ) \
169     button->setToolTip( tooltip );          \
170     button->setIcon( QIcon( ":/"#image ) );
171
172
173 #define ENABLE_ON_VIDEO( a ) \
174     CONNECT( THEMIM->getIM(), voutChanged( bool ), a, setEnabled( bool ) ); \
175     a->setEnabled( THEMIM->getIM()->hasVideo() ); /* TODO: is this necessary? when input is started before the interface? */
176
177 #define ENABLE_ON_INPUT( a ) \
178     CONNECT( this, inputExists( bool ), a, setEnabled( bool ) ); \
179     a->setEnabled( THEMIM->getIM()->hasInput() ); /* TODO: is this necessary? when input is started before the interface? */
180
181 QWidget *AbstractController::createWidget( buttonType_e button, int options )
182 {
183
184     bool b_flat = options & WIDGET_FLAT;
185     bool b_big = options & WIDGET_BIG;
186     bool b_shiny = options & WIDGET_SHINY;
187     bool b_special = false;
188
189     QWidget *widget = NULL;
190     switch( button )
191     {
192     case PLAY_BUTTON: {
193         PlayButton *playButton = new PlayButton( this );
194         setupButton( playButton );
195         BUTTON_SET_BAR(  playButton );
196         CONNECT_MAP_SET( playButton, PLAY_ACTION );
197         CONNECT( this, inputPlaying( bool ),
198                  playButton, updateButton( bool ));
199         widget = playButton;
200         }
201         break;
202     case STOP_BUTTON:{
203         QToolButton *stopButton = new QToolButton( this );
204         setupButton( stopButton );
205         CONNECT_MAP_SET( stopButton, STOP_ACTION );
206         BUTTON_SET_BAR(  stopButton );
207         widget = stopButton;
208         }
209         break;
210     case OPEN_BUTTON:{
211         QToolButton *openButton = new QToolButton( this );
212         setupButton( openButton );
213         CONNECT_MAP_SET( openButton, OPEN_ACTION );
214         BUTTON_SET_BAR( openButton );
215         widget = openButton;
216         }
217         break;
218     case PREVIOUS_BUTTON:{
219         QToolButton *prevButton = new QToolButton( this );
220         setupButton( prevButton );
221         CONNECT_MAP_SET( prevButton, PREVIOUS_ACTION );
222         BUTTON_SET_BAR( prevButton );
223         widget = prevButton;
224         }
225         break;
226     case NEXT_BUTTON:
227         {
228         QToolButton *nextButton = new QToolButton( this );
229         setupButton( nextButton );
230         CONNECT_MAP_SET( nextButton, NEXT_ACTION );
231         BUTTON_SET_BAR( nextButton );
232         widget = nextButton;
233         }
234         break;
235     case SLOWER_BUTTON:{
236         QToolButton *slowerButton = new QToolButton( this );
237         setupButton( slowerButton );
238         CONNECT_MAP_SET( slowerButton, SLOWER_ACTION );
239         BUTTON_SET_BAR(  slowerButton );
240         ENABLE_ON_INPUT( slowerButton );
241         widget = slowerButton;
242         }
243         break;
244     case FASTER_BUTTON:{
245         QToolButton *fasterButton = new QToolButton( this );
246         setupButton( fasterButton );
247         CONNECT_MAP_SET( fasterButton, FASTER_ACTION );
248         BUTTON_SET_BAR(  fasterButton );
249         ENABLE_ON_INPUT( fasterButton );
250         widget = fasterButton;
251         }
252         break;
253     case FRAME_BUTTON: {
254         QToolButton *frameButton = new QToolButton( this );
255         setupButton( frameButton );
256         CONNECT_MAP_SET( frameButton, FRAME_ACTION );
257         BUTTON_SET_BAR(  frameButton );
258         ENABLE_ON_VIDEO( frameButton );
259         widget = frameButton;
260         }
261         break;
262     case FULLSCREEN_BUTTON:{
263         QToolButton *fullscreenButton = new QToolButton( this );
264         setupButton( fullscreenButton );
265         CONNECT_MAP_SET( fullscreenButton, FULLSCREEN_ACTION );
266         BUTTON_SET_BAR( fullscreenButton );
267         ENABLE_ON_VIDEO( fullscreenButton );
268         widget = fullscreenButton;
269         }
270         break;
271     case DEFULLSCREEN_BUTTON:{
272         QToolButton *fullscreenButton = new QToolButton( this );
273         setupButton( fullscreenButton );
274         CONNECT_MAP_SET( fullscreenButton, FULLSCREEN_ACTION );
275         BUTTON_SET_BAR( fullscreenButton )
276         ENABLE_ON_VIDEO( fullscreenButton );
277         widget = fullscreenButton;
278         }
279         break;
280     case EXTENDED_BUTTON:{
281         QToolButton *extSettingsButton = new QToolButton( this );
282         setupButton( extSettingsButton );
283         CONNECT_MAP_SET( extSettingsButton, EXTENDED_ACTION );
284         BUTTON_SET_BAR( extSettingsButton )
285         widget = extSettingsButton;
286         }
287         break;
288     case PLAYLIST_BUTTON:{
289         QToolButton *playlistButton = new QToolButton( this );
290         setupButton( playlistButton );
291         CONNECT_MAP_SET( playlistButton, PLAYLIST_ACTION );
292         BUTTON_SET_BAR( playlistButton );
293         widget = playlistButton;
294         }
295         break;
296     case SNAPSHOT_BUTTON:{
297         QToolButton *snapshotButton = new QToolButton( this );
298         setupButton( snapshotButton );
299         CONNECT_MAP_SET( snapshotButton, SNAPSHOT_ACTION );
300         BUTTON_SET_BAR(  snapshotButton );
301         ENABLE_ON_VIDEO( snapshotButton );
302         widget = snapshotButton;
303         }
304         break;
305     case RECORD_BUTTON:{
306         QToolButton *recordButton = new QToolButton( this );
307         setupButton( recordButton );
308         CONNECT_MAP_SET( recordButton, RECORD_ACTION );
309         BUTTON_SET_BAR(  recordButton );
310         ENABLE_ON_INPUT( recordButton );
311         recordButton->setCheckable( true );
312         CONNECT( THEMIM->getIM(), recordingStateChanged( bool ),
313                  recordButton, setChecked( bool ) );
314         widget = recordButton;
315         }
316         break;
317     case ATOB_BUTTON: {
318         AtoB_Button *ABButton = new AtoB_Button( this );
319         setupButton( ABButton );
320         BUTTON_SET_BAR( ABButton );
321         ENABLE_ON_INPUT( ABButton );
322         CONNECT_MAP_SET( ABButton, ATOB_ACTION );
323         CONNECT( THEMIM->getIM(), AtoBchanged( bool, bool),
324                  ABButton, setIcons( bool, bool ) );
325         widget = ABButton;
326         }
327         break;
328     case INPUT_SLIDER: {
329         InputSlider *slider = new InputSlider( Qt::Horizontal, NULL );
330
331         /* Update the position when the IM has changed */
332         CONNECT( THEMIM->getIM(), positionUpdated( float, int, int ),
333                 slider, setPosition( float, int, int ) );
334         /* And update the IM, when the position has changed */
335         CONNECT( slider, sliderDragged( float ),
336                  THEMIM->getIM(), sliderUpdate( float ) );
337         widget = slider;
338         }
339         break;
340     case MENU_BUTTONS:
341         widget = discFrame();
342         widget->hide();
343         break;
344     case TELETEXT_BUTTONS:
345         widget = telexFrame();
346         widget->hide();
347         break;
348     case VOLUME_SPECIAL:
349         b_special = true;
350     case VOLUME:
351         {
352             SoundWidget *snd = new SoundWidget( this, p_intf, b_shiny, b_special );
353             widget = snd;
354         }
355         break;
356     case TIME_LABEL:
357         {
358             TimeLabel *timeLabel = new TimeLabel( p_intf );
359             widget = timeLabel;
360         }
361         break;
362     case SPLITTER:
363         {
364             QFrame *line = new QFrame( this );
365             line->setFrameShape( QFrame::VLine );
366             line->setFrameShadow( QFrame::Raised );
367             line->setLineWidth( 0 );
368             line->setMidLineWidth( 1 );
369             widget = line;
370         }
371         break;
372     case ADVANCED_CONTROLLER:
373         {
374             advControls = new AdvControlsWidget( p_intf, this );
375             widget = advControls;
376         }
377         break;
378     case REVERSE_BUTTON:{
379         QToolButton *reverseButton = new QToolButton( this );
380         setupButton( reverseButton );
381         CONNECT_MAP_SET( reverseButton, REVERSE_ACTION );
382         BUTTON_SET_BAR(  reverseButton );
383         ENABLE_ON_INPUT( reverseButton );
384         widget = reverseButton;
385         }
386         break;
387     case SKIP_BACK_BUTTON: {
388         QToolButton *skipBakButton = new QToolButton( this );
389         setupButton( skipBakButton );
390         CONNECT_MAP_SET( skipBakButton, SKIP_BACK_ACTION );
391         BUTTON_SET_BAR(  skipBakButton );
392         ENABLE_ON_INPUT( skipBakButton );
393         widget = skipBakButton;
394         }
395         break;
396     case SKIP_FW_BUTTON: {
397         QToolButton *skipFwButton = new QToolButton( this );
398         setupButton( skipFwButton );
399         CONNECT_MAP_SET( skipFwButton, SKIP_FW_ACTION );
400         BUTTON_SET_BAR(  skipFwButton );
401         ENABLE_ON_INPUT( skipFwButton );
402         widget = skipFwButton;
403         }
404         break;
405     case QUIT_BUTTON: {
406         QToolButton *quitButton = new QToolButton( this );
407         setupButton( quitButton );
408         CONNECT_MAP_SET( quitButton, QUIT_ACTION );
409         BUTTON_SET_BAR(  quitButton );
410         widget = quitButton;
411         }
412         break;
413     default:
414         msg_Warn( p_intf, "This should not happen %i", button );
415         break;
416     }
417
418     /* Customize Buttons */
419     if( b_flat || b_big )
420     {
421         QToolButton *tmpButton = qobject_cast<QToolButton *>(widget);
422         if( tmpButton )
423         {
424             if( b_flat )
425                 tmpButton->setAutoRaise( b_flat );
426             if( b_big )
427             {
428                 tmpButton->setFixedSize( QSize( 32, 32 ) );
429                 tmpButton->setIconSize( QSize( 26, 26 ) );
430             }
431         }
432     }
433     return widget;
434 }
435
436 QFrame *AbstractController::discFrame()
437 {
438     /** Disc and Menus handling */
439     QFrame *discFrame = new QFrame( this );
440
441     QHBoxLayout *discLayout = new QHBoxLayout( discFrame );
442     discLayout->setSpacing( 0 ); discLayout->setMargin( 0 );
443
444     QToolButton *prevSectionButton = new QToolButton( discFrame );
445     setupButton( prevSectionButton );
446     BUTTON_SET_BAR2( prevSectionButton, dvd_prev,
447             qtr("Previous Chapter/Title" ) );
448     discLayout->addWidget( prevSectionButton );
449
450     QToolButton *menuButton = new QToolButton( discFrame );
451     setupButton( menuButton );
452     discLayout->addWidget( menuButton );
453     BUTTON_SET_BAR2( menuButton, dvd_menu, qtr( "Menu" ) );
454
455     QToolButton *nextSectionButton = new QToolButton( discFrame );
456     setupButton( nextSectionButton );
457     discLayout->addWidget( nextSectionButton );
458     BUTTON_SET_BAR2( nextSectionButton, dvd_next,
459             qtr("Next Chapter/Title" ) );
460
461     /* Change the navigation button display when the IM
462        navigation changes */
463     CONNECT( THEMIM->getIM(), titleChanged( bool ),
464             discFrame, setVisible( bool ) );
465     CONNECT( THEMIM->getIM(), chapterChanged( bool ),
466             menuButton, setVisible( bool ) );
467     /* Changes the IM navigation when triggered on the nav buttons */
468     CONNECT( prevSectionButton, clicked(), THEMIM->getIM(),
469             sectionPrev() );
470     CONNECT( nextSectionButton, clicked(), THEMIM->getIM(),
471             sectionNext() );
472     CONNECT( menuButton, clicked(), THEMIM->getIM(),
473             sectionMenu() );
474
475     return discFrame;
476 }
477
478 QFrame *AbstractController::telexFrame()
479 {
480     /**
481      * Telextext QFrame
482      **/
483     QFrame *telexFrame = new QFrame;
484     QHBoxLayout *telexLayout = new QHBoxLayout( telexFrame );
485     telexLayout->setSpacing( 0 ); telexLayout->setMargin( 0 );
486     CONNECT( THEMIM->getIM(), teletextPossible( bool ),
487              telexFrame, setVisible( bool ) );
488
489     /* On/Off button */
490     QToolButton *telexOn = new QToolButton;
491     setupButton( telexOn );
492     BUTTON_SET_BAR2( telexOn, tv, qtr( "Teletext Activation" ) );
493     telexLayout->addWidget( telexOn );
494
495     /* Teletext Activation and set */
496     CONNECT( telexOn, clicked( bool ),
497              THEMIM->getIM(), activateTeletext( bool ) );
498
499
500     /* Transparency button */
501     QToolButton *telexTransparent = new QToolButton;
502     setupButton( telexTransparent );
503     BUTTON_SET_BAR2( telexTransparent, tvtelx,
504                      qtr( "Toggle Transparency " ) );
505     telexTransparent->setEnabled( false );
506     telexTransparent->setCheckable( true );
507     telexLayout->addWidget( telexTransparent );
508
509     /* Transparency change and set */
510     CONNECT( telexTransparent, clicked( bool ),
511             THEMIM->getIM(), telexSetTransparency( bool ) );
512     CONNECT( THEMIM->getIM(), teletextTransparencyActivated( bool ),
513              telexTransparent, setChecked( bool ) );
514
515
516     /* Page setting */
517     QSpinBox *telexPage = new QSpinBox( telexFrame );
518     telexPage->setRange( 0, 999 );
519     telexPage->setValue( 100 );
520     telexPage->setAccelerated( true );
521     telexPage->setWrapping( true );
522     telexPage->setAlignment( Qt::AlignRight );
523     telexPage->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Minimum );
524     telexPage->setEnabled( false );
525     telexLayout->addWidget( telexPage );
526
527     /* Page change and set */
528     CONNECT( telexPage, valueChanged( int ),
529             THEMIM->getIM(), telexSetPage( int ) );
530     CONNECT( THEMIM->getIM(), newTelexPageSet( int ),
531             telexPage, setValue( int ) );
532
533     CONNECT( THEMIM->getIM(), teletextActivated( bool ), telexPage, setEnabled( bool ) );
534     CONNECT( THEMIM->getIM(), teletextActivated( bool ), telexTransparent, setEnabled( bool ) );
535     CONNECT( THEMIM->getIM(), teletextActivated( bool ), telexOn, setChecked( bool ) );
536     return telexFrame;
537 }
538 #undef CONNECT_MAP
539 #undef SET_MAPPING
540 #undef CONNECT_MAP_SET
541 #undef BUTTON_SET_BAR
542 #undef ENABLE_ON_VIDEO
543 #undef ENABLE_ON_INPUT
544
545 #include <QHBoxLayout>
546 /*****************************
547  * DA Control Widget !
548  *****************************/
549 ControlsWidget::ControlsWidget( intf_thread_t *_p_i,
550                                 bool b_advControls,
551                                 QWidget *_parent ) :
552                                 AbstractController( _p_i, _parent )
553 {
554     setSizePolicy( QSizePolicy::Preferred , QSizePolicy::Maximum );
555
556     /* advanced Controls handling */
557     b_advancedVisible = b_advControls;
558
559     QVBoxLayout *controlLayout = new QVBoxLayout( this );
560     controlLayout->setLayoutMargins( 6, 4, 6, 2, 5 );
561     controlLayout->setSpacing( 0 );
562     QHBoxLayout *controlLayout1 = new QHBoxLayout;
563     controlLayout1->setSpacing( 0 );
564
565     QString line1 = getSettings()->value( "MainToolbar1", MAIN_TB1_DEFAULT )
566                                         .toString();
567     parseAndCreate( line1, controlLayout1 );
568
569     QHBoxLayout *controlLayout2 = new QHBoxLayout;
570     controlLayout2->setSpacing( 0 );
571     QString line2 = getSettings()->value( "MainToolbar2", MAIN_TB2_DEFAULT )
572                                         .toString();
573     parseAndCreate( line2, controlLayout2 );
574
575     if( !b_advancedVisible && advControls ) advControls->hide();
576
577     controlLayout->addLayout( controlLayout1 );
578     controlLayout->addLayout( controlLayout2 );
579 }
580
581 ControlsWidget::~ControlsWidget()
582 {}
583
584 void ControlsWidget::toggleAdvanced()
585 {
586     if( !advControls ) return;
587
588     if( !b_advancedVisible )
589     {
590         advControls->show();
591         b_advancedVisible = true;
592     }
593     else
594     {
595         advControls->hide();
596         b_advancedVisible = false;
597     }
598     emit advancedControlsToggled( b_advancedVisible );
599 }
600
601 AdvControlsWidget::AdvControlsWidget( intf_thread_t *_p_i, QWidget *_parent ) :
602                                      AbstractController( _p_i, _parent )
603 {
604     controlLayout = new QHBoxLayout( this );
605     controlLayout->setMargin( 0 );
606     controlLayout->setSpacing( 0 );
607
608     QString line = getSettings()->value( "AdvToolbar", ADV_TB_DEFAULT )
609         .toString();
610     parseAndCreate( line, controlLayout );
611 }
612
613 InputControlsWidget::InputControlsWidget( intf_thread_t *_p_i, QWidget *_parent ) :
614                                      AbstractController( _p_i, _parent )
615 {
616     controlLayout = new QHBoxLayout( this );
617     controlLayout->setMargin( 0 );
618     controlLayout->setSpacing( 0 );
619
620     QString line = getSettings()->value( "InputToolbar", INPT_TB_DEFAULT ).toString();
621     parseAndCreate( line, controlLayout );
622 }
623 /**********************************************************************
624  * Fullscrenn control widget
625  **********************************************************************/
626 FullscreenControllerWidget::FullscreenControllerWidget( intf_thread_t *_p_i, QWidget *parent )
627                            : AbstractController( _p_i, parent )
628 {
629     i_mouse_last_x      = -1;
630     i_mouse_last_y      = -1;
631     b_mouse_over        = false;
632     i_mouse_last_move_x = -1;
633     i_mouse_last_move_y = -1;
634 #if HAVE_TRANSPARENCY
635     b_slow_hide_begin   = false;
636     i_slow_hide_timeout = 1;
637 #endif
638     b_fullscreen        = false;
639     i_hide_timeout      = 1;
640     i_screennumber      = -1;
641
642     vout.clear();
643
644     setWindowFlags( Qt::ToolTip );
645     setMinimumWidth( 600 );
646
647     setFrameShape( QFrame::StyledPanel );
648     setFrameStyle( QFrame::Sunken );
649     setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
650
651     QVBoxLayout *controlLayout2 = new QVBoxLayout( this );
652     controlLayout2->setLayoutMargins( 5, 2, 5, 2, 5 );
653
654     /* First line */
655     InputControlsWidget *inputC = new InputControlsWidget( p_intf, this );
656     controlLayout2->addWidget( inputC );
657
658     controlLayout = new QHBoxLayout;
659     QString line = getSettings()->value( "MainWindow/FSCtoolbar", FSC_TB_DEFAULT ).toString();
660     parseAndCreate( line, controlLayout );
661     controlLayout2->addLayout( controlLayout );
662
663     /* hiding timer */
664     p_hideTimer = new QTimer( this );
665     CONNECT( p_hideTimer, timeout(), this, hideFSC() );
666     p_hideTimer->setSingleShot( true );
667
668     /* slow hiding timer */
669 #if HAVE_TRANSPARENCY
670     p_slowHideTimer = new QTimer( this );
671     CONNECT( p_slowHideTimer, timeout(), this, slowHideFSC() );
672 #endif
673
674     adjustSize ();  /* need to get real width and height for moving */
675
676 #ifdef WIN32TRICK
677     setWindowOpacity( 0.0 );
678     b_fscHidden = true;
679     adjustSize();
680     show();
681 #endif
682
683     vlc_mutex_init_recursive( &lock );
684
685     CONNECT( THEMIM->getIM(), voutListChanged( vout_thread_t **, int ), this, setVoutList( vout_thread_t **, int ) );
686 }
687
688 FullscreenControllerWidget::~FullscreenControllerWidget()
689 {
690     getSettings()->setValue( "FullScreen/pos", pos() );
691     setVoutList( NULL, 0 );
692     vlc_mutex_destroy( &lock );
693 }
694
695 /**
696  * Show fullscreen controller
697  */
698 void FullscreenControllerWidget::showFSC()
699 {
700     adjustSize();
701     /* center down */
702     int number = QApplication::desktop()->screenNumber( p_intf->p_sys->p_mi );
703     if( number != i_screennumber ||
704         screenRes != QApplication::desktop()->screenGeometry(number) )
705     {
706         screenRes = QApplication::desktop()->screenGeometry(number);
707         msg_Dbg( p_intf, "Calculation fullscreen controllers center");
708         /* screen has changed, calculate new position */
709         QPoint pos = QPoint( screenRes.x() + (screenRes.width() / 2) - (width() / 2),
710                              screenRes.y() + screenRes.height() - height());
711         move( pos );
712         i_screennumber = number;
713     }
714 #ifdef WIN32TRICK
715     // after quiting and going to fs, we need to call show()
716     if( isHidden() )
717         show();
718     if( b_fscHidden )
719     {
720         b_fscHidden = false;
721         setWindowOpacity( 1.0 );
722     }
723 #else
724     show();
725 #endif
726
727 #if HAVE_TRANSPARENCY
728     setWindowOpacity( DEFAULT_OPACITY );
729 #endif
730 }
731
732 /**
733  * Hide fullscreen controller
734  * FIXME: under windows it have to be done by moving out of screen
735  *        because hide() doesnt work
736  */
737 void FullscreenControllerWidget::hideFSC()
738 {
739 #ifdef WIN32TRICK
740     b_fscHidden = true;
741     setWindowOpacity( 0.0 );    // simulate hidding
742 #else
743     hide();
744 #endif
745 }
746
747 /**
748  * Plane to hide fullscreen controller
749  */
750 void FullscreenControllerWidget::planHideFSC()
751 {
752     vlc_mutex_lock( &lock );
753     int i_timeout = i_hide_timeout;
754     vlc_mutex_unlock( &lock );
755
756     p_hideTimer->start( i_timeout );
757
758 #if HAVE_TRANSPARENCY
759     b_slow_hide_begin = true;
760     i_slow_hide_timeout = i_timeout;
761     p_slowHideTimer->start( i_slow_hide_timeout / 2 );
762 #endif
763 }
764
765 /**
766  * Hidding fullscreen controller slowly
767  * Linux: need composite manager
768  * Windows: it is blinking, so it can be enabled by define TRASPARENCY
769  */
770 void FullscreenControllerWidget::slowHideFSC()
771 {
772 #if HAVE_TRANSPARENCY
773     if( b_slow_hide_begin )
774     {
775         b_slow_hide_begin = false;
776
777         p_slowHideTimer->stop();
778         /* the last part of time divided to 100 pieces */
779         p_slowHideTimer->start( (int)( i_slow_hide_timeout / 2 / ( windowOpacity() * 100 ) ) );
780
781     }
782     else
783     {
784 #ifdef WIN32TRICK
785          if ( windowOpacity() > 0.0 && !b_fscHidden )
786 #else
787          if ( windowOpacity() > 0.0 )
788 #endif
789          {
790              /* we should use 0.01 because of 100 pieces ^^^
791                 but than it cannt be done in time */
792              setWindowOpacity( windowOpacity() - 0.02 );
793          }
794
795          if ( windowOpacity() <= 0.0 )
796              p_slowHideTimer->stop();
797     }
798 #endif
799 }
800
801 /**
802  * event handling
803  * events: show, hide, start timer for hidding
804  */
805 void FullscreenControllerWidget::customEvent( QEvent *event )
806 {
807     bool b_fs;
808
809     switch( event->type() )
810     {
811         case FullscreenControlToggle_Type:
812             vlc_mutex_lock( &lock );
813             b_fs = b_fullscreen;
814             vlc_mutex_unlock( &lock );
815             if( b_fs )
816             {
817 #ifdef WIN32TRICK
818                 if( b_fscHidden )
819 #else
820                 if( isHidden() )
821 #endif
822                 {
823                     p_hideTimer->stop();
824                     showFSC();
825                 }
826                 else
827                     hideFSC();
828             }
829             break;
830         case FullscreenControlShow_Type:
831             vlc_mutex_lock( &lock );
832             b_fs = b_fullscreen;
833             vlc_mutex_unlock( &lock );
834
835 #ifdef WIN32TRICK
836             if( b_fs && b_fscHidden )
837 #else
838             if( b_fs && !isVisible() )
839 #endif
840                 showFSC();
841             break;
842         case FullscreenControlHide_Type:
843             hideFSC();
844             break;
845         case FullscreenControlPlanHide_Type:
846             if( !b_mouse_over ) // Only if the mouse is not over FSC
847                 planHideFSC();
848             break;
849         default:
850             break;
851     }
852 }
853
854 /**
855  * On mouse move
856  * moving with FSC
857  */
858 void FullscreenControllerWidget::mouseMoveEvent( QMouseEvent *event )
859 {
860     if ( event->buttons() == Qt::LeftButton )
861     {
862         int i_moveX = event->globalX() - i_mouse_last_x;
863         int i_moveY = event->globalY() - i_mouse_last_y;
864
865         move( x() + i_moveX, y() + i_moveY );
866
867         i_mouse_last_x = event->globalX();
868         i_mouse_last_y = event->globalY();
869     }
870 }
871
872 /**
873  * On mouse press
874  * store position of cursor
875  */
876 void FullscreenControllerWidget::mousePressEvent( QMouseEvent *event )
877 {
878     i_mouse_last_x = event->globalX();
879     i_mouse_last_y = event->globalY();
880 }
881
882 /**
883  * On mouse go above FSC
884  */
885 void FullscreenControllerWidget::enterEvent( QEvent *event )
886 {
887     b_mouse_over = true;
888
889     p_hideTimer->stop();
890 #if HAVE_TRANSPARENCY
891     p_slowHideTimer->stop();
892 #endif
893     event->accept();
894 }
895
896 /**
897  * On mouse go out from FSC
898  */
899 void FullscreenControllerWidget::leaveEvent( QEvent *event )
900 {
901     planHideFSC();
902
903     b_mouse_over = false;
904     event->accept();
905 }
906
907 /**
908  * When you get pressed key, send it to video output
909  * FIXME: clearing focus by clearFocus() to not getting
910  * key press events didnt work
911  */
912 void FullscreenControllerWidget::keyPressEvent( QKeyEvent *event )
913 {
914     int i_vlck = qtEventToVLCKey( event );
915     if( i_vlck > 0 )
916     {
917         var_SetInteger( p_intf->p_libvlc, "key-pressed", i_vlck );
918         event->accept();
919     }
920     else
921         event->ignore();
922 }
923
924 /* */
925 static int FullscreenControllerWidgetFullscreenChanged( vlc_object_t *vlc_object,
926                 const char *variable, vlc_value_t old_val,
927                 vlc_value_t new_val,  void *data )
928 {
929     vout_thread_t *p_vout = (vout_thread_t *) vlc_object;
930
931     msg_Dbg( p_vout, "Qt4: Fullscreen state changed" );
932     FullscreenControllerWidget *p_fs = (FullscreenControllerWidget *)data;
933
934     p_fs->fullscreenChanged( p_vout, new_val.b_bool, var_GetInteger( p_vout, "mouse-hide-timeout" ) );
935
936     return VLC_SUCCESS;
937 }
938 /* */
939 static int FullscreenControllerWidgetMouseMoved( vlc_object_t *vlc_object, const char *variable,
940                                                  vlc_value_t old_val, vlc_value_t new_val,
941                                                  void *data )
942 {
943     vout_thread_t *p_vout = (vout_thread_t *)vlc_object;
944     FullscreenControllerWidget *p_fs = (FullscreenControllerWidget *)data;
945
946     /* Get the value from the Vout - Trust the vout more than Qt */
947     const int i_mousex = var_GetInteger( p_vout, "mouse-x" );
948     const int i_mousey = var_GetInteger( p_vout, "mouse-y" );
949
950     p_fs->mouseChanged( p_vout, i_mousex, i_mousey );
951
952     return VLC_SUCCESS;
953 }
954
955 /**
956  * It is call to update the list of vout handled by the fullscreen controller
957  */
958 void FullscreenControllerWidget::setVoutList( vout_thread_t **pp_vout, int i_vout )
959 {
960     QList<vout_thread_t*> del;
961     QList<vout_thread_t*> add;
962
963     QList<vout_thread_t*> set;
964
965     /* */
966     for( int i = 0; i < i_vout; i++ )
967         set += pp_vout[i];
968
969     /* Vout to remove */
970     vlc_mutex_lock( &lock );
971     foreach( vout_thread_t *p_vout, vout )
972     {
973         if( !set.contains( p_vout ) )
974             del += p_vout;
975     }
976     vlc_mutex_unlock( &lock );
977
978     foreach( vout_thread_t *p_vout, del )
979     {
980         var_DelCallback( p_vout, "fullscreen",
981                          FullscreenControllerWidgetFullscreenChanged, this );
982         vlc_mutex_lock( &lock );
983         fullscreenChanged( p_vout, false, 0 );
984         vout.removeAll( p_vout );
985         vlc_mutex_unlock( &lock );
986     }
987
988     /* Vout to track */
989     vlc_mutex_lock( &lock );
990     foreach( vout_thread_t *p_vout, set )
991     {
992         if( !vout.contains( p_vout ) )
993             add += p_vout;
994     }
995     vlc_mutex_unlock( &lock );
996
997     foreach( vout_thread_t *p_vout, add )
998     {
999         vlc_object_hold( (vlc_object_t*)p_vout );
1000
1001         vlc_mutex_lock( &lock );
1002         vout.append( p_vout );
1003         var_AddCallback( p_vout, "fullscreen",
1004                          FullscreenControllerWidgetFullscreenChanged, this );
1005         /* I miss a add and fire */
1006         fullscreenChanged( p_vout, var_GetBool( p_vout, "fullscreen" ),
1007                            var_GetInteger( p_vout, "mouse-hide-timeout" ) );
1008         vlc_mutex_unlock( &lock );
1009     }
1010 }
1011 /**
1012  * Register and unregister callback for mouse moving
1013  */
1014 void FullscreenControllerWidget::fullscreenChanged( vout_thread_t *p_vout,
1015         bool b_fs, int i_timeout )
1016 {
1017     /* FIXME - multiple vout (ie multiple mouse position ?) and thread safety if multiple vout ? */
1018     msg_Dbg( p_vout, "Qt: Entering Fullscreen" );
1019
1020     vlc_mutex_lock( &lock );
1021     /* Entering fullscreen, register callback */
1022     if( b_fs && !b_fullscreen )
1023     {
1024         b_fullscreen = true;
1025         i_hide_timeout = i_timeout;
1026         var_AddCallback( p_vout, "mouse-moved",
1027                 FullscreenControllerWidgetMouseMoved, this );
1028     }
1029     /* Quitting fullscreen, unregistering callback */
1030     else if( !b_fs && b_fullscreen )
1031     {
1032         b_fullscreen = false;
1033         i_hide_timeout = i_timeout;
1034         var_DelCallback( p_vout, "mouse-moved",
1035                 FullscreenControllerWidgetMouseMoved, this );
1036
1037         /* Force fs hidding */
1038         IMEvent *eHide = new IMEvent( FullscreenControlHide_Type, 0 );
1039         QApplication::postEvent( this, eHide );
1040     }
1041     vlc_mutex_unlock( &lock );
1042 }
1043 /**
1044  * Mouse change callback (show/hide the controller on mouse movement)
1045  */
1046 void FullscreenControllerWidget::mouseChanged( vout_thread_t *p_vout, int i_mousex, int i_mousey )
1047 {
1048     bool b_toShow;
1049
1050     /* FIXME - multiple vout (ie multiple mouse position ?) and thread safety if multiple vout ? */
1051
1052     b_toShow = false;
1053     if( ( i_mouse_last_move_x == -1 || i_mouse_last_move_y == -1 ) ||
1054         ( abs( i_mouse_last_move_x - i_mousex ) > 2 ||
1055           abs( i_mouse_last_move_y - i_mousey ) > 2 ) )
1056     {
1057         i_mouse_last_move_x = i_mousex;
1058         i_mouse_last_move_y = i_mousey;
1059         b_toShow = true;
1060     }
1061
1062     if( b_toShow )
1063     {
1064         /* Show event */
1065         IMEvent *eShow = new IMEvent( FullscreenControlShow_Type, 0 );
1066         QApplication::postEvent( this, eShow );
1067
1068         /* Plan hide event */
1069         IMEvent *eHide = new IMEvent( FullscreenControlPlanHide_Type, 0 );
1070         QApplication::postEvent( this, eHide );
1071     }
1072 }
1073