]> git.sesse.net Git - vlc/blob - modules/gui/qt4/components/controller.cpp
4c5ffc061c13b8b2329d3c9c5bec35bdd06685ec
[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         }
376         break;
377     default:
378         msg_Warn( p_intf, "This should not happen %i", button );
379         break;
380     }
381
382     /* Customize Buttons */
383     if( b_flat || b_big )
384     {
385         QFrame *frame = qobject_cast<QFrame *>(widget);
386         if( frame )
387         {
388             QList<QToolButton *> allTButtons = frame->findChildren<QToolButton *>();
389             for( int i = 0; i < allTButtons.size(); i++ )
390                 applyAttributes( allTButtons[i], b_flat, b_big );
391         }
392         else
393         {
394             QToolButton *tmpButton = qobject_cast<QToolButton *>(widget);
395             if( tmpButton )
396                 applyAttributes( tmpButton, b_flat, b_big );
397         }
398     }
399     return widget;
400 }
401 #undef NORMAL_BUTTON
402
403 void AbstractController::applyAttributes( QToolButton *tmpButton, bool b_flat, bool b_big )
404 {
405     if( tmpButton )
406     {
407         if( b_flat )
408             tmpButton->setAutoRaise( b_flat );
409         if( b_big )
410         {
411             tmpButton->setFixedSize( QSize( 32, 32 ) );
412             tmpButton->setIconSize( QSize( 26, 26 ) );
413         }
414     }
415 }
416
417 QFrame *AbstractController::discFrame()
418 {
419     /** Disc and Menus handling */
420     QFrame *discFrame = new QFrame( this );
421
422     QHBoxLayout *discLayout = new QHBoxLayout( discFrame );
423     discLayout->setSpacing( 0 ); discLayout->setMargin( 0 );
424
425     QToolButton *prevSectionButton = new QToolButton( discFrame );
426     setupButton( prevSectionButton );
427     BUTTON_SET_BAR2( prevSectionButton, toolbar/dvd_prev,
428             qtr("Previous Chapter/Title" ) );
429     discLayout->addWidget( prevSectionButton );
430
431     QToolButton *menuButton = new QToolButton( discFrame );
432     setupButton( menuButton );
433     discLayout->addWidget( menuButton );
434     BUTTON_SET_BAR2( menuButton, toolbar/dvd_menu, qtr( "Menu" ) );
435
436     QToolButton *nextSectionButton = new QToolButton( discFrame );
437     setupButton( nextSectionButton );
438     discLayout->addWidget( nextSectionButton );
439     BUTTON_SET_BAR2( nextSectionButton, toolbar/dvd_next,
440             qtr("Next Chapter/Title" ) );
441
442     /* Change the navigation button display when the IM
443        navigation changes */
444     CONNECT( THEMIM->getIM(), titleChanged( bool ),
445             discFrame, setVisible( bool ) );
446     CONNECT( THEMIM->getIM(), chapterChanged( bool ),
447             menuButton, setVisible( bool ) );
448     /* Changes the IM navigation when triggered on the nav buttons */
449     CONNECT( prevSectionButton, clicked(), THEMIM->getIM(),
450             sectionPrev() );
451     CONNECT( nextSectionButton, clicked(), THEMIM->getIM(),
452             sectionNext() );
453     CONNECT( menuButton, clicked(), THEMIM->getIM(),
454             sectionMenu() );
455     connect( THEMIM->getIM(), SIGNAL( titleChanged( bool ) ),
456              this, SIGNAL( sizeChanged() ) );
457
458     return discFrame;
459 }
460
461 QFrame *AbstractController::telexFrame()
462 {
463     /**
464      * Telextext QFrame
465      **/
466     QFrame *telexFrame = new QFrame;
467     QHBoxLayout *telexLayout = new QHBoxLayout( telexFrame );
468     telexLayout->setSpacing( 0 ); telexLayout->setMargin( 0 );
469     CONNECT( THEMIM->getIM(), teletextPossible( bool ),
470              telexFrame, setVisible( bool ) );
471     connect( THEMIM->getIM(), SIGNAL( teletextPossible( bool ) ),
472              this, SIGNAL( sizeChanged() ) );
473
474     /* On/Off button */
475     QToolButton *telexOn = new QToolButton;
476     setupButton( telexOn );
477     BUTTON_SET_BAR2( telexOn, toolbar/tv, qtr( "Teletext Activation" ) );
478     telexOn->setEnabled( false );
479     telexOn->setCheckable( true );
480
481     telexLayout->addWidget( telexOn );
482
483     /* Teletext Activation and set */
484     CONNECT( telexOn, clicked( bool ),
485              THEMIM->getIM(), activateTeletext( bool ) );
486     CONNECT( THEMIM->getIM(), teletextPossible( bool ),
487              telexOn, setEnabled( bool ) );
488
489     /* Transparency button */
490     QToolButton *telexTransparent = new QToolButton;
491     setupButton( telexTransparent );
492     BUTTON_SET_BAR2( telexTransparent, toolbar/tvtelx,
493                      qtr( "Toggle Transparency " ) );
494     telexTransparent->setEnabled( false );
495     telexTransparent->setCheckable( true );
496     telexLayout->addWidget( telexTransparent );
497
498     /* Transparency change and set */
499     CONNECT( telexTransparent, clicked( bool ),
500             THEMIM->getIM(), telexSetTransparency( bool ) );
501     CONNECT( THEMIM->getIM(), teletextTransparencyActivated( bool ),
502              telexTransparent, setChecked( bool ) );
503
504
505     /* Page setting */
506     QSpinBox *telexPage = new QSpinBox( telexFrame );
507     telexPage->setRange( 0, 999 );
508     telexPage->setValue( 100 );
509     telexPage->setAccelerated( true );
510     telexPage->setWrapping( true );
511     telexPage->setAlignment( Qt::AlignRight );
512     telexPage->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Minimum );
513     telexPage->setEnabled( false );
514     telexLayout->addWidget( telexPage );
515
516     /* Page change and set */
517     CONNECT( telexPage, valueChanged( int ),
518             THEMIM->getIM(), telexSetPage( int ) );
519     CONNECT( THEMIM->getIM(), newTelexPageSet( int ),
520             telexPage, setValue( int ) );
521
522     CONNECT( THEMIM->getIM(), teletextActivated( bool ), telexPage, setEnabled( bool ) );
523     CONNECT( THEMIM->getIM(), teletextActivated( bool ), telexTransparent, setEnabled( bool ) );
524     CONNECT( THEMIM->getIM(), teletextActivated( bool ), telexOn, setChecked( bool ) );
525     return telexFrame;
526 }
527 #undef CONNECT_MAP
528 #undef SET_MAPPING
529 #undef CONNECT_MAP_SET
530 #undef BUTTON_SET_BAR
531 #undef BUTTON_SET_BAR2
532 #undef ENABLE_ON_VIDEO
533 #undef ENABLE_ON_INPUT
534
535 #include <QHBoxLayout>
536 /*****************************
537  * DA Control Widget !
538  *****************************/
539 ControlsWidget::ControlsWidget( intf_thread_t *_p_i,
540                                 bool b_advControls,
541                                 QWidget *_parent ) :
542                                 AbstractController( _p_i, _parent )
543 {
544     /* advanced Controls handling */
545     b_advancedVisible = b_advControls;
546
547     QVBoxLayout *controlLayout = new QVBoxLayout( this );
548     controlLayout->setLayoutMargins( 6, 4, 6, 2, 5 );
549     controlLayout->setSpacing( 0 );
550     QHBoxLayout *controlLayout1 = new QHBoxLayout;
551     controlLayout1->setSpacing( 0 );
552
553     QString line1 = getSettings()->value( "MainToolbar1", MAIN_TB1_DEFAULT )
554                                         .toString();
555     parseAndCreate( line1, controlLayout1 );
556
557     QHBoxLayout *controlLayout2 = new QHBoxLayout;
558     controlLayout2->setSpacing( 0 );
559     QString line2 = getSettings()->value( "MainToolbar2", MAIN_TB2_DEFAULT )
560                                         .toString();
561     parseAndCreate( line2, controlLayout2 );
562
563     if( !b_advancedVisible && advControls ) advControls->hide();
564
565     controlLayout->addLayout( controlLayout1 );
566     controlLayout->addLayout( controlLayout2 );
567 }
568
569 ControlsWidget::~ControlsWidget()
570 {}
571
572 void ControlsWidget::toggleAdvanced()
573 {
574     if( !advControls ) return;
575
576     if( !b_advancedVisible )
577     {
578         advControls->show();
579         b_advancedVisible = true;
580     }
581     else
582     {
583         advControls->hide();
584         b_advancedVisible = false;
585     }
586     emit advancedControlsToggled( b_advancedVisible );
587 }
588
589 AdvControlsWidget::AdvControlsWidget( intf_thread_t *_p_i, QWidget *_parent ) :
590                                      AbstractController( _p_i, _parent )
591 {
592     controlLayout = new QHBoxLayout( this );
593     controlLayout->setMargin( 0 );
594     controlLayout->setSpacing( 0 );
595
596     QString line = getSettings()->value( "AdvToolbar", ADV_TB_DEFAULT )
597         .toString();
598     parseAndCreate( line, controlLayout );
599 }
600
601 InputControlsWidget::InputControlsWidget( 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( "InputToolbar", INPT_TB_DEFAULT ).toString();
609     parseAndCreate( line, controlLayout );
610 }
611 /**********************************************************************
612  * Fullscrenn control widget
613  **********************************************************************/
614 FullscreenControllerWidget::FullscreenControllerWidget( intf_thread_t *_p_i, QWidget *_parent )
615                            : AbstractController( _p_i, _parent )
616 {
617     i_mouse_last_x      = -1;
618     i_mouse_last_y      = -1;
619     b_mouse_over        = false;
620     i_mouse_last_move_x = -1;
621     i_mouse_last_move_y = -1;
622 #if HAVE_TRANSPARENCY
623     b_slow_hide_begin   = false;
624     i_slow_hide_timeout = 1;
625 #endif
626     b_fullscreen        = false;
627     i_hide_timeout      = 1;
628     i_screennumber      = -1;
629
630     vout.clear();
631
632     setWindowFlags( Qt::ToolTip );
633     setMinimumWidth( 600 );
634
635     setFrameShape( QFrame::StyledPanel );
636     setFrameStyle( QFrame::Sunken );
637     setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
638
639     QVBoxLayout *controlLayout2 = new QVBoxLayout( this );
640     controlLayout2->setLayoutMargins( 4, 6, 4, 2, 5 );
641
642     /* First line */
643     InputControlsWidget *inputC = new InputControlsWidget( p_intf, this );
644     controlLayout2->addWidget( inputC );
645
646     controlLayout = new QHBoxLayout;
647     QString line = getSettings()->value( "MainWindow/FSCtoolbar", FSC_TB_DEFAULT ).toString();
648     parseAndCreate( line, controlLayout );
649     controlLayout2->addLayout( controlLayout );
650
651     /* hiding timer */
652     p_hideTimer = new QTimer( this );
653     CONNECT( p_hideTimer, timeout(), this, hideFSC() );
654     p_hideTimer->setSingleShot( true );
655
656     /* slow hiding timer */
657 #if HAVE_TRANSPARENCY
658     p_slowHideTimer = new QTimer( this );
659     CONNECT( p_slowHideTimer, timeout(), this, slowHideFSC() );
660 #endif
661
662     vlc_mutex_init_recursive( &lock );
663
664     CONNECT( THEMIM->getIM(), voutListChanged( vout_thread_t **, int ),
665              this, setVoutList( vout_thread_t **, int ) );
666
667     /* First Move */
668     QRect rect1 = getSettings()->value( "FullScreen/screen" ).toRect();
669     QPoint pos1 = getSettings()->value( "FullScreen/pos" ).toPoint();
670     int number =  config_GetInt( p_intf, "qt-fullscreen-screennumber" );
671     if( number == -1 || number > QApplication::desktop()->numScreens() )
672         number = QApplication::desktop()->screenNumber( p_intf->p_sys->p_mi );
673
674     QRect rect = QApplication::desktop()->screenGeometry( number );
675     if( rect == rect1 && rect.contains( pos1, true ) )
676     {
677         move( pos1 );
678         i_screennumber = number;
679         screenRes = QApplication::desktop()->screenGeometry(number);
680     }
681     else
682     {
683         centerFSC( number );
684     }
685
686 }
687
688 FullscreenControllerWidget::~FullscreenControllerWidget()
689 {
690     QPoint pos1 = pos();
691     QRect rect1 = QApplication::desktop()->screenGeometry( pos1 );
692     getSettings()->setValue( "FullScreen/pos", pos1 );
693     getSettings()->setValue( "FullScreen/screen", rect1 );
694
695     setVoutList( NULL, 0 );
696     vlc_mutex_destroy( &lock );
697 }
698
699 void FullscreenControllerWidget::centerFSC( int number )
700 {
701     screenRes = QApplication::desktop()->screenGeometry(number);
702
703     /* screen has changed, calculate new position */
704     QPoint pos = QPoint( screenRes.x() + (screenRes.width() / 2) - (sizeHint().width() / 2),
705             screenRes.y() + screenRes.height() - sizeHint().height());
706     move( pos );
707
708     i_screennumber = number;
709 }
710
711 /**
712  * Show fullscreen controller
713  */
714 void FullscreenControllerWidget::showFSC()
715 {
716     adjustSize();
717
718     int number = QApplication::desktop()->screenNumber( p_intf->p_sys->p_mi );
719
720     if( number != i_screennumber ||
721         screenRes != QApplication::desktop()->screenGeometry(number) )
722     {
723         centerFSC( number );
724         msg_Dbg( p_intf, "Recentering the Fullscreen Controller" );
725     }
726
727 #if HAVE_TRANSPARENCY
728     setWindowOpacity( config_GetFloat( p_intf, "qt-fs-opacity" )  );
729 #endif
730
731     show();
732 }
733
734 /**
735  * Plane to hide fullscreen controller
736  */
737 void FullscreenControllerWidget::planHideFSC()
738 {
739     vlc_mutex_lock( &lock );
740     int i_timeout = i_hide_timeout;
741     vlc_mutex_unlock( &lock );
742
743     p_hideTimer->start( i_timeout );
744
745 #if HAVE_TRANSPARENCY
746     b_slow_hide_begin = true;
747     i_slow_hide_timeout = i_timeout;
748     p_slowHideTimer->start( i_slow_hide_timeout / 2 );
749 #endif
750 }
751
752 /**
753  * Hidding fullscreen controller slowly
754  * Linux: need composite manager
755  * Windows: it is blinking, so it can be enabled by define TRASPARENCY
756  */
757 void FullscreenControllerWidget::slowHideFSC()
758 {
759 #if HAVE_TRANSPARENCY
760     if( b_slow_hide_begin )
761     {
762         b_slow_hide_begin = false;
763
764         p_slowHideTimer->stop();
765         /* the last part of time divided to 100 pieces */
766         p_slowHideTimer->start( (int)( i_slow_hide_timeout / 2 / ( windowOpacity() * 100 ) ) );
767
768     }
769     else
770     {
771          if ( windowOpacity() > 0.0 )
772          {
773              /* we should use 0.01 because of 100 pieces ^^^
774                 but than it cannt be done in time */
775              setWindowOpacity( windowOpacity() - 0.02 );
776          }
777
778          if ( windowOpacity() <= 0.0 )
779              p_slowHideTimer->stop();
780     }
781 #endif
782 }
783
784 /**
785  * event handling
786  * events: show, hide, start timer for hidding
787  */
788 void FullscreenControllerWidget::customEvent( QEvent *event )
789 {
790     bool b_fs;
791
792     switch( event->type() )
793     {
794         /* This is used when the 'i' hotkey is used, to force quick toggle */
795         case FullscreenControlToggle_Type:
796             vlc_mutex_lock( &lock );
797             b_fs = b_fullscreen;
798             vlc_mutex_unlock( &lock );
799
800             if( b_fs )
801             {
802                 if( isHidden() )
803                 {
804                     p_hideTimer->stop();
805                     showFSC();
806                 }
807                 else
808                     hideFSC();
809             }
810             break;
811         /* Event called to Show the FSC on mouseChanged() */
812         case FullscreenControlShow_Type:
813             vlc_mutex_lock( &lock );
814             b_fs = b_fullscreen;
815             vlc_mutex_unlock( &lock );
816
817             if( b_fs )
818                 showFSC();
819
820             break;
821         /* Start the timer to hide later, called usually with above case */
822         case FullscreenControlPlanHide_Type:
823             if( !b_mouse_over ) // Only if the mouse is not over FSC
824                 planHideFSC();
825             break;
826         /* Hide */
827         case FullscreenControlHide_Type:
828             hideFSC();
829             break;
830         default:
831             break;
832     }
833 }
834
835 /**
836  * On mouse move
837  * moving with FSC
838  */
839 void FullscreenControllerWidget::mouseMoveEvent( QMouseEvent *event )
840 {
841     if( event->buttons() == Qt::LeftButton )
842     {
843         if( i_mouse_last_x == -1 || i_mouse_last_y == -1 )
844             return;
845
846         int i_moveX = event->globalX() - i_mouse_last_x;
847         int i_moveY = event->globalY() - i_mouse_last_y;
848
849         move( x() + i_moveX, y() + i_moveY );
850
851         i_mouse_last_x = event->globalX();
852         i_mouse_last_y = event->globalY();
853     }
854 }
855
856 /**
857  * On mouse press
858  * store position of cursor
859  */
860 void FullscreenControllerWidget::mousePressEvent( QMouseEvent *event )
861 {
862     i_mouse_last_x = event->globalX();
863     i_mouse_last_y = event->globalY();
864     event->accept();
865 }
866
867 void FullscreenControllerWidget::mouseReleaseEvent( QMouseEvent *event )
868 {
869     i_mouse_last_x = -1;
870     i_mouse_last_y = -1;
871     event->accept();
872 }
873
874 /**
875  * On mouse go above FSC
876  */
877 void FullscreenControllerWidget::enterEvent( QEvent *event )
878 {
879     b_mouse_over = true;
880
881     p_hideTimer->stop();
882 #if HAVE_TRANSPARENCY
883     p_slowHideTimer->stop();
884     setWindowOpacity( DEFAULT_OPACITY );
885 #endif
886     event->accept();
887 }
888
889 /**
890  * On mouse go out from FSC
891  */
892 void FullscreenControllerWidget::leaveEvent( QEvent *event )
893 {
894     planHideFSC();
895
896     b_mouse_over = false;
897     event->accept();
898 }
899
900 /**
901  * When you get pressed key, send it to video output
902  */
903 void FullscreenControllerWidget::keyPressEvent( QKeyEvent *event )
904 {
905     emit keyPressed( event );
906 }
907
908 /* */
909 static int FullscreenControllerWidgetFullscreenChanged( vlc_object_t *vlc_object,
910                 const char *variable, vlc_value_t old_val,
911                 vlc_value_t new_val,  void *data )
912 {
913     vout_thread_t *p_vout = (vout_thread_t *) vlc_object;
914
915     msg_Dbg( p_vout, "Qt4: Fullscreen state changed" );
916     FullscreenControllerWidget *p_fs = (FullscreenControllerWidget *)data;
917
918     p_fs->fullscreenChanged( p_vout, new_val.b_bool, var_GetInteger( p_vout, "mouse-hide-timeout" ) );
919
920     return VLC_SUCCESS;
921 }
922 /* */
923 static int FullscreenControllerWidgetMouseMoved( vlc_object_t *vlc_object, const char *variable,
924                                                  vlc_value_t old_val, vlc_value_t new_val,
925                                                  void *data )
926 {
927     vout_thread_t *p_vout = (vout_thread_t *)vlc_object;
928     FullscreenControllerWidget *p_fs = (FullscreenControllerWidget *)data;
929
930     /* Get the value from the Vout - Trust the vout more than Qt */
931     const int i_mousex = var_GetInteger( p_vout, "mouse-x" );
932     const int i_mousey = var_GetInteger( p_vout, "mouse-y" );
933
934     p_fs->mouseChanged( p_vout, i_mousex, i_mousey );
935
936     return VLC_SUCCESS;
937 }
938
939 /**
940  * It is call to update the list of vout handled by the fullscreen controller
941  */
942 void FullscreenControllerWidget::setVoutList( vout_thread_t **pp_vout, int i_vout )
943 {
944     QList<vout_thread_t*> del;
945     QList<vout_thread_t*> add;
946
947     QList<vout_thread_t*> set;
948
949     /* */
950     for( int i = 0; i < i_vout; i++ )
951         set += pp_vout[i];
952
953     /* Vout to remove */
954     vlc_mutex_lock( &lock );
955     foreach( vout_thread_t *p_vout, vout )
956     {
957         if( !set.contains( p_vout ) )
958             del += p_vout;
959     }
960     vlc_mutex_unlock( &lock );
961
962     foreach( vout_thread_t *p_vout, del )
963     {
964         var_DelCallback( p_vout, "fullscreen",
965                          FullscreenControllerWidgetFullscreenChanged, this );
966         vlc_mutex_lock( &lock );
967         fullscreenChanged( p_vout, false, 0 );
968         vout.removeAll( p_vout );
969         vlc_mutex_unlock( &lock );
970
971         vlc_object_release( VLC_OBJECT(p_vout) );
972     }
973
974     /* Vout to track */
975     vlc_mutex_lock( &lock );
976     foreach( vout_thread_t *p_vout, set )
977     {
978         if( !vout.contains( p_vout ) )
979             add += p_vout;
980     }
981     vlc_mutex_unlock( &lock );
982
983     foreach( vout_thread_t *p_vout, add )
984     {
985         vlc_object_hold( VLC_OBJECT(p_vout) );
986
987         vlc_mutex_lock( &lock );
988         vout.append( p_vout );
989         var_AddCallback( p_vout, "fullscreen",
990                          FullscreenControllerWidgetFullscreenChanged, this );
991         /* I miss a add and fire */
992         fullscreenChanged( p_vout, var_GetBool( p_vout, "fullscreen" ),
993                            var_GetInteger( p_vout, "mouse-hide-timeout" ) );
994         vlc_mutex_unlock( &lock );
995     }
996 }
997 /**
998  * Register and unregister callback for mouse moving
999  */
1000 void FullscreenControllerWidget::fullscreenChanged( vout_thread_t *p_vout,
1001         bool b_fs, int i_timeout )
1002 {
1003     /* FIXME - multiple vout (ie multiple mouse position ?) and thread safety if multiple vout ? */
1004     msg_Dbg( p_vout, "Qt: Entering Fullscreen" );
1005
1006     vlc_mutex_lock( &lock );
1007     /* Entering fullscreen, register callback */
1008     if( b_fs && !b_fullscreen )
1009     {
1010         b_fullscreen = true;
1011         i_hide_timeout = i_timeout;
1012         var_AddCallback( p_vout, "mouse-moved",
1013                 FullscreenControllerWidgetMouseMoved, this );
1014     }
1015     /* Quitting fullscreen, unregistering callback */
1016     else if( !b_fs && b_fullscreen )
1017     {
1018         b_fullscreen = false;
1019         i_hide_timeout = i_timeout;
1020         var_DelCallback( p_vout, "mouse-moved",
1021                 FullscreenControllerWidgetMouseMoved, this );
1022
1023         /* Force fs hidding */
1024         IMEvent *eHide = new IMEvent( FullscreenControlHide_Type, 0 );
1025         QApplication::postEvent( this, eHide );
1026     }
1027     vlc_mutex_unlock( &lock );
1028 }
1029
1030 /**
1031  * Mouse change callback (show/hide the controller on mouse movement)
1032  */
1033 void FullscreenControllerWidget::mouseChanged( vout_thread_t *p_vout, int i_mousex, int i_mousey )
1034 {
1035     bool b_toShow;
1036
1037     /* FIXME - multiple vout (ie multiple mouse position ?) and thread safety if multiple vout ? */
1038
1039     b_toShow = false;
1040     if( ( i_mouse_last_move_x == -1 || i_mouse_last_move_y == -1 ) ||
1041         ( abs( i_mouse_last_move_x - i_mousex ) > 2 ||
1042           abs( i_mouse_last_move_y - i_mousey ) > 2 ) )
1043     {
1044         i_mouse_last_move_x = i_mousex;
1045         i_mouse_last_move_y = i_mousey;
1046         b_toShow = true;
1047     }
1048
1049     if( b_toShow )
1050     {
1051         /* Show event */
1052         IMEvent *eShow = new IMEvent( FullscreenControlShow_Type, 0 );
1053         QApplication::postEvent( this, eShow );
1054
1055         /* Plan hide event */
1056         IMEvent *eHide = new IMEvent( FullscreenControlPlanHide_Type, 0 );
1057         QApplication::postEvent( this, eHide );
1058     }
1059 }
1060