]> git.sesse.net Git - vlc/blob - modules/gui/qt4/components/interface_widgets.cpp
Qt4 - Comments about TODO and FIXMEs on the main_interface, for my holidays :D
[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  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * ( at your option ) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #include "dialogs_provider.hpp"
26 #include "qt4.hpp"
27 #include "components/interface_widgets.hpp"
28 #include "main_interface.hpp"
29 #include "input_manager.hpp"
30
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 300
44
45 /**********************************************************************
46  * Video Widget. A simple frame on which video is drawn
47  * This class handles resize issues
48  **********************************************************************/
49 static void *DoRequest( intf_thread_t *, vout_thread_t *, int*,int*,
50                         unsigned int *, unsigned int * );
51 static void DoRelease( intf_thread_t *, void * );
52 static int DoControl( intf_thread_t *, void *, int, va_list );
53
54 VideoWidget::VideoWidget( intf_thread_t *_p_i ) : QFrame( NULL ), p_intf( _p_i )
55 {
56     vlc_mutex_init( p_intf, &lock );
57     p_vout = NULL;
58
59     setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );
60 }
61
62 VideoWidget::~VideoWidget()
63 {
64     vlc_mutex_lock( &lock );
65     if( p_vout )
66     {
67         if( !p_intf->psz_switch_intf )
68         {
69             if( vout_Control( p_vout, VOUT_CLOSE ) != VLC_SUCCESS )
70                 vout_Control( p_vout, VOUT_REPARENT );
71         }
72         else
73         {
74             if( vout_Control( p_vout, VOUT_REPARENT ) != VLC_SUCCESS )
75                 vout_Control( p_vout, VOUT_CLOSE );
76         }
77     }
78     vlc_mutex_unlock( &lock );
79     vlc_mutex_destroy( &lock );
80 }
81
82 QSize VideoWidget::sizeHint() const
83 {
84     return widgetSize;
85 }
86
87 void *VideoWidget::request( vout_thread_t *p_nvout, int *pi_x, int *pi_y,
88                            unsigned int *pi_width, unsigned int *pi_height )
89 {
90     if( p_vout )
91     {
92         msg_Dbg( p_intf, "embedded video already in use" );
93         return NULL;
94     }
95     p_vout = p_nvout;
96     setMinimumSize( 16, 16 );
97     return ( void* )winId();
98 }
99
100 void VideoWidget::release( void *p_win )
101 {
102     p_vout = NULL;
103 }
104 /**********************************************************************
105  * Background Widget. Show a simple image background. Currently,
106  * it's a static cone.
107  **********************************************************************/
108 BackgroundWidget::BackgroundWidget( intf_thread_t *_p_i ) :
109                                         QFrame( NULL ), p_intf( _p_i )
110 {
111
112     setAutoFillBackground( true );
113     plt =  palette();
114     plt.setColor( QPalette::Active, QPalette::Window , Qt::black );
115     plt.setColor( QPalette::Inactive, QPalette::Window , Qt::black );
116     setPalette( plt );
117
118     label = new QLabel( "" );
119     label->setMaximumHeight( ICON_SIZE );
120     label->setMaximumWidth( ICON_SIZE );
121     label->setScaledContents( true );
122     label->setPixmap( QPixmap( ":/vlc128.png" ) );
123     backgroundLayout = new QHBoxLayout;
124     backgroundLayout->addWidget( label );
125     setLayout( backgroundLayout );
126 }
127
128 BackgroundWidget::~BackgroundWidget()
129 {
130     backgroundLayout->takeAt( 0 );
131     delete backgroundLayout;
132 }
133
134 void BackgroundWidget::setArt( QString url )
135 {
136     if( url.isNull() )
137         label->setPixmap( QPixmap( ":/vlc128.png" ) );
138     else
139         label->setPixmap( QPixmap( url ) );
140 }
141
142 QSize BackgroundWidget::sizeHint() const
143 {
144     return widgetSize;
145 }
146
147 void BackgroundWidget::resizeEvent( QResizeEvent *e )
148 {
149     if( e->size().height() < ICON_SIZE -1 )
150         label->setMaximumWidth( e->size().height() );
151     else
152         label->setMaximumWidth( ICON_SIZE );
153 }
154
155 /**********************************************************************
156  * Visualization selector panel
157  **********************************************************************/
158 VisualSelector::VisualSelector( intf_thread_t *_p_i ) :
159                                                 QFrame( NULL ), p_intf( _p_i )
160 {
161     QHBoxLayout *layout = new QHBoxLayout( this );
162     layout->setMargin( 0 );
163     QPushButton *prevButton = new QPushButton( "Prev" );
164     QPushButton *nextButton = new QPushButton( "Next" );
165     layout->addWidget( prevButton );
166     layout->addWidget( nextButton );
167
168     layout->addItem( new QSpacerItem( 40,20,
169                               QSizePolicy::Expanding, QSizePolicy::Minimum ) );
170     layout->addWidget( new QLabel( qtr( "Current visualization:" ) ) );
171
172     current = new QLabel( qtr( "None" ) );
173     layout->addWidget( current );
174
175     BUTTONACT( prevButton, prev() );
176     BUTTONACT( nextButton, next() );
177
178     setLayout( layout );
179     setMaximumHeight( 35 );
180 }
181
182 VisualSelector::~VisualSelector()
183 {
184 }
185
186 void VisualSelector::prev()
187 {
188     char *psz_new = aout_VisualPrev( p_intf );
189     if( psz_new )
190     {
191         current->setText( qfu( psz_new ) );
192         free( psz_new );
193     }
194 }
195
196 void VisualSelector::next()
197 {
198     char *psz_new = aout_VisualNext( p_intf );
199     if( psz_new )
200     {
201         current->setText( qfu( psz_new ) );
202         free( psz_new );
203     }
204 }
205
206 /**********************************************************************
207  * More controls
208  **********************************************************************/
209 AdvControlsWidget::AdvControlsWidget( intf_thread_t *_p_i ) :
210                                            QFrame( NULL ), p_intf( _p_i )
211 {
212     QHBoxLayout *layout = new QHBoxLayout( this );
213     layout->setMargin( 0 );
214
215     normalButton = new QPushButton( "N" );
216     BUTTON_SET_ACT( normalButton, "N", qtr( "Normal rate" ), normal() );
217     layout->addWidget( normalButton );
218     normalButton->setMaximumWidth( 35 );
219
220
221     layout->addItem( new QSpacerItem( 100,20,
222                               QSizePolicy::Expanding, QSizePolicy::Minimum ) );
223
224     snapshotButton = new QPushButton( "S" );
225     BUTTON_SET_ACT( snapshotButton, "S", qtr( "Take a snapshot" ), snapshot() );
226     layout->addWidget( snapshotButton );
227     snapshotButton->setMaximumWidth( 35 );
228 }
229
230 AdvControlsWidget::~AdvControlsWidget()
231 {
232 }
233
234 void AdvControlsWidget::enableInput( bool enable )
235 {
236 //    slowerButton->setEnabled( enable );
237     normalButton->setEnabled( enable );
238 //    fasterButton->setEnabled( enable );
239 }
240 void AdvControlsWidget::enableVideo( bool enable )
241 {
242     snapshotButton->setEnabled( enable );
243     //fullscreenButton->setEnabled( enable );
244 }
245
246 void AdvControlsWidget::normal()
247 {
248     THEMIM->getIM()->normalRate();
249 }
250
251 void AdvControlsWidget::snapshot()
252 {
253 }
254
255 void AdvControlsWidget::fullscreen()
256 {
257 }
258
259 ControlsWidget::ControlsWidget( intf_thread_t *_p_i ) :
260                              QFrame( NULL ), p_intf( _p_i )
261 {
262     //QSize size( 500, 200 );
263     //resize( size );
264     controlLayout = new QGridLayout( this );
265
266     /** The main Slider **/
267     slider = new InputSlider( Qt::Horizontal, NULL );
268     controlLayout->addWidget( slider, 0, 1, 1, 15 );
269     /* Update the position when the IM has changed */
270     CONNECT( THEMIM->getIM(), positionUpdated( float, int, int ),
271              slider, setPosition( float,int, int ) );
272     /* And update the IM, when the position has changed */
273     CONNECT( slider, sliderDragged( float ),
274              THEMIM->getIM(), sliderUpdate( float ) );
275
276     /** Slower and faster Buttons **/
277     slowerButton = new QPushButton( "S" );
278     BUTTON_SET_ACT( slowerButton, "S", qtr( "Slower" ), slower() );
279     controlLayout->addWidget( slowerButton, 0, 0 );
280     slowerButton->setMaximumSize( QSize( 26, 20 ) );
281
282     fasterButton = new QPushButton( "F" );
283     BUTTON_SET_ACT( fasterButton, "F", qtr( "Faster" ), faster() );
284     controlLayout->addWidget( fasterButton, 0, 16 );
285     fasterButton->setMaximumSize( QSize( 26, 20 ) );
286
287     /** TODO: Insert here the AdvControls Widget 
288      * and add - A->B button
289      *         - frame by frame
290      *         - record button
291      * and put the snapshot in the same QFrame 
292      * Then fix all the size issues in main_interface.cpp
293      **/
294     
295     /** Disc and Menus handling */
296     discFrame = new QFrame( this );
297     QHBoxLayout *discLayout = new QHBoxLayout( discFrame );
298
299     QPushButton *menuButton = new QPushButton( discFrame );
300     discLayout->addWidget( menuButton );
301
302     QPushButton *prevSectionButton = new QPushButton( discFrame );
303     discLayout->addWidget( prevSectionButton );
304
305     QPushButton *nextSectionButton = new QPushButton( discFrame );
306     discLayout->addWidget( nextSectionButton );
307
308     controlLayout->addWidget( discFrame, 1, 13, 1, 4 );
309
310     BUTTON_SET_IMG( prevSectionButton, "", previous.png, "" );
311     BUTTON_SET_IMG( nextSectionButton, "", next.png, "" );
312     BUTTON_SET_IMG( menuButton, "", previous.png, "" );
313
314     discFrame->hide();
315
316     /* Change the navigation button display when the IM navigation changes */
317     CONNECT( THEMIM->getIM(), navigationChanged( int ),
318              this, setNavigation( int ) );
319     /* Changes the IM navigation when triggered on the nav buttons */
320     CONNECT( prevSectionButton, clicked(), THEMIM->getIM(),
321              sectionPrev() );
322     CONNECT( nextSectionButton, clicked(), THEMIM->getIM(),
323              sectionNext() );
324     CONNECT( menuButton, clicked(), THEMIM->getIM(),
325              sectionMenu() );
326
327     /** TODO
328      * Telextext QFrame
329      **/
330
331     /** Play Buttons **/
332     QSizePolicy sizePolicy( QSizePolicy::Maximum, QSizePolicy::Fixed );
333     sizePolicy.setHorizontalStretch( 0 );
334     sizePolicy.setVerticalStretch( 0 );
335 //  sizePolicy.setHeightForWidth( playButton->sizePolicy().hasHeightForWidth() );
336
337     /* Play */
338     QPushButton *playButton = new QPushButton;
339     playButton->setSizePolicy( sizePolicy );
340     playButton->setMaximumSize( QSize( 45, 45 ) );
341     playButton->setIconSize( QSize( 30, 30 ) );
342
343     controlLayout->addWidget( playButton, 2, 0, 2, 2 );
344
345     /** Prev + Stop + Next Block **/
346     QHBoxLayout *controlButLayout = new QHBoxLayout;
347     controlButLayout->setSpacing( 0 ); /* Don't remove that, will be useful */
348
349     /* Prev */
350     QPushButton *prevButton = new QPushButton;
351     prevButton->setSizePolicy( sizePolicy );
352     prevButton->setMaximumSize( QSize( 26, 26 ) );
353     prevButton->setIconSize( QSize( 20, 20 ) );
354
355     controlButLayout->addWidget( prevButton );
356
357     /* Stop */
358     QPushButton *stopButton = new QPushButton;
359     stopButton->setSizePolicy( sizePolicy );
360     stopButton->setMaximumSize( QSize( 26, 26 ) );
361     stopButton->setIconSize( QSize( 20, 20 ) );
362
363     controlButLayout->addWidget( stopButton );
364
365     /* next */
366     QPushButton *nextButton = new QPushButton;
367     nextButton->setSizePolicy( sizePolicy );
368     nextButton->setMaximumSize( QSize( 26, 26 ) );
369     nextButton->setIconSize( QSize( 20, 20 ) );
370
371     controlButLayout->addWidget( nextButton );
372
373     /* Add this block to the main layout */
374     controlLayout->addLayout( controlButLayout, 3, 3, 1, 3 );
375
376     BUTTON_SET_ACT_I( playButton, "", play.png, qtr( "Play" ), play() );
377     BUTTON_SET_ACT_I( prevButton, "" , previous.png,
378                       qtr( "Previous" ), prev() );
379     BUTTON_SET_ACT_I( nextButton, "", next.png, qtr( "Next" ), next() );
380     BUTTON_SET_ACT_I( stopButton, "", stop.png, qtr( "Stop" ), stop() );
381
382     /*
383      * Other first Line buttons
384      * Might need to be inside a frame to avoid a few resizing pb
385      * FIXME
386      */
387     /** Fullscreen/Visualisation **/
388     fullscreenButton = new QPushButton( "F" );
389     BUTTON_SET_ACT( fullscreenButton, "F", qtr( "Fullscreen" ), fullscreen() );
390     fullscreenButton->setMaximumSize( QSize( 26, 26 ) );
391     controlLayout->addWidget( fullscreenButton, 3, 10 );
392
393     /** Playlist Button **/
394     playlistButton = new QPushButton;
395     playlistButton->setMaximumSize( QSize( 26, 26 ) );
396     playlistButton->setIconSize( QSize( 20, 20 ) );
397
398     controlLayout->addWidget( playlistButton, 3, 11 );
399
400     /** extended Settings **/
401     QPushButton *extSettingsButton = new QPushButton( "F" );
402     BUTTON_SET_ACT( extSettingsButton, "Ex", qtr( "Extended Settings" ),
403             extSettings() );
404     extSettingsButton->setMaximumSize( QSize( 26, 26 ) );
405     controlLayout->addWidget( extSettingsButton, 3, 12 );
406
407     /** Preferences **/
408     QPushButton *prefsButton = new QPushButton( "P" );
409     BUTTON_SET_ACT( prefsButton, "P", qtr( "Preferences / Settings" ), prefs() );
410     prefsButton->setMaximumSize( QSize( 26, 26 ) );
411     controlLayout->addWidget( prefsButton, 3, 13 );
412
413     /* Volume */
414     VolumeClickHandler *h = new VolumeClickHandler( p_intf, this );
415
416     QLabel *volMuteLabel = new QLabel;
417     volMuteLabel->setPixmap( QPixmap( ":/pixmaps/volume-low.png" ) );
418     volMuteLabel->setToolTip( qtr( "Mute" ) );
419     volMuteLabel->installEventFilter( h );
420
421     /** TODO: 
422      * Change this slider to use a nice Amarok-like one 
423      * Add a Context menu to change to the most useful %
424      * **/
425     /** FIXME
426      *  THis percerntage thing has to be handled correctly
427      *  This has to match to the OSD
428      **/
429     volumeSlider = new QSlider;
430     volumeSlider->setSizePolicy( sizePolicy );
431     volumeSlider->setMaximumSize( QSize( 80, 200 ) );
432     volumeSlider->setOrientation( Qt::Horizontal );
433
434     volumeSlider->setMaximum( 100 );
435     volumeSlider->setFocusPolicy( Qt::NoFocus );
436     controlLayout->addWidget( volMuteLabel, 3, 14 );
437     controlLayout->addWidget( volumeSlider, 3, 15, 1, 2 );
438
439     /* Volume control connection */
440     CONNECT( volumeSlider, valueChanged( int ), this, updateVolume( int ) );
441
442 }
443 ControlsWidget::~ControlsWidget()
444 {
445 }
446 void ControlsWidget::stop()
447 {
448     THEMIM->stop();
449 }
450
451 void ControlsWidget::play()
452 {
453     if( playlist_IsEmpty( THEPL ) )
454     {
455         /* The playlist is empty, open a file requester */
456         THEDP->openFileDialog();
457         setStatus( 0 );
458         return;
459     }
460     THEMIM->togglePlayPause();
461 }
462
463 void ControlsWidget::prev()
464 {
465     THEMIM->prev();
466 }
467
468 void ControlsWidget::next()
469 {
470     THEMIM->next();
471 }
472
473 void ControlsWidget::setNavigation( int navigation )
474 {
475 #define HELP_MENU N_( "Menu" )
476 #define HELP_PCH N_( "Previous chapter" )
477 #define HELP_NCH N_( "Next chapter" )
478 #define HELP_PTR N_( "Previous track" )
479 #define HELP_NTR N_( "Next track" )
480
481     // 1 = chapter, 2 = title, 0 = no
482     if( navigation == 0 )
483     {
484         discFrame->hide();
485     } else if( navigation == 1 ) {
486         prevSectionButton->show();
487         prevSectionButton->setToolTip( qfu( HELP_PCH ) );
488         nextSectionButton->show();
489         nextSectionButton->setToolTip( qfu( HELP_NCH ) );
490         menuButton->show();
491         discFrame->show();
492     } else {
493         prevSectionButton->show();
494         prevSectionButton->setToolTip( qfu( HELP_PCH ) );
495         nextSectionButton->show();
496         nextSectionButton->setToolTip( qfu( HELP_NCH ) );
497         menuButton->hide();
498         discFrame->show();
499     }
500 }
501
502 static bool b_my_volume;
503 void ControlsWidget::updateVolume( int sliderVolume )
504 {
505     if( !b_my_volume )
506     {
507         int i_res = sliderVolume * AOUT_VOLUME_MAX /
508                             ( 2*volumeSlider->maximum() );
509         aout_VolumeSet( p_intf, i_res );
510     }
511 }
512
513 void ControlsWidget::updateOnTimer()
514 {
515     audio_volume_t i_volume;
516     aout_VolumeGet( p_intf, &i_volume );
517     i_volume = ( i_volume *  200 )/ AOUT_VOLUME_MAX ;
518     int i_gauge = volumeSlider->value();
519     b_my_volume = false;
520     if( i_volume - i_gauge > 1 || i_gauge - i_volume > 1 )
521     {
522         b_my_volume = true;
523         volumeSlider->setValue( i_volume );
524         b_my_volume = false;
525     }
526 }
527
528 /* FIXME */
529 void ControlsWidget::setStatus( int status )
530 {
531     if( status == 1 ) // Playing
532     {
533         msg_Dbg( p_intf, "I was here %i", status );
534         // playButton->setIcon( QIcon( ":/pixmaps/pause.png" ) );
535     }
536     else
537     {
538         msg_Dbg( p_intf, "I was here %i", status );
539         // playButton->setIcon( QIcon( ":/pixmaps/play.png" ) );
540     }
541 }
542
543 /**
544  * TODO
545  * This functions toggle the fullscreen mode
546  * If there is no video, it should first activate Visualisations... 
547  *  This has also to be fixed in enableVideo()
548  */
549 void ControlsWidget::fullscreen()
550 {
551     msg_Dbg( p_intf, "Not implemented yet" );
552 }
553
554 void ControlsWidget::extSettings()
555 {
556     THEDP->extendedDialog();
557 }
558 void ControlsWidget::prefs()
559 {
560     THEDP->prefsDialog();
561 }
562
563 void ControlsWidget::slower()
564 {
565     THEMIM->getIM()->slower();
566 }
567
568 void ControlsWidget::faster()
569 {
570     THEMIM->getIM()->faster();
571 }
572
573 void ControlsWidget::enableInput( bool enable )
574 {
575     slowerButton->setEnabled( enable );
576     slider->setEnabled( enable );
577     fasterButton->setEnabled( enable );
578 }
579
580 void ControlsWidget::enableVideo( bool enable )
581 {
582     // TODO Later make the fullscreenButton toggle Visualisation and so on.
583     fullscreenButton->setEnabled( enable );
584 }
585
586
587 /**********************************************************************
588  * Playlist Widget. The embedded playlist
589  **********************************************************************/
590 #include "components/playlist/panels.hpp"
591 #include "components/playlist/selector.hpp"
592
593 PlaylistWidget::PlaylistWidget( intf_thread_t *_p_intf ) :
594                                 p_intf ( _p_intf )
595 {
596     /* Left Part and design */
597     QWidget *leftW = new QWidget( this );
598     QVBoxLayout *left = new QVBoxLayout( leftW );
599
600     /* Source Selector */
601     selector = new PLSelector( this, p_intf, THEPL );
602     left->addWidget( selector );
603
604     /* Art label */
605     art = new QLabel( "" );
606     art->setMinimumHeight( 128 );
607     art->setMinimumWidth( 128 );
608     art->setMaximumHeight( 128 );
609     art->setMaximumWidth( 128 );
610     art->setScaledContents( true );
611     art->setPixmap( QPixmap( ":/noart.png" ) );
612     left->addWidget( art );
613
614     /* Initialisation of the playlist */
615     playlist_item_t *p_root = playlist_GetPreferredNode( THEPL,
616                                                 THEPL->p_local_category );
617
618     rightPanel = qobject_cast<PLPanel *>( new StandardPLPanel( this,
619                               p_intf, THEPL, p_root ) );
620
621     /* Connects */
622     CONNECT( selector, activated( int ), rightPanel, setRoot( int ) );
623
624     CONNECT( qobject_cast<StandardPLPanel *>( rightPanel )->model,
625              artSet( QString ) , this, setArt( QString ) );
626     /* Forward removal requests from the selector to the main panel */
627     CONNECT( qobject_cast<PLSelector *>( selector )->model,
628              shouldRemove( int ),
629              qobject_cast<StandardPLPanel *>( rightPanel ), removeItem( int ) );
630
631     connect( selector, SIGNAL( activated( int ) ),
632              this, SIGNAL( rootChanged( int ) ) );
633     emit rootChanged( p_root->i_id );
634
635     /* Add the two sides of the QSplitter */
636     addWidget( leftW );
637     addWidget( rightPanel );
638
639     leftW->setMaximumWidth( 250 );
640     setCollapsible( 1, false );
641
642     QList<int> sizeList;
643     sizeList << 180 << 520 ;
644     setSizes( sizeList );
645 }
646
647 void PlaylistWidget::setArt( QString url )
648 {
649     if( url.isNull() )
650         art->setPixmap( QPixmap( ":/noart.png" ) );
651     else if( prevArt != url )
652         art->setPixmap( QPixmap( url ) );
653     prevArt = url;
654     emit artSet( url );
655 }
656
657 PlaylistWidget::~PlaylistWidget()
658 {
659 }
660
661 QSize PlaylistWidget::sizeHint() const
662 {
663     return widgetSize;
664 }
665