]> git.sesse.net Git - vlc/blob - modules/gui/qt4/components/controller.cpp
UI: prefix/namespace resources for better maintainability
[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     adjustSize ();  /* need to get real width and height for moving */
708
709 #ifdef WIN32TRICK
710     setWindowOpacity( 0.0 );
711     b_fscHidden = true;
712     adjustSize();
713     show();
714 #endif
715
716     vlc_mutex_init_recursive( &lock );
717
718     CONNECT( THEMIM->getIM(), voutListChanged( vout_thread_t **, int ),
719              this, setVoutList( vout_thread_t **, int ) );
720
721     /* First Move */
722     QPoint pos1 = getSettings()->value( "FullScreen/pos" ).toPoint();
723     int number = QApplication::desktop()->screenNumber( p_intf->p_sys->p_mi );
724     if( QApplication::desktop()->screenGeometry( number ).contains( pos1, true ) )
725     {
726         move( pos1 );
727         i_screennumber = number;
728         screenRes = QApplication::desktop()->screenGeometry(number);
729     }
730     else
731     {
732         centerFSC( number );
733     }
734
735 }
736
737 FullscreenControllerWidget::~FullscreenControllerWidget()
738 {
739     getSettings()->setValue( "FullScreen/pos", pos() );
740     setVoutList( NULL, 0 );
741     vlc_mutex_destroy( &lock );
742 }
743
744 void FullscreenControllerWidget::centerFSC( int number )
745 {
746     screenRes = QApplication::desktop()->screenGeometry(number);
747     /* screen has changed, calculate new position */
748     QPoint pos = QPoint( screenRes.x() + (screenRes.width() / 2) - (width() / 2),
749             screenRes.y() + screenRes.height() - height());
750     move( pos );
751     i_screennumber = number;
752 }
753
754 /**
755  * Show fullscreen controller
756  */
757 void FullscreenControllerWidget::showFSC()
758 {
759     adjustSize();
760
761     int number = QApplication::desktop()->screenNumber( p_intf->p_sys->p_mi );
762
763     if( number != i_screennumber ||
764         screenRes != QApplication::desktop()->screenGeometry(number) )
765     {
766         centerFSC( number );
767     }
768 #ifdef WIN32TRICK
769     // after quiting and going to fs, we need to call show()
770     if( isHidden() )
771         show();
772     if( b_fscHidden )
773     {
774         b_fscHidden = false;
775         setWindowOpacity( 1.0 );
776     }
777 #else
778     show();
779 #endif
780
781 #if HAVE_TRANSPARENCY
782     setWindowOpacity( DEFAULT_OPACITY );
783 #endif
784 }
785
786 /**
787  * Hide fullscreen controller
788  * FIXME: under windows it have to be done by moving out of screen
789  *        because hide() doesnt work
790  */
791 void FullscreenControllerWidget::hideFSC()
792 {
793 #ifdef WIN32TRICK
794     b_fscHidden = true;
795     setWindowOpacity( 0.0 );    // simulate hidding
796 #else
797     hide();
798 #endif
799 }
800
801 /**
802  * Plane to hide fullscreen controller
803  */
804 void FullscreenControllerWidget::planHideFSC()
805 {
806     vlc_mutex_lock( &lock );
807     int i_timeout = i_hide_timeout;
808     vlc_mutex_unlock( &lock );
809
810     p_hideTimer->start( i_timeout );
811
812 #if HAVE_TRANSPARENCY
813     b_slow_hide_begin = true;
814     i_slow_hide_timeout = i_timeout;
815     p_slowHideTimer->start( i_slow_hide_timeout / 2 );
816 #endif
817 }
818
819 /**
820  * Hidding fullscreen controller slowly
821  * Linux: need composite manager
822  * Windows: it is blinking, so it can be enabled by define TRASPARENCY
823  */
824 void FullscreenControllerWidget::slowHideFSC()
825 {
826 #if HAVE_TRANSPARENCY
827     if( b_slow_hide_begin )
828     {
829         b_slow_hide_begin = false;
830
831         p_slowHideTimer->stop();
832         /* the last part of time divided to 100 pieces */
833         p_slowHideTimer->start( (int)( i_slow_hide_timeout / 2 / ( windowOpacity() * 100 ) ) );
834
835     }
836     else
837     {
838 #ifdef WIN32TRICK
839          if ( windowOpacity() > 0.0 && !b_fscHidden )
840 #else
841          if ( windowOpacity() > 0.0 )
842 #endif
843          {
844              /* we should use 0.01 because of 100 pieces ^^^
845                 but than it cannt be done in time */
846              setWindowOpacity( windowOpacity() - 0.02 );
847          }
848
849          if ( windowOpacity() <= 0.0 )
850              p_slowHideTimer->stop();
851     }
852 #endif
853 }
854
855 /**
856  * event handling
857  * events: show, hide, start timer for hidding
858  */
859 void FullscreenControllerWidget::customEvent( QEvent *event )
860 {
861     bool b_fs;
862
863     switch( event->type() )
864     {
865         case FullscreenControlToggle_Type:
866             vlc_mutex_lock( &lock );
867             b_fs = b_fullscreen;
868             vlc_mutex_unlock( &lock );
869             if( b_fs )
870             {
871 #ifdef WIN32TRICK
872                 if( b_fscHidden )
873 #else
874                 if( isHidden() )
875 #endif
876                 {
877                     p_hideTimer->stop();
878                     showFSC();
879                 }
880                 else
881                     hideFSC();
882             }
883             break;
884         case FullscreenControlShow_Type:
885             vlc_mutex_lock( &lock );
886             b_fs = b_fullscreen;
887             vlc_mutex_unlock( &lock );
888
889 #ifdef WIN32TRICK
890             if( b_fs && b_fscHidden )
891 #else
892             if( b_fs && !isVisible() )
893 #endif
894                 showFSC();
895             break;
896         case FullscreenControlHide_Type:
897             hideFSC();
898             break;
899         case FullscreenControlPlanHide_Type:
900             if( !b_mouse_over ) // Only if the mouse is not over FSC
901                 planHideFSC();
902             break;
903         default:
904             break;
905     }
906 }
907
908 /**
909  * On mouse move
910  * moving with FSC
911  */
912 void FullscreenControllerWidget::mouseMoveEvent( QMouseEvent *event )
913 {
914     if( event->buttons() == Qt::LeftButton )
915     {
916         if( i_mouse_last_x == -1 || i_mouse_last_y == -1 )
917             return;
918
919         int i_moveX = event->globalX() - i_mouse_last_x;
920         int i_moveY = event->globalY() - i_mouse_last_y;
921
922         move( x() + i_moveX, y() + i_moveY );
923
924         i_mouse_last_x = event->globalX();
925         i_mouse_last_y = event->globalY();
926     }
927 }
928
929 /**
930  * On mouse press
931  * store position of cursor
932  */
933 void FullscreenControllerWidget::mousePressEvent( QMouseEvent *event )
934 {
935     i_mouse_last_x = event->globalX();
936     i_mouse_last_y = event->globalY();
937 }
938
939 void FullscreenControllerWidget::mouseReleaseEvent( QMouseEvent *event )
940 {
941     i_mouse_last_x = -1;
942     i_mouse_last_y = -1;
943 }
944
945 /**
946  * On mouse go above FSC
947  */
948 void FullscreenControllerWidget::enterEvent( QEvent *event )
949 {
950     b_mouse_over = true;
951
952     p_hideTimer->stop();
953 #if HAVE_TRANSPARENCY
954     p_slowHideTimer->stop();
955 #endif
956     event->accept();
957 }
958
959 /**
960  * On mouse go out from FSC
961  */
962 void FullscreenControllerWidget::leaveEvent( QEvent *event )
963 {
964     planHideFSC();
965
966     b_mouse_over = false;
967     event->accept();
968 }
969
970 /**
971  * When you get pressed key, send it to video output
972  * FIXME: clearing focus by clearFocus() to not getting
973  * key press events didnt work
974  */
975 void FullscreenControllerWidget::keyPressEvent( QKeyEvent *event )
976 {
977     int i_vlck = qtEventToVLCKey( event );
978     if( i_vlck > 0 )
979     {
980         var_SetInteger( p_intf->p_libvlc, "key-pressed", i_vlck );
981         event->accept();
982     }
983     else
984         event->ignore();
985 }
986
987 /* */
988 static int FullscreenControllerWidgetFullscreenChanged( vlc_object_t *vlc_object,
989                 const char *variable, vlc_value_t old_val,
990                 vlc_value_t new_val,  void *data )
991 {
992     vout_thread_t *p_vout = (vout_thread_t *) vlc_object;
993
994     msg_Dbg( p_vout, "Qt4: Fullscreen state changed" );
995     FullscreenControllerWidget *p_fs = (FullscreenControllerWidget *)data;
996
997     p_fs->fullscreenChanged( p_vout, new_val.b_bool, var_GetInteger( p_vout, "mouse-hide-timeout" ) );
998
999     return VLC_SUCCESS;
1000 }
1001 /* */
1002 static int FullscreenControllerWidgetMouseMoved( vlc_object_t *vlc_object, const char *variable,
1003                                                  vlc_value_t old_val, vlc_value_t new_val,
1004                                                  void *data )
1005 {
1006     vout_thread_t *p_vout = (vout_thread_t *)vlc_object;
1007     FullscreenControllerWidget *p_fs = (FullscreenControllerWidget *)data;
1008
1009     /* Get the value from the Vout - Trust the vout more than Qt */
1010     const int i_mousex = var_GetInteger( p_vout, "mouse-x" );
1011     const int i_mousey = var_GetInteger( p_vout, "mouse-y" );
1012
1013     p_fs->mouseChanged( p_vout, i_mousex, i_mousey );
1014
1015     return VLC_SUCCESS;
1016 }
1017
1018 /**
1019  * It is call to update the list of vout handled by the fullscreen controller
1020  */
1021 void FullscreenControllerWidget::setVoutList( vout_thread_t **pp_vout, int i_vout )
1022 {
1023     QList<vout_thread_t*> del;
1024     QList<vout_thread_t*> add;
1025
1026     QList<vout_thread_t*> set;
1027
1028     /* */
1029     for( int i = 0; i < i_vout; i++ )
1030         set += pp_vout[i];
1031
1032     /* Vout to remove */
1033     vlc_mutex_lock( &lock );
1034     foreach( vout_thread_t *p_vout, vout )
1035     {
1036         if( !set.contains( p_vout ) )
1037             del += p_vout;
1038     }
1039     vlc_mutex_unlock( &lock );
1040
1041     foreach( vout_thread_t *p_vout, del )
1042     {
1043         var_DelCallback( p_vout, "fullscreen",
1044                          FullscreenControllerWidgetFullscreenChanged, this );
1045         vlc_mutex_lock( &lock );
1046         fullscreenChanged( p_vout, false, 0 );
1047         vout.removeAll( p_vout );
1048         vlc_mutex_unlock( &lock );
1049
1050         vlc_object_release( VLC_OBJECT(p_vout) );
1051     }
1052
1053     /* Vout to track */
1054     vlc_mutex_lock( &lock );
1055     foreach( vout_thread_t *p_vout, set )
1056     {
1057         if( !vout.contains( p_vout ) )
1058             add += p_vout;
1059     }
1060     vlc_mutex_unlock( &lock );
1061
1062     foreach( vout_thread_t *p_vout, add )
1063     {
1064         vlc_object_hold( VLC_OBJECT(p_vout) );
1065
1066         vlc_mutex_lock( &lock );
1067         vout.append( p_vout );
1068         var_AddCallback( p_vout, "fullscreen",
1069                          FullscreenControllerWidgetFullscreenChanged, this );
1070         /* I miss a add and fire */
1071         fullscreenChanged( p_vout, var_GetBool( p_vout, "fullscreen" ),
1072                            var_GetInteger( p_vout, "mouse-hide-timeout" ) );
1073         vlc_mutex_unlock( &lock );
1074     }
1075 }
1076 /**
1077  * Register and unregister callback for mouse moving
1078  */
1079 void FullscreenControllerWidget::fullscreenChanged( vout_thread_t *p_vout,
1080         bool b_fs, int i_timeout )
1081 {
1082     /* FIXME - multiple vout (ie multiple mouse position ?) and thread safety if multiple vout ? */
1083     msg_Dbg( p_vout, "Qt: Entering Fullscreen" );
1084
1085     vlc_mutex_lock( &lock );
1086     /* Entering fullscreen, register callback */
1087     if( b_fs && !b_fullscreen )
1088     {
1089         b_fullscreen = true;
1090         i_hide_timeout = i_timeout;
1091         var_AddCallback( p_vout, "mouse-moved",
1092                 FullscreenControllerWidgetMouseMoved, this );
1093     }
1094     /* Quitting fullscreen, unregistering callback */
1095     else if( !b_fs && b_fullscreen )
1096     {
1097         b_fullscreen = false;
1098         i_hide_timeout = i_timeout;
1099         var_DelCallback( p_vout, "mouse-moved",
1100                 FullscreenControllerWidgetMouseMoved, this );
1101
1102         /* Force fs hidding */
1103         IMEvent *eHide = new IMEvent( FullscreenControlHide_Type, 0 );
1104         QApplication::postEvent( this, eHide );
1105     }
1106     vlc_mutex_unlock( &lock );
1107 }
1108
1109 /**
1110  * Mouse change callback (show/hide the controller on mouse movement)
1111  */
1112 void FullscreenControllerWidget::mouseChanged( vout_thread_t *p_vout, int i_mousex, int i_mousey )
1113 {
1114     bool b_toShow;
1115
1116     /* FIXME - multiple vout (ie multiple mouse position ?) and thread safety if multiple vout ? */
1117
1118     b_toShow = false;
1119     if( ( i_mouse_last_move_x == -1 || i_mouse_last_move_y == -1 ) ||
1120         ( abs( i_mouse_last_move_x - i_mousex ) > 2 ||
1121           abs( i_mouse_last_move_y - i_mousey ) > 2 ) )
1122     {
1123         i_mouse_last_move_x = i_mousex;
1124         i_mouse_last_move_y = i_mousey;
1125         b_toShow = true;
1126     }
1127
1128     if( b_toShow )
1129     {
1130         /* Show event */
1131         IMEvent *eShow = new IMEvent( FullscreenControlShow_Type, 0 );
1132         QApplication::postEvent( this, eShow );
1133
1134         /* Plan hide event */
1135         IMEvent *eHide = new IMEvent( FullscreenControlPlanHide_Type, 0 );
1136         QApplication::postEvent( this, eHide );
1137     }
1138 }
1139