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