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