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