]> git.sesse.net Git - vlc/blob - modules/gui/qt4/components/interface_widgets.cpp
Qt4 - A to B Loop implementation.
[vlc] / modules / gui / qt4 / components / interface_widgets.cpp
1 /*****************************************************************************
2  * interface_widgets.cpp : Custom widgets for the main interface
3  ****************************************************************************
4  * Copyright ( C ) 2006 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  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * ( at your option ) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 #include "dialogs_provider.hpp"
27 #include "components/interface_widgets.hpp"
28 #include "main_interface.hpp"
29 #include "input_manager.hpp"
30 #include "menus.hpp"
31 #include "util/input_slider.hpp"
32 #include <vlc_vout.h>
33
34 #include <QLabel>
35 #include <QSpacerItem>
36 #include <QCursor>
37 #include <QPushButton>
38 #include <QHBoxLayout>
39 #include <QMenu>
40 #include <QPalette>
41 #include <QResizeEvent>
42
43 #define ICON_SIZE 128
44 #define MAX_BG_SIZE 300
45
46 /**********************************************************************
47  * Video Widget. A simple frame on which video is drawn
48  * This class handles resize issues
49  **********************************************************************/
50 static void *DoRequest( intf_thread_t *, vout_thread_t *, int*,int*,
51                         unsigned int *, unsigned int * );
52 static void DoRelease( intf_thread_t *, void * );
53 static int DoControl( intf_thread_t *, void *, int, va_list );
54
55 VideoWidget::VideoWidget( intf_thread_t *_p_i ) : QFrame( NULL ), p_intf( _p_i )
56 {
57     vlc_mutex_init( p_intf, &lock );
58     p_vout = NULL;
59     CONNECT( this, askResize(), this, SetMinSize() );
60     CONNECT( this, askVideoToShow(), this, show() );
61     setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );
62 }
63
64 VideoWidget::~VideoWidget()
65 {
66     vlc_mutex_lock( &lock );
67     if( p_vout )
68     {
69         if( !p_intf->psz_switch_intf )
70         {
71             if( vout_Control( p_vout, VOUT_CLOSE ) != VLC_SUCCESS )
72                 vout_Control( p_vout, VOUT_REPARENT );
73         }
74         else
75         {
76             if( vout_Control( p_vout, VOUT_REPARENT ) != VLC_SUCCESS )
77                 vout_Control( p_vout, VOUT_CLOSE );
78         }
79     }
80     vlc_mutex_unlock( &lock );
81     vlc_mutex_destroy( &lock );
82 }
83
84 QSize VideoWidget::sizeHint() const
85 {
86     return widgetSize;
87 }
88
89 void *VideoWidget::request( vout_thread_t *p_nvout, int *pi_x, int *pi_y,
90                            unsigned int *pi_width, unsigned int *pi_height )
91 {
92     emit askVideoToShow();
93     if( p_vout )
94     {
95         msg_Dbg( p_intf, "embedded video already in use" );
96         return NULL;
97     }
98     p_vout = p_nvout;
99     emit askResize();
100     return ( void* )winId();
101 }
102
103 void VideoWidget::SetMinSize()
104 {
105     setMinimumSize( 16, 16 );
106 }
107
108 void VideoWidget::release( void *p_win )
109 {
110     p_vout = NULL;
111 }
112 /**********************************************************************
113  * Background Widget. Show a simple image background. Currently,
114  * it's a static cone.
115  **********************************************************************/
116 BackgroundWidget::BackgroundWidget( intf_thread_t *_p_i ) :
117                                         QFrame( NULL ), p_intf( _p_i )
118 {
119
120     setAutoFillBackground( true );
121     plt =  palette();
122     plt.setColor( QPalette::Active, QPalette::Window , Qt::black );
123     plt.setColor( QPalette::Inactive, QPalette::Window , Qt::black );
124     setPalette( plt );
125
126     label = new QLabel;
127     label->setMaximumHeight( ICON_SIZE );
128     label->setMaximumWidth( ICON_SIZE );
129     label->setScaledContents( true );
130     label->setPixmap( QPixmap( ":/vlc128.png" ) );
131     backgroundLayout = new QHBoxLayout;
132     backgroundLayout->addWidget( label );
133     setLayout( backgroundLayout );
134 }
135
136 BackgroundWidget::~BackgroundWidget()
137 {
138     backgroundLayout->takeAt( 0 );
139     delete backgroundLayout;
140 }
141
142 void BackgroundWidget::setArt( QString url )
143 {
144     if( url.isNull() )
145         label->setPixmap( QPixmap( ":/vlc128.png" ) );
146     else
147         label->setPixmap( QPixmap( url ) );
148 }
149
150 QSize BackgroundWidget::sizeHint() const
151 {
152     return widgetSize;
153 }
154
155 void BackgroundWidget::resizeEvent( QResizeEvent *e )
156 {
157     if( e->size().height() < ICON_SIZE -1 )
158         label->setMaximumWidth( e->size().height() );
159     else
160         label->setMaximumWidth( ICON_SIZE );
161 }
162
163 void BackgroundWidget::contextMenuEvent( QContextMenuEvent *event )
164 {
165     QVLCMenu::PopupMenu( p_intf, true );
166 }
167 /**********************************************************************
168  * Visualization selector panel
169  **********************************************************************/
170 VisualSelector::VisualSelector( intf_thread_t *_p_i ) :
171                                                 QFrame( NULL ), p_intf( _p_i )
172 {
173     QHBoxLayout *layout = new QHBoxLayout( this );
174     layout->setMargin( 0 );
175     QPushButton *prevButton = new QPushButton( "Prev" );
176     QPushButton *nextButton = new QPushButton( "Next" );
177     layout->addWidget( prevButton );
178     layout->addWidget( nextButton );
179
180     layout->addItem( new QSpacerItem( 40,20,
181                               QSizePolicy::Expanding, QSizePolicy::Minimum ) );
182     layout->addWidget( new QLabel( qtr( "Current visualization:" ) ) );
183
184     current = new QLabel( qtr( "None" ) );
185     layout->addWidget( current );
186
187     BUTTONACT( prevButton, prev() );
188     BUTTONACT( nextButton, next() );
189
190     setLayout( layout );
191     setMaximumHeight( 35 );
192 }
193
194 VisualSelector::~VisualSelector()
195 {
196 }
197
198 void VisualSelector::prev()
199 {
200     char *psz_new = aout_VisualPrev( p_intf );
201     if( psz_new )
202     {
203         current->setText( qfu( psz_new ) );
204         free( psz_new );
205     }
206 }
207
208 void VisualSelector::next()
209 {
210     char *psz_new = aout_VisualNext( p_intf );
211     if( psz_new )
212     {
213         current->setText( qfu( psz_new ) );
214         free( psz_new );
215     }
216 }
217
218 /**********************************************************************
219  * TEH controls
220  **********************************************************************/
221
222 #define setupSmallButton( aButton ){  \
223     aButton->setMaximumSize( QSize( 26, 26 ) ); \
224     aButton->setMinimumSize( QSize( 26, 26 ) ); \
225     aButton->setIconSize( QSize( 20, 20 ) ); }
226
227 AdvControlsWidget::AdvControlsWidget( intf_thread_t *_p_i ) :
228                                            QFrame( NULL ), p_intf( _p_i )
229 {
230     QHBoxLayout *advLayout = new QHBoxLayout( this );
231     advLayout->setMargin( 0 );
232     advLayout->setSpacing( 0 );
233
234 /* FIXME A to B function */
235     ABButton = new QPushButton( "AB" );
236     ABButton->setMaximumSize( QSize( 26, 26 ) );
237     ABButton->setIconSize( QSize( 20, 20 ) );
238     advLayout->addWidget( ABButton );
239     BUTTON_SET_ACT( ABButton, "AB", qtr( "A to B" ), fromAtoB() );
240     timeA = 0;
241     timeB = 0;
242     CONNECT( THEMIM->getIM(), positionUpdated( float, int, int ),
243              this, AtoBLoop( float, int, int ) );
244
245 //FIXME Frame by frame function
246     frameButton = new QPushButton( "Fr" );
247     frameButton->setMaximumSize( QSize( 26, 26 ) );
248     frameButton->setIconSize( QSize( 20, 20 ) );
249     advLayout->addWidget( frameButton );
250     BUTTON_SET_ACT( frameButton, "Fr", qtr( "Frame by Frame" ), frame() );
251
252 /* FIXME Record function */
253     recordButton = new QPushButton( "R" );
254     recordButton->setMaximumSize( QSize( 26, 26 ) );
255     recordButton->setIconSize( QSize( 20, 20 ) );
256     advLayout->addWidget( recordButton );
257     BUTTON_SET_ACT_I( recordButton, "", record_16px.png,
258             qtr( "Record" ), record() );
259
260     snapshotButton = new QPushButton( "S" );
261     snapshotButton->setMaximumSize( QSize( 26, 26 ) );
262     snapshotButton->setIconSize( QSize( 20, 20 ) );
263     advLayout->addWidget( snapshotButton );
264     BUTTON_SET_ACT( snapshotButton, "S", qtr( "Take a snapshot" ), snapshot() );
265
266 }
267
268 AdvControlsWidget::~AdvControlsWidget()
269 {
270 }
271
272 void AdvControlsWidget::enableInput( bool enable )
273 {
274     ABButton->setEnabled( enable );
275     recordButton->setEnabled( enable );
276 }
277 void AdvControlsWidget::enableVideo( bool enable )
278 {
279     snapshotButton->setEnabled( enable );
280     frameButton->setEnabled( enable );
281 }
282
283 void AdvControlsWidget::snapshot()
284 {
285     vout_thread_t *p_vout = (vout_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_VOUT, FIND_ANYWHERE );
286     if( p_vout ) vout_Control( p_vout, VOUT_SNAPSHOT );
287 }
288
289 void AdvControlsWidget::frame()
290 {
291 }
292
293 void AdvControlsWidget::fromAtoB()
294 {
295     if( !timeA )
296     {
297         timeA = var_GetTime( THEMIM->getInput(), "time"  );
298         ABButton->setText( "A->..." );
299         return;
300     }
301     if( !timeB )
302     {
303         timeB = var_GetTime( THEMIM->getInput(), "time"  );
304         var_SetTime( THEMIM->getInput(), "time" , timeA );
305         ABButton->setText( "A<=>B" );
306         return;
307     }
308     timeA = 0;
309     timeB = 0;
310     ABButton->setText( "AB" );
311 }
312    
313 void AdvControlsWidget::record(){}
314
315 void AdvControlsWidget::AtoBLoop( float f_pos, int i_time, int i_length )
316 {
317     if( timeB )
318     {
319         if( i_time == (int)(timeB/1000000) )
320             var_SetTime( THEMIM->getInput(), "time" , timeA );
321     }
322 }
323 /*****************************
324  * DA Control Widget !
325  *****************************/
326 ControlsWidget::ControlsWidget( intf_thread_t *_p_i, bool b_advControls ) :
327                              QFrame( NULL ), p_intf( _p_i )
328 {
329     controlLayout = new QGridLayout( this );
330     controlLayout->setSpacing( 0 );
331     setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Minimum );
332
333     /** The main Slider **/
334     slider = new InputSlider( Qt::Horizontal, NULL );
335     controlLayout->addWidget( slider, 0, 1, 1, 16 );
336     /* Update the position when the IM has changed */
337     CONNECT( THEMIM->getIM(), positionUpdated( float, int, int ),
338              slider, setPosition( float,int, int ) );
339     /* And update the IM, when the position has changed */
340     CONNECT( slider, sliderDragged( float ),
341              THEMIM->getIM(), sliderUpdate( float ) );
342
343     /** Slower and faster Buttons **/
344     slowerButton = new QPushButton;
345     slowerButton->setFlat( true );
346     slowerButton->setMaximumSize( QSize( 26, 20 ) );
347
348     BUTTON_SET_ACT( slowerButton, "-", qtr( "Slower" ), slower() );
349     controlLayout->addWidget( slowerButton, 0, 0 );
350
351     fasterButton = new QPushButton;
352     fasterButton->setFlat( true );
353     fasterButton->setMaximumSize( QSize( 26, 20 ) );
354
355     BUTTON_SET_ACT( fasterButton, "+", qtr( "Faster" ), faster() );
356     controlLayout->addWidget( fasterButton, 0, 17 );
357
358     /* advanced Controls handling */
359     b_advancedVisible = b_advControls;
360
361     advControls = new AdvControlsWidget( p_intf );
362     controlLayout->addWidget( advControls, 1, 3, 2, 4, Qt::AlignBottom );
363     if( !b_advancedVisible ) advControls->hide();
364
365     /** Disc and Menus handling */
366     discFrame = new QFrame( this );
367
368     QHBoxLayout *discLayout = new QHBoxLayout( discFrame );
369     discLayout->setSpacing( 0 );
370     discLayout->setMargin( 0 );
371
372     prevSectionButton = new QPushButton( discFrame );
373     setupSmallButton( prevSectionButton );
374     discLayout->addWidget( prevSectionButton );
375
376     menuButton = new QPushButton( discFrame );
377     setupSmallButton( menuButton );
378     discLayout->addWidget( menuButton );
379
380     nextSectionButton = new QPushButton( discFrame );
381     setupSmallButton( nextSectionButton );
382     discLayout->addWidget( nextSectionButton );
383
384     controlLayout->addWidget( discFrame, 1, 10, 2, 3, Qt::AlignBottom );
385
386     BUTTON_SET_IMG( prevSectionButton, "", previous.png, "" );
387     BUTTON_SET_IMG( nextSectionButton, "", next.png, "" );
388     BUTTON_SET_IMG( menuButton, "", previous.png, "" );
389
390     discFrame->hide();
391
392     /* Change the navigation button display when the IM navigation changes */
393     CONNECT( THEMIM->getIM(), navigationChanged( int ),
394              this, setNavigation( int ) );
395     /* Changes the IM navigation when triggered on the nav buttons */
396     CONNECT( prevSectionButton, clicked(), THEMIM->getIM(),
397              sectionPrev() );
398     CONNECT( nextSectionButton, clicked(), THEMIM->getIM(),
399              sectionNext() );
400     CONNECT( menuButton, clicked(), THEMIM->getIM(),
401              sectionMenu() );
402
403     /** TODO
404      * Telextext QFrame
405      * Merge with upper menu in a StackLayout
406      **/
407
408     /** Play Buttons **/
409     QSizePolicy sizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
410     sizePolicy.setHorizontalStretch( 0 );
411     sizePolicy.setVerticalStretch( 0 );
412
413     /* Play */
414     playButton = new QPushButton;
415     playButton->setSizePolicy( sizePolicy );
416     playButton->setMaximumSize( QSize( 38, 38 ) );
417     playButton->setMinimumSize( QSize( 45, 45 ) );
418     playButton->setIconSize( QSize( 30, 30 ) );
419
420     controlLayout->addWidget( playButton, 2, 0, 2, 2 );
421
422     controlLayout->setColumnMinimumWidth( 2, 20 );
423     controlLayout->setColumnStretch( 2, 0 );
424
425     /** Prev + Stop + Next Block **/
426     QHBoxLayout *controlButLayout = new QHBoxLayout;
427     controlButLayout->setSpacing( 0 ); /* Don't remove that, will be useful */
428
429     /* Prev */
430     QPushButton *prevButton = new QPushButton;
431     prevButton->setSizePolicy( sizePolicy );
432     setupSmallButton( prevButton );
433
434     controlButLayout->addWidget( prevButton );
435
436     /* Stop */
437     QPushButton *stopButton = new QPushButton;
438     stopButton->setSizePolicy( sizePolicy );
439     setupSmallButton( stopButton );
440
441     controlButLayout->addWidget( stopButton );
442
443     /* next */
444     QPushButton *nextButton = new QPushButton;
445     nextButton->setSizePolicy( sizePolicy );
446     setupSmallButton( nextButton );
447
448     controlButLayout->addWidget( nextButton );
449
450     /* Add this block to the main layout */
451     controlLayout->addLayout( controlButLayout, 3, 3, 1, 3 );
452
453     BUTTON_SET_ACT_I( playButton, "", play.png, qtr( "Play" ), play() );
454     BUTTON_SET_ACT_I( prevButton, "" , previous.png,
455                       qtr( "Previous" ), prev() );
456     BUTTON_SET_ACT_I( nextButton, "", next.png, qtr( "Next" ), next() );
457     BUTTON_SET_ACT_I( stopButton, "", stop.png, qtr( "Stop" ), stop() );
458
459     controlLayout->setColumnStretch( 7 , 2 );
460
461     /*
462      * Other first Line buttons
463      * Might need to be inside a frame to avoid a few resizing pb
464      * FIXME
465      */
466     /** Fullscreen/Visualisation **/
467     fullscreenButton = new QPushButton( "F" );
468     BUTTON_SET_ACT( fullscreenButton, "F", qtr( "Fullscreen" ), fullscreen() );
469     setupSmallButton( fullscreenButton );
470     controlLayout->addWidget( fullscreenButton, 3, 10 );
471
472     /** Playlist Button **/
473     playlistButton = new QPushButton;
474     setupSmallButton( playlistButton );
475     controlLayout->addWidget( playlistButton, 3, 11 );
476     BUTTON_SET_IMG( playlistButton, "" , playlist.png, qtr( "Show playlist" ) );
477
478     /** extended Settings **/
479     QPushButton *extSettingsButton = new QPushButton( "F" );
480     BUTTON_SET_ACT( extSettingsButton, "Ex", qtr( "Extended Settings" ),
481             extSettings() );
482     setupSmallButton( extSettingsButton );
483     controlLayout->addWidget( extSettingsButton, 3, 12 );
484
485     controlLayout->setColumnStretch( 14, 5 );
486
487     /* Volume */
488     VolumeClickHandler *h = new VolumeClickHandler( p_intf, this );
489
490     volMuteLabel = new QLabel;
491     volMuteLabel->setPixmap( QPixmap( ":/pixmaps/volume-high.png" ) );
492     volMuteLabel->setToolTip( qtr( "Mute" ) );
493     volMuteLabel->installEventFilter( h );
494
495     /** TODO:
496      * Change this slider to use a nice Amarok-like one
497      * **/
498     /** FIXME
499      *  THis percerntage thing has to be handled correctly
500      *  This has to match to the OSD
501      **/
502     volumeSlider = new QSlider;
503     volumeSlider->setSizePolicy( sizePolicy );
504     volumeSlider->setMaximumSize( QSize( 80, 200 ) );
505     volumeSlider->setOrientation( Qt::Horizontal );
506
507     volumeSlider->setMaximum( VOLUME_MAX );
508     volumeSlider->setFocusPolicy( Qt::NoFocus );
509     controlLayout->addWidget( volMuteLabel, 3, 15 );
510     controlLayout->addWidget( volumeSlider, 3, 16, 1, 2 );
511
512     /* Volume control connection */
513     CONNECT( volumeSlider, valueChanged( int ), this, updateVolume( int ) );
514     msg_Dbg( p_intf, "size: %i - %i", size().height(), size().width() );
515 }
516 ControlsWidget::~ControlsWidget()
517 {
518 }
519 void ControlsWidget::stop()
520 {
521     THEMIM->stop();
522 }
523
524 void ControlsWidget::play()
525 {
526     if( THEPL->current.i_size == 0 )
527     {
528         /* The playlist is empty, open a file requester */
529         THEDP->openFileDialog();
530         setStatus( 0 );
531         return;
532     }
533     THEMIM->togglePlayPause();
534 }
535
536 void ControlsWidget::prev()
537 {
538     THEMIM->prev();
539 }
540
541 void ControlsWidget::next()
542 {
543     THEMIM->next();
544 }
545
546 void ControlsWidget::setNavigation( int navigation )
547 {
548 #define HELP_MENU N_( "Menu" )
549 #define HELP_PCH N_( "Previous chapter" )
550 #define HELP_NCH N_( "Next chapter" )
551 #define HELP_PTR N_( "Previous track" )
552 #define HELP_NTR N_( "Next track" )
553
554     // 1 = chapter, 2 = title, 0 = no
555     if( navigation == 0 )
556     {
557         discFrame->hide();
558     } else if( navigation == 1 ) {
559         prevSectionButton->setToolTip( qfu( HELP_PCH ) );
560         nextSectionButton->setToolTip( qfu( HELP_NCH ) );
561         menuButton->show();
562         discFrame->show();
563     } else {
564         prevSectionButton->setToolTip( qfu( HELP_PCH ) );
565         nextSectionButton->setToolTip( qfu( HELP_NCH ) );
566         menuButton->hide();
567         discFrame->show();
568     }
569 }
570
571 static bool b_my_volume;
572 void ControlsWidget::updateVolume( int i_sliderVolume )
573 {
574     if( !b_my_volume )
575     {
576         int i_res = i_sliderVolume  * (AOUT_VOLUME_MAX / 2) / VOLUME_MAX;
577         aout_VolumeSet( p_intf, i_res );
578     }
579     if( i_sliderVolume == 0 )
580         volMuteLabel->setPixmap( QPixmap(":/pixmaps/volume-muted.png" ) );
581     else if( i_sliderVolume < VOLUME_MAX / 2 )
582         volMuteLabel->setPixmap( QPixmap( ":/pixmaps/volume-low.png" ) );
583     else volMuteLabel->setPixmap( QPixmap( ":/pixmaps/volume-high.png" ) );
584 }
585
586 void ControlsWidget::updateOnTimer()
587 {
588     /* Audio part */
589     audio_volume_t i_volume;
590     aout_VolumeGet( p_intf, &i_volume );
591     i_volume = ( i_volume *  VOLUME_MAX )/ (AOUT_VOLUME_MAX/2) ;
592     int i_gauge = volumeSlider->value();
593     b_my_volume = false;
594     if( i_volume - i_gauge > 1 || i_gauge - i_volume > 1 )
595     {
596         b_my_volume = true;
597         volumeSlider->setValue( i_volume );
598         b_my_volume = false;
599     }
600
601     /* Activate the interface buttons according to the presence of the input */
602     enableInput( THEMIM->getIM()->hasInput() );
603     //enableVideo( THEMIM->getIM()->hasVideo() );
604     enableVideo( true );
605 }
606
607 void ControlsWidget::setStatus( int status )
608 {
609     if( status == PLAYING_S ) // Playing
610         playButton->setIcon( QIcon( ":/pixmaps/pause.png" ) );
611     else
612         playButton->setIcon( QIcon( ":/pixmaps/play.png" ) );
613 }
614
615 /**
616  * TODO
617  * This functions toggle the fullscreen mode
618  * If there is no video, it should first activate Visualisations...
619  *  This has also to be fixed in enableVideo()
620  */
621 void ControlsWidget::fullscreen()
622 {
623     vout_thread_t *p_vout = (vout_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_VOUT, FIND_ANYWHERE );
624     if( p_vout)
625     {
626         var_SetBool( p_vout, "fullscreen", !var_GetBool( p_vout, "fullscreen" ) );
627         vlc_object_release( p_vout );
628     }
629 }
630
631 void ControlsWidget::extSettings()
632 {
633     THEDP->extendedDialog();
634 }
635
636 void ControlsWidget::slower()
637 {
638     THEMIM->getIM()->slower();
639 }
640
641 void ControlsWidget::faster()
642 {
643     THEMIM->getIM()->faster();
644 }
645
646 void ControlsWidget::enableInput( bool enable )
647 {
648     slowerButton->setEnabled( enable );
649     slider->setEnabled( enable );
650     fasterButton->setEnabled( enable );
651
652     /* Advanced Buttons too */
653     advControls->enableInput( enable );
654 }
655
656 void ControlsWidget::enableVideo( bool enable )
657 {
658     // TODO Later make the fullscreenButton toggle Visualisation and so on.
659     fullscreenButton->setEnabled( enable );
660
661     /* Advanced Buttons too */
662     advControls->enableVideo( enable );
663 }
664
665 void ControlsWidget::toggleAdvanced()
666 {
667     if( !VISIBLE( advControls ) )
668     {
669         advControls->show();
670         b_advancedVisible = true;
671     }
672     else
673     {
674         advControls->hide();
675         b_advancedVisible = false;
676     }
677     //FIXME connect this one :D
678     emit advancedControlsToggled( b_advancedVisible );  //  doComponentsUpdate();
679 }
680
681 /**********************************************************************
682  * Playlist Widget. The embedded playlist
683  **********************************************************************/
684 #include "components/playlist/panels.hpp"
685 #include "components/playlist/selector.hpp"
686
687 PlaylistWidget::PlaylistWidget( intf_thread_t *_p_i ) :
688                                 p_intf ( _p_i )
689 {
690     /* Left Part and design */
691     QWidget *leftW = new QWidget( this );
692     QVBoxLayout *left = new QVBoxLayout( leftW );
693
694     /* Source Selector */
695     selector = new PLSelector( this, p_intf, THEPL );
696     left->addWidget( selector );
697
698     /* Art label */
699     art = new QLabel( "" );
700     art->setMinimumHeight( 128 );
701     art->setMinimumWidth( 128 );
702     art->setMaximumHeight( 128 );
703     art->setMaximumWidth( 128 );
704     art->setScaledContents( true );
705     art->setPixmap( QPixmap( ":/noart.png" ) );
706     left->addWidget( art );
707
708     /* Initialisation of the playlist */
709     playlist_item_t *p_root = playlist_GetPreferredNode( THEPL,
710                                                 THEPL->p_local_category );
711
712     rightPanel = qobject_cast<PLPanel *>( new StandardPLPanel( this,
713                               p_intf, THEPL, p_root ) );
714
715     /* Connects */
716     CONNECT( selector, activated( int ), rightPanel, setRoot( int ) );
717
718     CONNECT( qobject_cast<StandardPLPanel *>( rightPanel )->model,
719              artSet( QString ) , this, setArt( QString ) );
720     /* Forward removal requests from the selector to the main panel */
721     CONNECT( qobject_cast<PLSelector *>( selector )->model,
722              shouldRemove( int ),
723              qobject_cast<StandardPLPanel *>( rightPanel ), removeItem( int ) );
724
725     connect( selector, SIGNAL( activated( int ) ),
726              this, SIGNAL( rootChanged( int ) ) );
727     emit rootChanged( p_root->i_id );
728
729     /* Add the two sides of the QSplitter */
730     addWidget( leftW );
731     addWidget( rightPanel );
732
733     leftW->setMaximumWidth( 250 );
734     setCollapsible( 1, false );
735
736     QList<int> sizeList;
737     sizeList << 180 << 520 ;
738     setSizes( sizeList );
739 }
740
741 void PlaylistWidget::setArt( QString url )
742 {
743     if( url.isNull() )
744         art->setPixmap( QPixmap( ":/noart.png" ) );
745     else if( prevArt != url )
746     {
747         art->setPixmap( QPixmap( url ) );
748         prevArt = url;
749         emit artSet( url );
750     }
751 }
752
753 PlaylistWidget::~PlaylistWidget()
754 {
755 }
756
757 /**********************************************************************
758  * Speed control widget
759  **********************************************************************/
760 SpeedControlWidget::SpeedControlWidget( intf_thread_t *_p_i ) :
761                              QFrame( NULL ), p_intf( _p_i )
762 {
763     QSizePolicy sizePolicy( QSizePolicy::Maximum, QSizePolicy::Fixed );
764     sizePolicy.setHorizontalStretch( 0 );
765     sizePolicy.setVerticalStretch( 0 );
766
767     speedSlider = new QSlider;
768     speedSlider->setSizePolicy( sizePolicy );
769     speedSlider->setMaximumSize( QSize( 80, 200 ) );
770     speedSlider->setOrientation( Qt::Vertical );
771     speedSlider->setTickPosition( QSlider::TicksRight );
772
773     speedSlider->setRange( -100, 100 );
774     speedSlider->setSingleStep( 10 );
775     speedSlider->setPageStep( 20 );
776     speedSlider->setTickInterval( 20 );
777
778     CONNECT( speedSlider, valueChanged( int ), this, updateRate( int ) );
779
780     normalSpeedButton = new QPushButton( "N" );
781     normalSpeedButton->setMaximumSize( QSize( 26, 20 ) );
782     normalSpeedButton->setFlat( true );
783     normalSpeedButton->setToolTip( qtr( "Revert to normal play speed" ) );
784
785     CONNECT( normalSpeedButton, clicked(), this, resetRate() );
786
787     QVBoxLayout *speedControlLayout = new QVBoxLayout;
788     speedControlLayout->addWidget(speedSlider);
789     speedControlLayout->addWidget(normalSpeedButton);
790     setLayout(speedControlLayout);
791 }
792
793 SpeedControlWidget::~SpeedControlWidget()
794 {
795 }
796
797 #define RATE_SLIDER_MAXIMUM 3.0
798 #define RATE_SLIDER_MINIMUM 0.3
799 #define RATE_SLIDER_LENGTH 100.0
800
801 void SpeedControlWidget::updateControls( int rate )
802 {
803     if( speedSlider->isSliderDown() )
804     {
805         //We don't want to change anything if the user is using the slider
806         return;
807     }
808
809     int sliderValue;
810     double speed = INPUT_RATE_DEFAULT / (double)rate;
811
812     if( rate >= INPUT_RATE_DEFAULT )
813     {
814         if( speed < RATE_SLIDER_MINIMUM )
815         {
816             sliderValue = speedSlider->minimum();
817         }
818         else
819         {
820             sliderValue = (int)( ( speed - 1.0 ) * RATE_SLIDER_LENGTH
821                                         / ( 1.0 - RATE_SLIDER_MAXIMUM ) );
822         }
823     }
824     else
825     {
826         if( speed > RATE_SLIDER_MAXIMUM )
827         {
828             sliderValue = speedSlider->maximum();
829         }
830         else
831         {
832             sliderValue = (int)( ( speed - 1.0 ) * RATE_SLIDER_LENGTH
833                                         / ( RATE_SLIDER_MAXIMUM - 1.0 ) );
834         }
835     }
836
837     //Block signals to avoid feedback loop
838     speedSlider->blockSignals( true );
839     speedSlider->setValue( sliderValue );
840     speedSlider->blockSignals( false );
841 }
842
843 void SpeedControlWidget::updateRate( int sliderValue )
844 {
845     int rate;
846
847     if( sliderValue < 0.0 )
848     {
849         rate = INPUT_RATE_DEFAULT* RATE_SLIDER_LENGTH /
850                 ( sliderValue * ( 1.0 - RATE_SLIDER_MINIMUM ) + RATE_SLIDER_LENGTH ) ;
851     }
852     else
853     {
854         rate = INPUT_RATE_DEFAULT* RATE_SLIDER_LENGTH /
855                 ( sliderValue * ( RATE_SLIDER_MAXIMUM - 1.0 ) + RATE_SLIDER_LENGTH );
856     }
857
858     THEMIM->getIM()->setRate(rate);
859 }
860
861 void SpeedControlWidget::resetRate()
862 {
863     THEMIM->getIM()->setRate(INPUT_RATE_DEFAULT);
864 }