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