]> git.sesse.net Git - vlc/blob - modules/gui/qt4/components/controller.cpp
25d5ee638ea5cfbf129160d9c0a13c77a6c56923
[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 QWidget *AbstractController::discFrame()
421 {
422     /** Disc and Menus handling */
423     QWidget *discFrame = new QWidget( 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 QWidget *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( "MainWindow/MainToolbar1",
708             "64;36;37;38;65").toString();
709     parseAndCreate( line1, controlLayout1 );
710
711    /* QString line2 = QString( "%1-2;%2;%3;%4;%5;%6;%6;%7;%8;%9;%6;%10;%11-4")
712         .arg( PLAY_BUTTON )         .arg( WIDGET_SPACER )
713         .arg( PREVIOUS_BUTTON )         .arg( STOP_BUTTON )
714         .arg( NEXT_BUTTON )        .arg( WIDGET_SPACER )
715         .arg( FULLSCREEN_BUTTON )        .arg( PLAYLIST_BUTTON )
716         .arg( EXTENDED_BUTTON )        .arg( WIDGET_SPACER_EXTEND )
717         .arg( VOLUME );
718     msg_Dbg( p_intf, "%s", qtu( line2 )); */
719
720     QHBoxLayout *controlLayout2 = new QHBoxLayout;
721     controlLayout2->setSpacing( 0 );
722     QString line2 = getSettings()->value( "MainWindow/MainToolbar2",
723             "0-2;64;3;1;4;64;7;10;9;65;34-4" ).toString();
724     parseAndCreate( line2, controlLayout2 );
725
726     if( !b_advancedVisible && advControls ) advControls->hide();
727
728     controlLayout->addLayout( controlLayout1 );
729     controlLayout->addLayout( controlLayout2 );
730 }
731
732 ControlsWidget::~ControlsWidget()
733 {}
734
735 void ControlsWidget::toggleAdvanced()
736 {
737     if( !advControls ) return;
738
739     if( !b_advancedVisible )
740     {
741         advControls->show();
742         b_advancedVisible = true;
743     }
744     else
745     {
746         advControls->hide();
747         b_advancedVisible = false;
748     }
749     emit advancedControlsToggled( b_advancedVisible );
750 }
751
752 AdvControlsWidget::AdvControlsWidget( intf_thread_t *_p_i, QWidget *_parent ) :
753                                      AbstractController( _p_i, _parent )
754 {
755     controlLayout = new QHBoxLayout( this );
756     controlLayout->setMargin( 0 );
757     controlLayout->setSpacing( 0 );
758
759     /* QString line = QString( "%1;%2;%3").arg( RECORD_BUTTON )
760         .arg( SNAPSHOT_BUTTON )
761         .arg( ATOB_BUTTON )
762         .arg( FRAME_BUTTON ); */
763
764     QString line = getSettings()->value( "MainWindow/AdvToolbar",
765             "12;11;13;14" ).toString();
766     parseAndCreate( line, controlLayout );
767 }
768
769 InputControlsWidget::InputControlsWidget( intf_thread_t *_p_i, QWidget *_parent ) :
770                                      AbstractController( _p_i, _parent )
771 {
772     controlLayout = new QHBoxLayout( this );
773     controlLayout->setMargin( 0 );
774     controlLayout->setSpacing( 0 );
775
776     /*    QString line = QString( "%1-%2;%3;%4-%2")
777         .arg( SLOWER_BUTTON ).arg( WIDGET_FLAT )
778         .arg( INPUT_SLIDER )
779         .arg( FASTER_BUTTON ); */
780     QString line = getSettings()->value( "MainWindow/InputToolbar",
781                    "5-1;33;6-1" ).toString();
782     parseAndCreate( line, controlLayout );
783 }
784 /**********************************************************************
785  * Fullscrenn control widget
786  **********************************************************************/
787 FullscreenControllerWidget::FullscreenControllerWidget( intf_thread_t *_p_i )
788                            : AbstractController( _p_i )
789 {
790     i_mouse_last_x      = -1;
791     i_mouse_last_y      = -1;
792     b_mouse_over        = false;
793     i_mouse_last_move_x = -1;
794     i_mouse_last_move_y = -1;
795 #if HAVE_TRANSPARENCY
796     b_slow_hide_begin   = false;
797     i_slow_hide_timeout = 1;
798 #endif
799     b_fullscreen        = false;
800     i_hide_timeout      = 1;
801     i_screennumber      = -1;
802
803     vout.clear();
804
805     setWindowFlags( Qt::ToolTip );
806     setMinimumWidth( 600 );
807
808     setFrameShape( QFrame::StyledPanel );
809     setFrameStyle( QFrame::Sunken );
810     setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
811
812     QVBoxLayout *controlLayout2 = new QVBoxLayout( this );
813     controlLayout2->setLayoutMargins( 5, 2, 5, 2, 5 );
814
815     /* First line */
816     InputControlsWidget *inputC = new InputControlsWidget( p_intf, this );
817     controlLayout2->addWidget( inputC );
818
819     /* Second line */
820 /*     QString line = QString( "%1-2;%2;%3;%4;%5;%2;%6;%2;%7;%2;%8;%9;%10-4")
821         .arg( PLAY_BUTTON )
822         .arg( WIDGET_SPACER )
823         .arg( PREVIOUS_BUTTON )
824         .arg( STOP_BUTTON )
825         .arg( NEXT_BUTTON )
826         .arg( MENU_BUTTONS )
827         .arg( TELETEXT_BUTTONS )
828         .arg( DEFULLSCREEN_BUTTON )
829         .arg( WIDGET_SPACER_EXTEND )
830         .arg( TIME_LABEL )
831         .arg( VOLUME ); */
832
833     controlLayout = new QHBoxLayout;
834     QString line = getSettings()->value( "MainWindow/FSCtoolbar",
835             "0-2;64;3;1;4;64;36;64;37;64;8;65;35-4;34" ).toString();
836     parseAndCreate( line, controlLayout );
837     controlLayout2->addLayout( controlLayout );
838
839     /* hiding timer */
840     p_hideTimer = new QTimer( this );
841     CONNECT( p_hideTimer, timeout(), this, hideFSC() );
842     p_hideTimer->setSingleShot( true );
843
844     /* slow hiding timer */
845 #if HAVE_TRANSPARENCY
846     p_slowHideTimer = new QTimer( this );
847     CONNECT( p_slowHideTimer, timeout(), this, slowHideFSC() );
848 #endif
849
850     adjustSize ();  /* need to get real width and height for moving */
851
852 #ifdef WIN32TRICK
853     setWindowOpacity( 0.0 );
854     b_fscHidden = true;
855     adjustSize();
856     show();
857 #endif
858
859     vlc_mutex_init_recursive( &lock );
860
861     CONNECT( THEMIM->getIM(), voutListChanged( vout_thread_t **, int ), this, setVoutList( vout_thread_t **, int ) );
862 }
863
864 FullscreenControllerWidget::~FullscreenControllerWidget()
865 {
866     getSettings()->setValue( "FullScreen/pos", pos() );
867     setVoutList( NULL, 0 );
868     vlc_mutex_destroy( &lock );
869 }
870
871 /**
872  * Show fullscreen controller
873  */
874 void FullscreenControllerWidget::showFSC()
875 {
876     adjustSize();
877     /* center down */
878     int number = QApplication::desktop()->screenNumber( p_intf->p_sys->p_mi );
879     if( number != i_screennumber )
880     {
881         msg_Dbg( p_intf, "Calculation fullscreen controllers center");
882         /* screen has changed, calculate new position */
883         QRect screenRes = QApplication::desktop()->screenGeometry(number);
884         QPoint pos = QPoint( screenRes.x() + (screenRes.width() / 2) - (width() / 2),
885                              screenRes.y() + screenRes.height() - height());
886         move( pos );
887         i_screennumber = number;
888     }
889 #ifdef WIN32TRICK
890     // after quiting and going to fs, we need to call show()
891     if( isHidden() )
892         show();
893     if( b_fscHidden )
894     {
895         b_fscHidden = false;
896         setWindowOpacity( 1.0 );
897     }
898 #else
899     show();
900 #endif
901
902 #if HAVE_TRANSPARENCY
903     setWindowOpacity( DEFAULT_OPACITY );
904 #endif
905 }
906
907 /**
908  * Hide fullscreen controller
909  * FIXME: under windows it have to be done by moving out of screen
910  *        because hide() doesnt work
911  */
912 void FullscreenControllerWidget::hideFSC()
913 {
914 #ifdef WIN32TRICK
915     b_fscHidden = true;
916     setWindowOpacity( 0.0 );    // simulate hidding
917 #else
918     hide();
919 #endif
920 }
921
922 /**
923  * Plane to hide fullscreen controller
924  */
925 void FullscreenControllerWidget::planHideFSC()
926 {
927     vlc_mutex_lock( &lock );
928     int i_timeout = i_hide_timeout;
929     vlc_mutex_unlock( &lock );
930
931     p_hideTimer->start( i_timeout );
932
933 #if HAVE_TRANSPARENCY
934     b_slow_hide_begin = true;
935     i_slow_hide_timeout = i_timeout;
936     p_slowHideTimer->start( i_slow_hide_timeout / 2 );
937 #endif
938 }
939
940 /**
941  * Hidding fullscreen controller slowly
942  * Linux: need composite manager
943  * Windows: it is blinking, so it can be enabled by define TRASPARENCY
944  */
945 void FullscreenControllerWidget::slowHideFSC()
946 {
947 #if HAVE_TRANSPARENCY
948     if( b_slow_hide_begin )
949     {
950         b_slow_hide_begin = false;
951
952         p_slowHideTimer->stop();
953         /* the last part of time divided to 100 pieces */
954         p_slowHideTimer->start( (int)( i_slow_hide_timeout / 2 / ( windowOpacity() * 100 ) ) );
955
956     }
957     else
958     {
959 #ifdef WIN32TRICK
960          if ( windowOpacity() > 0.0 && !b_fscHidden )
961 #else
962          if ( windowOpacity() > 0.0 )
963 #endif
964          {
965              /* we should use 0.01 because of 100 pieces ^^^
966                 but than it cannt be done in time */
967              setWindowOpacity( windowOpacity() - 0.02 );
968          }
969
970          if ( windowOpacity() <= 0.0 )
971              p_slowHideTimer->stop();
972     }
973 #endif
974 }
975
976 /**
977  * event handling
978  * events: show, hide, start timer for hidding
979  */
980 void FullscreenControllerWidget::customEvent( QEvent *event )
981 {
982     bool b_fs;
983
984     switch( event->type() )
985     {
986         case FullscreenControlToggle_Type:
987             vlc_mutex_lock( &lock );
988             b_fs = b_fullscreen;
989             vlc_mutex_unlock( &lock );
990             if( b_fs )
991 #ifdef WIN32TRICK
992                 if( b_fscHidden )
993 #else
994                 if( isHidden() )
995 #endif
996                 {
997                     p_hideTimer->stop();
998                     showFSC();
999                 }
1000                 else
1001                     hideFSC();
1002             break;
1003         case FullscreenControlShow_Type:
1004             vlc_mutex_lock( &lock );
1005             b_fs = b_fullscreen;
1006             vlc_mutex_unlock( &lock );
1007
1008 #ifdef WIN32TRICK
1009             if( b_fs && b_fscHidden )
1010 #else
1011             if( b_fs && !isVisible() )
1012 #endif
1013                 showFSC();
1014             break;
1015         case FullscreenControlHide_Type:
1016             hideFSC();
1017             break;
1018         case FullscreenControlPlanHide_Type:
1019             if( !b_mouse_over ) // Only if the mouse is not over FSC
1020                 planHideFSC();
1021             break;
1022     }
1023 }
1024
1025 /**
1026  * On mouse move
1027  * moving with FSC
1028  */
1029 void FullscreenControllerWidget::mouseMoveEvent( QMouseEvent *event )
1030 {
1031     if ( event->buttons() == Qt::LeftButton )
1032     {
1033         int i_moveX = event->globalX() - i_mouse_last_x;
1034         int i_moveY = event->globalY() - i_mouse_last_y;
1035
1036         move( x() + i_moveX, y() + i_moveY );
1037
1038         i_mouse_last_x = event->globalX();
1039         i_mouse_last_y = event->globalY();
1040     }
1041 }
1042
1043 /**
1044  * On mouse press
1045  * store position of cursor
1046  */
1047 void FullscreenControllerWidget::mousePressEvent( QMouseEvent *event )
1048 {
1049     i_mouse_last_x = event->globalX();
1050     i_mouse_last_y = event->globalY();
1051 }
1052
1053 /**
1054  * On mouse go above FSC
1055  */
1056 void FullscreenControllerWidget::enterEvent( QEvent *event )
1057 {
1058     b_mouse_over = true;
1059
1060     p_hideTimer->stop();
1061 #if HAVE_TRANSPARENCY
1062     p_slowHideTimer->stop();
1063 #endif
1064 }
1065
1066 /**
1067  * On mouse go out from FSC
1068  */
1069 void FullscreenControllerWidget::leaveEvent( QEvent *event )
1070 {
1071     planHideFSC();
1072
1073     b_mouse_over = false;
1074 }
1075
1076 /**
1077  * When you get pressed key, send it to video output
1078  * FIXME: clearing focus by clearFocus() to not getting
1079  * key press events didnt work
1080  */
1081 void FullscreenControllerWidget::keyPressEvent( QKeyEvent *event )
1082 {
1083     int i_vlck = qtEventToVLCKey( event );
1084     if( i_vlck > 0 )
1085     {
1086         var_SetInteger( p_intf->p_libvlc, "key-pressed", i_vlck );
1087         event->accept();
1088     }
1089     else
1090         event->ignore();
1091 }
1092
1093 /* */
1094 static int FullscreenControllerWidgetFullscreenChanged( vlc_object_t *vlc_object,
1095                 const char *variable, vlc_value_t old_val,
1096                 vlc_value_t new_val,  void *data )
1097 {
1098     vout_thread_t *p_vout = (vout_thread_t *) vlc_object;
1099
1100     msg_Dbg( p_vout, "Qt4: Fullscreen state changed" );
1101     FullscreenControllerWidget *p_fs = (FullscreenControllerWidget *)data;
1102
1103     p_fs->fullscreenChanged( p_vout, new_val.b_bool, var_GetInteger( p_vout, "mouse-hide-timeout" ) );
1104
1105     return VLC_SUCCESS;
1106 }
1107 /* */
1108 static int FullscreenControllerWidgetMouseMoved( vlc_object_t *vlc_object, const char *variable,
1109                                                  vlc_value_t old_val, vlc_value_t new_val,
1110                                                  void *data )
1111 {
1112     vout_thread_t *p_vout = (vout_thread_t *)vlc_object;
1113     FullscreenControllerWidget *p_fs = (FullscreenControllerWidget *)data;
1114
1115     /* Get the value from the Vout - Trust the vout more than Qt */
1116     const int i_mousex = var_GetInteger( p_vout, "mouse-x" );
1117     const int i_mousey = var_GetInteger( p_vout, "mouse-y" );
1118
1119     p_fs->mouseChanged( p_vout, i_mousex, i_mousey );
1120
1121     return VLC_SUCCESS;
1122 }
1123
1124 /**
1125  * It is call to update the list of vout handled by the fullscreen controller
1126  */
1127 void FullscreenControllerWidget::setVoutList( vout_thread_t **pp_vout, int i_vout )
1128 {
1129     QList<vout_thread_t*> del;
1130     QList<vout_thread_t*> add;
1131
1132     QList<vout_thread_t*> set;
1133
1134     /* */
1135     for( int i = 0; i < i_vout; i++ )
1136         set += pp_vout[i];
1137
1138     /* Vout to remove */
1139     vlc_mutex_lock( &lock );
1140     foreach( vout_thread_t *p_vout, vout )
1141     {
1142         if( !set.contains( p_vout ) )
1143             del += p_vout;
1144     }
1145     vlc_mutex_unlock( &lock );
1146
1147     foreach( vout_thread_t *p_vout, del )
1148     {
1149         var_DelCallback( p_vout, "fullscreen",
1150                          FullscreenControllerWidgetFullscreenChanged, this );
1151         vlc_mutex_lock( &lock );
1152         fullscreenChanged( p_vout, false, 0 );
1153         vout.removeAll( p_vout );
1154         vlc_mutex_unlock( &lock );
1155     }
1156
1157     /* Vout to track */
1158     vlc_mutex_lock( &lock );
1159     foreach( vout_thread_t *p_vout, set )
1160     {
1161         if( !vout.contains( p_vout ) )
1162             add += p_vout;
1163     }
1164     vlc_mutex_unlock( &lock );
1165
1166     foreach( vout_thread_t *p_vout, add )
1167     {
1168         vlc_object_hold( (vlc_object_t*)p_vout );
1169
1170         vlc_mutex_lock( &lock );
1171         vout.append( p_vout );
1172         var_AddCallback( p_vout, "fullscreen",
1173                          FullscreenControllerWidgetFullscreenChanged, this );
1174         /* I miss a add and fire */
1175         fullscreenChanged( p_vout, var_GetBool( p_vout, "fullscreen" ),
1176                            var_GetInteger( p_vout, "mouse-hide-timeout" ) );
1177         vlc_mutex_unlock( &lock );
1178     }
1179 }
1180 /**
1181  * Register and unregister callback for mouse moving
1182  */
1183 void FullscreenControllerWidget::fullscreenChanged( vout_thread_t *p_vout,
1184         bool b_fs, int i_timeout )
1185 {
1186     /* FIXME - multiple vout (ie multiple mouse position ?) and thread safety if multiple vout ? */
1187     msg_Dbg( p_vout, "Qt: Entering Fullscreen" );
1188
1189     vlc_mutex_lock( &lock );
1190     /* Entering fullscreen, register callback */
1191     if( b_fs && !b_fullscreen )
1192     {
1193         b_fullscreen = true;
1194         i_hide_timeout = i_timeout;
1195         var_AddCallback( p_vout, "mouse-moved",
1196                 FullscreenControllerWidgetMouseMoved, this );
1197     }
1198     /* Quitting fullscreen, unregistering callback */
1199     else if( !b_fs && b_fullscreen )
1200     {
1201         b_fullscreen = false;
1202         i_hide_timeout = i_timeout;
1203         var_DelCallback( p_vout, "mouse-moved",
1204                 FullscreenControllerWidgetMouseMoved, this );
1205
1206         /* Force fs hidding */
1207         IMEvent *eHide = new IMEvent( FullscreenControlHide_Type, 0 );
1208         QApplication::postEvent( this, static_cast<QEvent *>(eHide) );
1209     }
1210     vlc_mutex_unlock( &lock );
1211 }
1212 /**
1213  * Mouse change callback (show/hide the controller on mouse movement)
1214  */
1215 void FullscreenControllerWidget::mouseChanged( vout_thread_t *p_vout, int i_mousex, int i_mousey )
1216 {
1217     bool b_toShow;
1218
1219     /* FIXME - multiple vout (ie multiple mouse position ?) and thread safety if multiple vout ? */
1220
1221     b_toShow = false;
1222     if( ( i_mouse_last_move_x == -1 || i_mouse_last_move_y == -1 ) ||
1223         ( abs( i_mouse_last_move_x - i_mousex ) > 2 ||
1224           abs( i_mouse_last_move_y - i_mousey ) > 2 ) )
1225     {
1226         i_mouse_last_move_x = i_mousex;
1227         i_mouse_last_move_y = i_mousey;
1228         b_toShow = true;
1229     }
1230
1231     if( b_toShow )
1232     {
1233         /* Show event */
1234         IMEvent *eShow = new IMEvent( FullscreenControlShow_Type, 0 );
1235         QApplication::postEvent( this, static_cast<QEvent *>(eShow) );
1236
1237         /* Plan hide event */
1238         IMEvent *eHide = new IMEvent( FullscreenControlPlanHide_Type, 0 );
1239         QApplication::postEvent( this, static_cast<QEvent *>(eHide) );
1240     }
1241 }
1242