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