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