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