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