1 /*****************************************************************************
2 * interface_widgets.cpp : Custom widgets for the main interface
3 ****************************************************************************
4 * Copyright ( C ) 2006 the VideoLAN team
7 * Authors: Clément Stenac <zorglub@videolan.org>
8 * Jean-Baptiste Kempf <jb@videolan.org>
9 * Rafaël Carré <funman@videolanorg>
10 * Ilkka Ollakka <ileoo@videolan.org>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * ( at your option ) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
33 #include "dialogs_provider.hpp"
34 #include "components/interface_widgets.hpp"
35 #include "main_interface.hpp"
36 #include "input_manager.hpp"
38 #include "util/input_slider.hpp"
39 #include "util/customwidgets.hpp"
42 #include <QSpacerItem>
44 #include <QPushButton>
45 #include <QToolButton>
46 #include <QHBoxLayout>
49 #include <QResizeEvent>
53 # include <X11/Xlib.h>
54 # include <qx11info_x11.h>
59 #define I_PLAY_TOOLTIP N_("Play\nIf the playlist is empty, open a media")
61 /**********************************************************************
62 * Video Widget. A simple frame on which video is drawn
63 * This class handles resize issues
64 **********************************************************************/
66 VideoWidget::VideoWidget( intf_thread_t *_p_i ) : QFrame( NULL ), p_intf( _p_i )
70 videoSize.rwidth() = -1;
71 videoSize.rheight() = -1;
75 /* Set the policy to expand in both directions */
76 // setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
78 /* Black background is more coherent for a Video Widget */
79 QPalette plt = palette();
80 plt.setColor( QPalette::Window, Qt::black );
82 setAutoFillBackground(true);
84 /* Indicates that the widget wants to draw directly onto the screen.
85 Widgets with this attribute set do not participate in composition
87 setAttribute( Qt::WA_PaintOnScreen, true );
89 /* The core can ask through a callback to show the video. */
91 connect( this, SIGNAL(askVideoWidgetToShow( unsigned int, unsigned int)),
92 this, SLOT(SetSizing(unsigned int, unsigned int )),
93 Qt::BlockingQueuedConnection );
95 #warning This is broken. Fix it with a QEventLoop with a processEvents ()
96 connect( this, SIGNAL(askVideoWidgetToShow( unsigned int, unsigned int)),
97 this, SLOT(SetSizing(unsigned int, unsigned int )) );
101 void VideoWidget::paintEvent(QPaintEvent *ev)
103 QFrame::paintEvent(ev);
105 XFlush( QX11Info::display() );
109 /* Kill the vout at Destruction */
110 VideoWidget::~VideoWidget()
114 if( vout_Control( p_vout, VOUT_CLOSE ) != VLC_SUCCESS )
115 vout_Control( p_vout, VOUT_REPARENT );
119 * Request the video to avoid the conflicts
121 void *VideoWidget::request( vout_thread_t *p_nvout, int *pi_x, int *pi_y,
122 unsigned int *pi_width, unsigned int *pi_height )
124 msg_Dbg( p_intf, "Video was requested %i, %i", *pi_x, *pi_y );
125 emit askVideoWidgetToShow( *pi_width, *pi_height );
128 msg_Dbg( p_intf, "embedded video already in use" );
133 msg_Dbg( p_intf, "embedded video ready (handle %p)", winId() );
135 return ( void* )winId();
138 /* Set the Widget to the correct Size */
139 /* Function has to be called by the parent
140 Parent has to care about resizing himself*/
141 void VideoWidget::SetSizing( unsigned int w, unsigned int h )
143 msg_Dbg( p_intf, "Video is resizing to: %i %i", w, h );
144 videoSize.rwidth() = w;
145 videoSize.rheight() = h;
146 if( isHidden() ) show();
147 updateGeometry(); // Needed for deinterlace
150 void VideoWidget::release( void *p_win )
152 msg_Dbg( p_intf, "Video is not needed anymore" );
154 videoSize.rwidth() = 0;
155 videoSize.rheight() = 0;
160 QSize VideoWidget::sizeHint() const
165 /**********************************************************************
166 * Background Widget. Show a simple image background. Currently,
167 * it's album art if present or cone.
168 **********************************************************************/
169 #define ICON_SIZE 128
170 #define MAX_BG_SIZE 400
171 #define MIN_BG_SIZE 128
173 BackgroundWidget::BackgroundWidget( intf_thread_t *_p_i )
174 :QWidget( NULL ), p_intf( _p_i )
176 /* We should use that one to take the more size it can */
177 setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding);
179 /* A dark background */
180 setAutoFillBackground( true );
182 plt.setColor( QPalette::Active, QPalette::Window , Qt::black );
183 plt.setColor( QPalette::Inactive, QPalette::Window , Qt::black );
186 /* A cone in the middle */
188 label->setMargin( 5 );
189 label->setMaximumHeight( MAX_BG_SIZE );
190 label->setMaximumWidth( MAX_BG_SIZE );
191 label->setMinimumHeight( MIN_BG_SIZE );
192 label->setMinimumWidth( MIN_BG_SIZE );
193 if( QDate::currentDate().dayOfYear() >= 354 )
194 label->setPixmap( QPixmap( ":/vlc128-christmas.png" ) );
196 label->setPixmap( QPixmap( ":/vlc128.png" ) );
198 QGridLayout *backgroundLayout = new QGridLayout( this );
199 backgroundLayout->addWidget( label, 0, 1 );
200 backgroundLayout->setColumnStretch( 0, 1 );
201 backgroundLayout->setColumnStretch( 2, 1 );
203 CONNECT( THEMIM->getIM(), artChanged( input_item_t* ),
204 this, updateArt( input_item_t* ) );
207 BackgroundWidget::~BackgroundWidget()
210 void BackgroundWidget::resizeEvent( QResizeEvent * event )
212 if( event->size().height() <= MIN_BG_SIZE )
218 void BackgroundWidget::updateArt( input_item_t *p_item )
223 char *psz_art = input_item_GetArtURL( p_item );
230 if( QDate::currentDate().dayOfYear() >= 354 )
231 label->setPixmap( QPixmap( ":/vlc128-christmas.png" ) );
233 label->setPixmap( QPixmap( ":/vlc128.png" ) );
237 url = url.replace( "file://", QString("" ) );
238 /* Taglib seems to define a attachment://, It won't work yet */
239 url = url.replace( "attachment://", QString("" ) );
240 label->setPixmap( QPixmap( url ) );
244 void BackgroundWidget::contextMenuEvent( QContextMenuEvent *event )
246 QVLCMenu::PopupMenu( p_intf, true );
250 /**********************************************************************
251 * Visualization selector panel
252 **********************************************************************/
253 VisualSelector::VisualSelector( intf_thread_t *_p_i ) :
254 QFrame( NULL ), p_intf( _p_i )
256 QHBoxLayout *layout = new QHBoxLayout( this );
257 layout->setMargin( 0 );
258 QPushButton *prevButton = new QPushButton( "Prev" );
259 QPushButton *nextButton = new QPushButton( "Next" );
260 layout->addWidget( prevButton );
261 layout->addWidget( nextButton );
263 layout->addStretch( 10 );
264 layout->addWidget( new QLabel( qtr( "Current visualization" ) ) );
266 current = new QLabel( qtr( "None" ) );
267 layout->addWidget( current );
269 BUTTONACT( prevButton, prev() );
270 BUTTONACT( nextButton, next() );
273 setMaximumHeight( 35 );
276 VisualSelector::~VisualSelector()
279 void VisualSelector::prev()
281 char *psz_new = aout_VisualPrev( p_intf );
284 current->setText( qfu( psz_new ) );
289 void VisualSelector::next()
291 char *psz_new = aout_VisualNext( p_intf );
294 current->setText( qfu( psz_new ) );
300 /**********************************************************************
301 * Speed control widget
302 **********************************************************************/
303 SpeedControlWidget::SpeedControlWidget( intf_thread_t *_p_i ) :
304 QFrame( NULL ), p_intf( _p_i )
306 QSizePolicy sizePolicy( QSizePolicy::Maximum, QSizePolicy::Fixed );
307 sizePolicy.setHorizontalStretch( 0 );
308 sizePolicy.setVerticalStretch( 0 );
310 speedSlider = new QSlider;
311 speedSlider->setSizePolicy( sizePolicy );
312 speedSlider->setMaximumSize( QSize( 80, 200 ) );
313 speedSlider->setOrientation( Qt::Vertical );
314 speedSlider->setTickPosition( QSlider::TicksRight );
316 speedSlider->setRange( -24, 24 );
317 speedSlider->setSingleStep( 1 );
318 speedSlider->setPageStep( 1 );
319 speedSlider->setTickInterval( 12 );
321 CONNECT( speedSlider, valueChanged( int ), this, updateRate( int ) );
323 QToolButton *normalSpeedButton = new QToolButton( this );
324 normalSpeedButton->setMaximumSize( QSize( 26, 20 ) );
325 normalSpeedButton->setAutoRaise( true );
326 normalSpeedButton->setText( "1x" );
327 normalSpeedButton->setToolTip( qtr( "Revert to normal play speed" ) );
329 CONNECT( normalSpeedButton, clicked(), this, resetRate() );
331 QVBoxLayout *speedControlLayout = new QVBoxLayout;
332 speedControlLayout->setLayoutMargins( 4, 4, 4, 4, 4 );
333 speedControlLayout->setSpacing( 4 );
334 speedControlLayout->addWidget( speedSlider );
335 speedControlLayout->addWidget( normalSpeedButton );
336 setLayout( speedControlLayout );
339 SpeedControlWidget::~SpeedControlWidget()
342 void SpeedControlWidget::setEnable( bool b_enable )
344 speedSlider->setEnabled( b_enable );
347 void SpeedControlWidget::updateControls( int rate )
349 if( speedSlider->isSliderDown() )
351 //We don't want to change anything if the user is using the slider
355 double value = 12 * log( (double)INPUT_RATE_DEFAULT / rate ) / log( 2 );
356 int sliderValue = (int) ( ( value > 0 ) ? value + .5 : value - .5 );
358 if( sliderValue < speedSlider->minimum() )
360 sliderValue = speedSlider->minimum();
362 else if( sliderValue > speedSlider->maximum() )
364 sliderValue = speedSlider->maximum();
367 //Block signals to avoid feedback loop
368 speedSlider->blockSignals( true );
369 speedSlider->setValue( sliderValue );
370 speedSlider->blockSignals( false );
373 void SpeedControlWidget::updateRate( int sliderValue )
375 double speed = pow( 2, (double)sliderValue / 12 );
376 int rate = INPUT_RATE_DEFAULT / speed;
378 THEMIM->getIM()->setRate(rate);
381 void SpeedControlWidget::resetRate()
383 THEMIM->getIM()->setRate(INPUT_RATE_DEFAULT);
388 static int downloadCoverCallback( vlc_object_t *p_this,
390 vlc_value_t oldvar, vlc_value_t newvar,
393 if( !strcmp( psz_var, "item-change" ) )
395 CoverArtLabel *art = static_cast< CoverArtLabel* >( data );
397 art->requestUpdate();
402 CoverArtLabel::CoverArtLabel( QWidget *parent,
403 vlc_object_t *_p_this,
404 input_item_t *_p_input )
405 : QLabel( parent ), p_this( _p_this), p_input( _p_input ), prevArt()
407 setContextMenuPolicy( Qt::ActionsContextMenu );
408 CONNECT( this, updateRequested(), this, doUpdate() );
410 playlist_t *p_playlist = pl_Hold( p_this );
411 var_AddCallback( p_playlist, "item-change",
412 downloadCoverCallback, this );
413 pl_Release( p_this );
415 setMinimumHeight( 128 );
416 setMinimumWidth( 128 );
417 setMaximumHeight( 128 );
418 setMaximumWidth( 128 );
419 setScaledContents( true );
424 void CoverArtLabel::downloadCover()
428 playlist_t *p_playlist = pl_Hold( p_this );
429 playlist_AskForArtEnqueue( p_playlist, p_input );
430 pl_Release( p_this );
434 void CoverArtLabel::doUpdate()
438 setPixmap( QPixmap( ":/noart.png" ) );
439 QList< QAction* > artActions = actions();
440 if( !artActions.isEmpty() )
441 foreach( QAction *act, artActions )
447 char *psz_meta = input_item_GetArtURL( p_input );
448 if( psz_meta && !strncmp( psz_meta, "file://", 7 ) )
450 QString artUrl = qfu( psz_meta ).replace( "file://", "" );
451 if( artUrl != prevArt )
454 if( pix.load( artUrl ) )
458 msg_Dbg( p_this, "Qt could not load image '%s'",
460 setPixmap( QPixmap( ":/noart.png" ) );
463 QList< QAction* > artActions = actions();
464 if( !artActions.isEmpty() )
466 foreach( QAction *act, artActions )
474 setPixmap( QPixmap( ":/noart.png" ) );
476 QList< QAction* > artActions = actions();
477 if( artActions.isEmpty() )
479 QAction *action = new QAction( qtr( "Download cover art" ),
482 CONNECT( action, triggered(),
483 this, downloadCover() );
490 TimeLabel::TimeLabel( intf_thread_t *_p_intf ) :QLabel(), p_intf( _p_intf )
492 b_remainingTime = false;
493 setText( " --:--/--:-- " );
494 setAlignment( Qt::AlignRight | Qt::AlignVCenter );
495 setToolTip( qtr( "Toggle between elapsed and remaining time" ) );
498 CONNECT( THEMIM->getIM(), positionUpdated( float, int, int ),
499 this, setDisplayPosition( float, int, int ) );
502 void TimeLabel::setDisplayPosition( float pos, int time, int length )
504 char psz_length[MSTRTIME_MAX_SIZE], psz_time[MSTRTIME_MAX_SIZE];
505 secstotimestr( psz_length, length );
506 secstotimestr( psz_time, ( b_remainingTime && length ) ? length - time
510 timestr.sprintf( "%s/%s", psz_time,
511 ( !length && time ) ? "--:--" : psz_length );
513 /* Add a minus to remaining time*/
514 if( b_remainingTime && length ) setText( " -"+timestr+" " );
515 else setText( " "+timestr+" " );
518 void TimeLabel::toggleTimeDisplay()
520 b_remainingTime = !b_remainingTime;
523 bool VolumeClickHandler::eventFilter( QObject *obj, QEvent *e )
525 if (e->type() == QEvent::MouseButtonPress )
527 aout_VolumeMute( p_intf, NULL );
528 audio_volume_t i_volume;
529 aout_VolumeGet( p_intf, &i_volume );
530 // m->updateVolume( i_volume * VOLUME_MAX / (AOUT_VOLUME_MAX/2) );