# include "config.h"
#endif
+#include "qt4.hpp"
#include "components/interface_widgets.hpp"
+#include "dialogs_provider.hpp"
+#include "util/customwidgets.hpp" // qtEventToVLCKey, QVLCStackedWidget
#include "menus.hpp" /* Popup menu on bgWidget */
#include <QLabel>
#include <QToolButton>
#include <QPalette>
+#include <QEvent>
#include <QResizeEvent>
#include <QDate>
#include <QMenu>
#include <QPainter>
#include <QTimer>
#include <QSlider>
+#include <QBitmap>
#ifdef Q_WS_X11
-# include <X11/Xlib.h>
-# include <qx11info_x11.h>
-static void videoSync( void )
-{
- /* Make sure the X server has processed all requests.
- * This protects other threads using distinct connections from getting
- * the video widget window in an inconsistent states. */
- XSync( QX11Info::display(), False );
-}
-#else
-# define videoSync() (void)0
+# include <X11/Xlib.h>
+# include <qx11info_x11.h>
#endif
#include <math.h>
#include <assert.h>
-class ReparentableWidget : public QWidget
-{
-private:
- VideoWidget *owner;
-public:
- ReparentableWidget( VideoWidget *owner ) : owner( owner )
- {
- }
-
-protected:
- void keyPressEvent( QKeyEvent *e )
- {
- emit owner->keyPressed( e );
- }
-};
-
/**********************************************************************
* Video Widget. A simple frame on which video is drawn
* This class handles resize issues
**********************************************************************/
VideoWidget::VideoWidget( intf_thread_t *_p_i )
- : QFrame( NULL )
- , p_intf( _p_i )
- , reparentable( NULL )
+ : QFrame( NULL ) , p_intf( _p_i )
{
/* Set the policy to expand in both directions */
// setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
layout = new QHBoxLayout( this );
layout->setContentsMargins( 0, 0, 0, 0 );
- setLayout( layout );
+ stable = NULL;
+ show();
}
VideoWidget::~VideoWidget()
{
/* Ensure we are not leaking the video output. This would crash. */
- assert( reparentable == NULL );
+ assert( !stable );
+}
+
+void VideoWidget::sync( void )
+{
+#ifdef Q_WS_X11
+ /* Make sure the X server has processed all requests.
+ * This protects other threads using distinct connections from getting
+ * the video widget window in an inconsistent states. */
+ XSync( QX11Info::display(), False );
+#endif
}
/**
{
msg_Dbg( p_intf, "Video was requested %i, %i", *pi_x, *pi_y );
- if( reparentable != NULL )
+ if( stable )
{
msg_Dbg( p_intf, "embedded video already in use" );
- return NULL;
+ return 0;
}
if( b_keep_size )
{
*pi_height = size().height();
}
- /* The Qt4 UI needs a fixed a widget ("this"), so that the parent layout is
- * not messed up when we the video is reparented. Hence, we create an extra
- * reparentable widget, that will be within the VideoWidget in windowed
- * mode, and within the root window (NULL parent) in full-screen mode.
- */
- reparentable = new ReparentableWidget( this );
- QLayout *innerLayout = new QHBoxLayout( reparentable );
- innerLayout->setContentsMargins( 0, 0, 0, 0 );
-
/* The owner of the video window needs a stable handle (WinId). Reparenting
* in Qt4-X11 changes the WinId of the widget, so we need to create another
* dummy widget that stays within the reparentable widget. */
- QWidget *stable = new QWidget();
+ stable = new QWidget();
QPalette plt = palette();
plt.setColor( QPalette::Window, Qt::black );
stable->setPalette( plt );
/* Indicates that the widget wants to draw directly onto the screen.
Widgets with this attribute set do not participate in composition
management */
+ /* This is currently disabled on X11 as it does not seem to improve
+ * performance, but causes the video widget to be transparent... */
+#ifndef Q_WS_X11
stable->setAttribute( Qt::WA_PaintOnScreen, true );
+#endif
- innerLayout->addWidget( stable );
-
- layout->addWidget( reparentable );
+ layout->addWidget( stable );
#ifdef Q_WS_X11
/* HACK: Only one X11 client can subscribe to mouse button press events.
attr.your_event_mask &= ~(ButtonPressMask|ButtonReleaseMask);
XSelectInput( dpy, w, attr.your_event_mask );
#endif
- videoSync();
+ sync();
#ifndef NDEBUG
msg_Dbg( p_intf, "embedded video ready (handle %p)",
(void *)stable->winId() );
Parent has to care about resizing itself */
void VideoWidget::SetSizing( unsigned int w, unsigned int h )
{
- if (reparentable->windowState() & Qt::WindowFullScreen )
- return;
- msg_Dbg( p_intf, "Video is resizing to: %i %i", w, h );
- videoSize.setWidth( w );
- videoSize.setHeight( h );
- if( !isVisible() ) show();
- updateGeometry(); // Needed for deinterlace
- videoSync();
-}
-
-void VideoWidget::SetFullScreen( bool b_fs )
-{
- const Qt::WindowStates curstate = reparentable->windowState();
- Qt::WindowStates newstate = curstate;
- Qt::WindowFlags newflags = reparentable->windowFlags();
-
-
- if( b_fs )
- {
- newstate |= Qt::WindowFullScreen;
- newflags |= Qt::WindowStaysOnTopHint;
- }
- else
- {
- newstate &= ~Qt::WindowFullScreen;
- newflags &= ~Qt::WindowStaysOnTopHint;
- }
- if( newstate == curstate )
- return; /* no changes needed */
-
- if( b_fs )
- { /* Go full-screen */
- int numscreen = var_InheritInteger( p_intf, "qt-fullscreen-screennumber" );
- /* if user hasn't defined screennumber, or screennumber that is bigger
- * than current number of screens, take screennumber where current interface
- * is
- */
- if( numscreen == -1 || numscreen > QApplication::desktop()->numScreens() )
- numscreen = QApplication::desktop()->screenNumber( p_intf->p_sys->p_mi );
-
- QRect screenres = QApplication::desktop()->screenGeometry( numscreen );
-
- reparentable->setParent( NULL, newflags );
- reparentable->setWindowState( newstate );
- /* To be sure window is on proper-screen in xinerama */
- if( !screenres.contains( reparentable->pos() ) )
- {
- msg_Dbg( p_intf, "Moving video to correct screen");
- reparentable->move( QPoint( screenres.x(), screenres.y() ) );
- }
- reparentable->show();
- }
- else
- { /* Go windowed */
- reparentable->setWindowFlags( newflags );
- reparentable->setWindowState( newstate );
- layout->addWidget( reparentable );
- }
- videoSync();
+ resize( w, h );
+ emit sizeChanged( w, h );
+ /* Work-around a bug?misconception? that would happen when vout core resize
+ twice to the same size and would make the vout not centered.
+ This cause a small flicker.
+ See #3621
+ */
+ if( size().width() == w && size().height() == h )
+ updateGeometry();
+ sync();
}
void VideoWidget::release( void )
{
msg_Dbg( p_intf, "Video is not needed anymore" );
- //layout->removeWidget( reparentable );
-#ifdef WIN32
- /* Come back to default thumbnail for Windows 7 taskbar */
- LPTASKBARLIST3 p_taskbl;
-
- CoInitialize( 0 );
-
- if( S_OK == CoCreateInstance( &clsid_ITaskbarList,
- NULL, CLSCTX_INPROC_SERVER,
- &IID_ITaskbarList3,
- (void **)&p_taskbl) )
+ if( stable )
{
- p_taskbl->vt->HrInit(p_taskbl);
-
- HWND hroot = GetAncestor(reparentable->winId(),GA_ROOT);
-
- if (S_OK != p_taskbl->vt->SetThumbnailClip(p_taskbl, hroot, NULL))
- msg_Err(p_intf, "SetThumbNailClip failed");
- msg_Err(p_intf, "Releasing taskbar | root handle = %08x", hroot);
- p_taskbl->vt->Release(p_taskbl);
+ layout->removeWidget( stable );
+ stable->deleteLater();
+ stable = NULL;
}
- CoUninitialize();
-#endif
-
- delete reparentable;
- reparentable = NULL;
- videoSize = QSize();
updateGeometry();
- hide();
-}
-
-
-QSize VideoWidget::sizeHint() const
-{
- return videoSize;
}
/**********************************************************************
* Background Widget. Show a simple image background. Currently,
* it's album art if present or cone.
**********************************************************************/
-#define ICON_SIZE 128
-#define MAX_BG_SIZE 400
-#define MIN_BG_SIZE 128
-#define MARGIN 5
BackgroundWidget::BackgroundWidget( intf_thread_t *_p_i )
- :QWidget( NULL ), p_intf( _p_i )
+ :QWidget( NULL ), p_intf( _p_i ), b_expandPixmap( false ), b_withart( true )
{
- /* We should use that one to take the more size it can */
- //setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding);
-
/* A dark background */
setAutoFillBackground( true );
QPalette plt = palette();
plt.setColor( QPalette::Inactive, QPalette::Window , Qt::black );
setPalette( plt );
- /* A cone in the middle */
- label = new QLabel;
- label->setMargin( MARGIN );
- label->setAlignment( Qt::AlignCenter );
-
/* Init the cone art */
updateArt( "" );
- /* Grid, because of the visual selector */
- QGridLayout *backgroundLayout = new QGridLayout( this );
- backgroundLayout->setMargin( 0 );
- backgroundLayout->addWidget( label, 0, 1 );
- backgroundLayout->setColumnStretch( 0, 1 );
- backgroundLayout->setColumnStretch( 2, 1 );
-
CONNECT( THEMIM->getIM(), artChanged( QString ),
this, updateArt( const QString& ) );
-
- /* Start Hidden */
- label->hide();
}
-void BackgroundWidget::resizeEvent( QResizeEvent * event )
+void BackgroundWidget::updateArt( const QString& url )
{
- if( event->size().height() <= MIN_BG_SIZE + MARGIN * 2 + 2 )
- label->hide();
+ if ( !url.isEmpty() )
+ {
+ pixmapUrl = url;
+ }
else
- label->show();
+ { /* Xmas joke */
+ if( QDate::currentDate().dayOfYear() >= QT_XMAS_JOKE_DAY && var_InheritBool( p_intf, "qt-icon-change" ) )
+ pixmapUrl = QString( ":/logo/vlc128-xmas.png" );
+ else
+ pixmapUrl = QString( ":/logo/vlc128.png" );
+ }
+ update();
}
-void BackgroundWidget::updateArt( const QString& url )
+void BackgroundWidget::paintEvent( QPaintEvent *e )
{
- if( url.isEmpty() )
+ if ( !b_withart )
{
- /* Xmas joke */
- if( QDate::currentDate().dayOfYear() >= 354 )
- label->setPixmap( QPixmap( ":/logo/vlc128-christmas.png" ) );
- else
- label->setPixmap( QPixmap( ":/logo/vlc128.png" ) );
+ /* we just want background autofill */
+ QWidget::paintEvent( e );
+ return;
}
- else
+
+ int i_maxwidth, i_maxheight;
+ QPixmap pixmap = QPixmap( pixmapUrl );
+ QPainter painter(this);
+ QBitmap pMask;
+ float f_alpha = 1.0;
+
+ i_maxwidth = std::min( maximumWidth(), width() ) - MARGIN * 2;
+ i_maxheight = std::min( maximumHeight(), height() ) - MARGIN * 2;
+
+ if ( height() > MARGIN * 2 )
{
- QPixmap pixmap( url );
- if( pixmap.width() > label->maximumWidth() ||
- pixmap.height() > label->maximumHeight() )
+ /* Scale down the pixmap if the widget is too small */
+ if( pixmap.width() > i_maxwidth || pixmap.height() > i_maxheight )
{
- pixmap = pixmap.scaled( label->maximumWidth(),
- label->maximumHeight(), Qt::KeepAspectRatio );
+ pixmap = pixmap.scaled( i_maxwidth, i_maxheight,
+ Qt::KeepAspectRatio, Qt::SmoothTransformation );
+ }
+ else
+ if ( b_expandPixmap &&
+ pixmap.width() < width() && pixmap.height() < height() )
+ {
+ /* Scale up the pixmap to fill widget's size */
+ f_alpha = ( (float) pixmap.height() / (float) height() );
+ pixmap = pixmap.scaled(
+ width() - MARGIN * 2,
+ height() - MARGIN * 2,
+ Qt::KeepAspectRatio,
+ ( f_alpha < .2 )? /* Don't waste cpu when not visible */
+ Qt::SmoothTransformation:
+ Qt::FastTransformation
+ );
+ /* Non agressive alpha compositing when sizing up */
+ pMask = QBitmap( pixmap.width(), pixmap.height() );
+ pMask.fill( QColor::fromRgbF( 1.0, 1.0, 1.0, f_alpha ) );
+ pixmap.setMask( pMask );
}
- label->setPixmap( pixmap );
+ painter.drawPixmap(
+ MARGIN + ( i_maxwidth - pixmap.width() ) /2,
+ MARGIN + ( i_maxheight - pixmap.height() ) /2,
+ pixmap);
}
+ QWidget::paintEvent( e );
}
void BackgroundWidget::contextMenuEvent( QContextMenuEvent *event )
SpeedLabel::SpeedLabel( intf_thread_t *_p_intf, QWidget *parent )
: QLabel( parent ), p_intf( _p_intf )
{
- setToolTip( qtr( "Current playback speed.\nClick to adjust" ) );
+ tooltipStringPattern = qtr( "Current playback speed: %1\nClick to adjust" );
/* Create the Speed Control Widget */
speedControl = new SpeedControlWidget( p_intf, this );
str.setNum( rate, 'f', 2 );
str.append( "x" );
setText( str );
- setToolTip( str );
+ setToolTip( tooltipStringPattern.arg( str ) );
speedControl->updateControls( rate );
}
speedControlLayout->addWidget( speedSlider );
speedControlLayout->addWidget( normalSpeedButton );
+ lastValue = 0;
+
activateOnState();
}
return;
}
- double value = 17 * log( rate ) / log( 2 );
+ double value = 17 * log( rate ) / log( 2. );
int sliderValue = (int) ( ( value > 0 ) ? value + .5 : value - .5 );
if( sliderValue < speedSlider->minimum() )
{
sliderValue = speedSlider->maximum();
}
+ lastValue = sliderValue;
speedSlider->setValue( sliderValue );
}
void SpeedControlWidget::updateRate( int sliderValue )
{
+ if( sliderValue == lastValue )
+ return;
+
double speed = pow( 2, (double)sliderValue / 17 );
int rate = INPUT_RATE_DEFAULT / speed;
if( !url.isEmpty() && pix.load( url ) )
{
pix = pix.scaled( maximumWidth(), maximumHeight(),
- Qt::KeepAspectRatioByExpanding );
+ Qt::KeepAspectRatioByExpanding,
+ Qt::SmoothTransformation );
}
else
{
b_remainingTime = false;
setText( " --:--/--:-- " );
setAlignment( Qt::AlignRight | Qt::AlignVCenter );
- setToolTip( qtr( "Toggle between elapsed and remaining time" ) );
+ setToolTip( QString( "- " )
+ + qtr( "Click to toggle between elapsed and remaining time" )
+ + QString( "\n- " )
+ + qtr( "Double click to jump to a chosen time position" ) );
bufTimer->setSingleShot( true );
CONNECT( THEMIM->getIM(), positionUpdated( float, int64_t, int ),
secstotimestr( psz_time, ( b_remainingTime && length ) ? length - time
: time );
- QString timestr;
- timestr.sprintf( " %s%s/%s ", (b_remainingTime && length) ? "-" : "",
- psz_time, ( !length && time ) ? "--:--" : psz_length );
+ QString timestr = QString( " %1%2/%3 " )
+ .arg( QString( (b_remainingTime && length) ? "-" : "" ) )
+ .arg( QString( psz_time ) )
+ .arg( QString( ( !length && time ) ? "--:--" : psz_length ) );
setText( timestr );
secstotimestr( psz_time,
( b_remainingTime && cachedLength ?
cachedLength - time : time ) );
- QString timestr;
- timestr.sprintf( " %s%s/%s ", (b_remainingTime && cachedLength) ? "-" : "",
- psz_time, ( !cachedLength && time ) ? "--:--" : psz_length );
+ QString timestr = QString( " %1%2/%3 " )
+ .arg( QString( (b_remainingTime && cachedLength) ? "-" : "" ) )
+ .arg( QString( psz_time ) )
+ .arg( QString( ( !cachedLength && time ) ? "--:--" : psz_length ) );
setText( timestr );
}