X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fgui%2Fqt4%2Fcomponents%2Finterface_widgets.cpp;h=ba5b4a5535a6ff9a5f08a2d321015c16cc133f6c;hb=88d39ceef6b983663ccae410be55a2ace26d9074;hp=7807fa510f7b2c3b2fd33a472a45132301d8f196;hpb=05d3d3a12fb5023fc421fd3e6344ec0b209909bc;p=vlc diff --git a/modules/gui/qt4/components/interface_widgets.cpp b/modules/gui/qt4/components/interface_widgets.cpp index 7807fa510f..ba5b4a5535 100644 --- a/modules/gui/qt4/components/interface_widgets.cpp +++ b/modules/gui/qt4/components/interface_widgets.cpp @@ -41,104 +41,235 @@ #include #include #include +#include +#include +#include #ifdef Q_WS_X11 # include # include +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 #endif #include +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 ) +VideoWidget::VideoWidget( intf_thread_t *_p_i ) + : QFrame( NULL ) + , p_intf( _p_i ) + , reparentable( NULL ) { - /* Init */ - p_vout = NULL; - videoSize.rwidth() = -1; - videoSize.rheight() = -1; - - hide(); - /* Set the policy to expand in both directions */ // setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); - /* Black background is more coherent for a Video Widget */ - QPalette plt = palette(); - plt.setColor( QPalette::Window, Qt::black ); - setPalette( plt ); - setAutoFillBackground(true); - - /* Indicates that the widget wants to draw directly onto the screen. - Widgets with this attribute set do not participate in composition - management */ - setAttribute( Qt::WA_PaintOnScreen, true ); - - /* The core can ask through a callback to show the video. */ - connect( this, SIGNAL(askVideoWidgetToShow( unsigned int, unsigned int)), - this, SLOT(SetSizing(unsigned int, unsigned int )), - Qt::BlockingQueuedConnection ); -} - -void VideoWidget::paintEvent(QPaintEvent *ev) -{ - QFrame::paintEvent(ev); -#ifdef Q_WS_X11 - XFlush( QX11Info::display() ); -#endif + layout = new QHBoxLayout( this ); + layout->setContentsMargins( 0, 0, 0, 0 ); + setLayout( layout ); } VideoWidget::~VideoWidget() { /* Ensure we are not leaking the video output. This would crash. */ - assert( !p_vout ); + assert( reparentable == NULL ); } /** * Request the video to avoid the conflicts **/ -void *VideoWidget::request( vout_thread_t *p_nvout, int *pi_x, int *pi_y, - unsigned int *pi_width, unsigned int *pi_height ) +WId VideoWidget::request( int *pi_x, int *pi_y, + unsigned int *pi_width, unsigned int *pi_height, + bool b_keep_size ) { msg_Dbg( p_intf, "Video was requested %i, %i", *pi_x, *pi_y ); - emit askVideoWidgetToShow( *pi_width, *pi_height ); - if( p_vout ) + + if( reparentable != NULL ) { msg_Dbg( p_intf, "embedded video already in use" ); return NULL; } - p_vout = p_nvout; + if( b_keep_size ) + { + *pi_width = size().width(); + *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(); + QPalette plt = palette(); + plt.setColor( QPalette::Window, Qt::black ); + stable->setPalette( plt ); + stable->setAutoFillBackground(true); + /* Indicates that the widget wants to draw directly onto the screen. + Widgets with this attribute set do not participate in composition + management */ + stable->setAttribute( Qt::WA_PaintOnScreen, true ); + + innerLayout->addWidget( stable ); + + layout->addWidget( reparentable ); + +#ifdef Q_WS_X11 + /* HACK: Only one X11 client can subscribe to mouse button press events. + * VLC currently handles those in the video display. + * Force Qt4 to unsubscribe from mouse press and release events. */ + Display *dpy = QX11Info::display(); + Window w = stable->winId(); + XWindowAttributes attr; + + XGetWindowAttributes( dpy, w, &attr ); + attr.your_event_mask &= ~(ButtonPressMask|ButtonReleaseMask); + XSelectInput( dpy, w, attr.your_event_mask ); +#endif + videoSync(); #ifndef NDEBUG - msg_Dbg( p_intf, "embedded video ready (handle %p)", winId() ); + msg_Dbg( p_intf, "embedded video ready (handle %p)", + (void *)stable->winId() ); #endif - return ( void* )winId(); + return stable->winId(); } /* Set the Widget to the correct Size */ /* Function has to be called by the parent - Parent has to care about resizing himself*/ + 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.rwidth() = w; - videoSize.rheight() = h; - if( isHidden() ) show(); + 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(); } void VideoWidget::release( void ) { msg_Dbg( p_intf, "Video is not needed anymore" ); - p_vout = NULL; - videoSize.rwidth() = 0; - videoSize.rheight() = 0; + //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) ) + { + 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); + } + CoUninitialize(); + +#endif + + delete reparentable; + reparentable = NULL; + videoSize = QSize(); updateGeometry(); hide(); } + QSize VideoWidget::sizeHint() const { return videoSize; @@ -160,7 +291,7 @@ BackgroundWidget::BackgroundWidget( intf_thread_t *_p_i ) /* A dark background */ setAutoFillBackground( true ); - plt = palette(); + QPalette plt = palette(); plt.setColor( QPalette::Active, QPalette::Window , Qt::black ); plt.setColor( QPalette::Inactive, QPalette::Window , Qt::black ); setPalette( plt ); @@ -168,22 +299,23 @@ BackgroundWidget::BackgroundWidget( intf_thread_t *_p_i ) /* A cone in the middle */ label = new QLabel; label->setMargin( 5 ); - label->setMaximumHeight( MAX_BG_SIZE ); +/* label->setMaximumHeight( MAX_BG_SIZE ); label->setMaximumWidth( MAX_BG_SIZE ); label->setMinimumHeight( MIN_BG_SIZE ); - label->setMinimumWidth( MIN_BG_SIZE ); + label->setMinimumWidth( MIN_BG_SIZE );*/ + label->setAlignment( Qt::AlignCenter ); if( QDate::currentDate().dayOfYear() >= 354 ) - label->setPixmap( QPixmap( ":/vlc128-christmas.png" ) ); + label->setPixmap( QPixmap( ":/logo/vlc128-christmas.png" ) ); else - label->setPixmap( QPixmap( ":/vlc128.png" ) ); + label->setPixmap( QPixmap( ":/logo/vlc128.png" ) ); QGridLayout *backgroundLayout = new QGridLayout( this ); backgroundLayout->addWidget( label, 0, 1 ); backgroundLayout->setColumnStretch( 0, 1 ); backgroundLayout->setColumnStretch( 2, 1 ); - CONNECT( THEMIM->getIM(), artChanged( input_item_t* ), - this, updateArt( input_item_t* ) ); + CONNECT( THEMIM->getIM(), artChanged( QString ), + this, updateArt( const QString& ) ); } BackgroundWidget::~BackgroundWidget() @@ -197,35 +329,33 @@ void BackgroundWidget::resizeEvent( QResizeEvent * event ) label->show(); } -void BackgroundWidget::updateArt( input_item_t *p_item ) +void BackgroundWidget::updateArt( const QString& url ) { - QString url; - if( p_item ) - { - char *psz_art = input_item_GetArtURL( p_item ); - url = psz_art; - free( psz_art ); - } - if( url.isEmpty() ) { if( QDate::currentDate().dayOfYear() >= 354 ) - label->setPixmap( QPixmap( ":/vlc128-christmas.png" ) ); + label->setPixmap( QPixmap( ":/logo/vlc128-christmas.png" ) ); else - label->setPixmap( QPixmap( ":/vlc128.png" ) ); + label->setPixmap( QPixmap( ":/logo/vlc128.png" ) ); } else { - url = url.replace( "file://", QString("" ) ); - /* Taglib seems to define a attachment://, It won't work yet */ - url = url.replace( "attachment://", QString("" ) ); - label->setPixmap( QPixmap( url ) ); + QPixmap pixmap( url ); + if( pixmap.width() > label->maximumWidth() || + pixmap.height() > label->maximumHeight() ) + { + pixmap = pixmap.scaled( label->maximumWidth(), + label->maximumHeight(), Qt::KeepAspectRatio ); + } + + label->setPixmap( pixmap ); } } void BackgroundWidget::contextMenuEvent( QContextMenuEvent *event ) { QVLCMenu::PopupMenu( p_intf, true ); + event->accept(); } #if 0 @@ -282,11 +412,11 @@ void VisualSelector::next() } #endif -SpeedLabel::SpeedLabel( intf_thread_t *_p_intf, const QString text ) - : QLabel( text ), p_intf( _p_intf ) +SpeedLabel::SpeedLabel( intf_thread_t *_p_intf, const QString& text, + QWidget *parent ) + : QLabel( text, parent ), p_intf( _p_intf ) { - setToolTip( qtr( "Current playback speed.\nRight click to adjust" ) ); - setContextMenuPolicy ( Qt::CustomContextMenu ); + setToolTip( qtr( "Current playback speed.\nClick to adjust" ) ); /* Create the Speed Control Widget */ speedControl = new SpeedControlWidget( p_intf, this ); @@ -296,21 +426,23 @@ SpeedLabel::SpeedLabel( intf_thread_t *_p_intf, const QString text ) widgetAction->setDefaultWidget( speedControl ); speedControlMenu->addAction( widgetAction ); - /* Speed Label behaviour: - - right click gives the vertical speed slider */ - CONNECT( this, customContextMenuRequested( QPoint ), - this, showSpeedMenu( QPoint ) ); - /* Change the SpeedRate in the Status Bar */ CONNECT( THEMIM->getIM(), rateChanged( int ), this, setRate( int ) ); - CONNECT( THEMIM, inputChanged( input_thread_t * ), - speedControl, activateOnState() ); + DCONNECT( THEMIM, inputChanged( input_thread_t * ), + speedControl, activateOnState() ); +} + +SpeedLabel::~SpeedLabel() +{ + delete speedControl; + delete speedControlMenu; } /**************************************************************************** * Small right-click menu for rate control ****************************************************************************/ + void SpeedLabel::showSpeedMenu( QPoint pos ) { speedControlMenu->exec( QCursor::pos() - pos @@ -337,7 +469,7 @@ SpeedControlWidget::SpeedControlWidget( intf_thread_t *_p_i, QWidget *_parent ) sizePolicy.setHorizontalStretch( 0 ); sizePolicy.setVerticalStretch( 0 ); - speedSlider = new QSlider; + speedSlider = new QSlider( this ); speedSlider->setSizePolicy( sizePolicy ); speedSlider->setMaximumSize( QSize( 80, 200 ) ); speedSlider->setOrientation( Qt::Vertical ); @@ -348,7 +480,7 @@ SpeedControlWidget::SpeedControlWidget( intf_thread_t *_p_i, QWidget *_parent ) speedSlider->setPageStep( 1 ); speedSlider->setTickInterval( 17 ); - CONNECT( speedSlider, valueChanged( int ), this, updateRate( int ) ); + CONNECT( speedSlider, sliderMoved( int ), this, updateRate( int ) ); QToolButton *normalSpeedButton = new QToolButton( this ); normalSpeedButton->setMaximumSize( QSize( 26, 20 ) ); @@ -359,7 +491,7 @@ SpeedControlWidget::SpeedControlWidget( intf_thread_t *_p_i, QWidget *_parent ) CONNECT( normalSpeedButton, clicked(), this, resetRate() ); QVBoxLayout *speedControlLayout = new QVBoxLayout( this ); - speedControlLayout->setLayoutMargins( 4, 4, 4, 4, 4 ); + speedControlLayout->setContentsMargins( 4, 4, 4, 4 ); speedControlLayout->setSpacing( 4 ); speedControlLayout->addWidget( speedSlider ); speedControlLayout->addWidget( normalSpeedButton ); @@ -392,10 +524,7 @@ void SpeedControlWidget::updateControls( int rate ) sliderValue = speedSlider->maximum(); } - //Block signals to avoid feedback loop - speedSlider->blockSignals( true ); speedSlider->setValue( sliderValue ); - speedSlider->blockSignals( false ); } void SpeedControlWidget::updateRate( int sliderValue ) @@ -411,149 +540,155 @@ void SpeedControlWidget::resetRate() THEMIM->getIM()->setRate( INPUT_RATE_DEFAULT ); } -static int downloadCoverCallback( vlc_object_t *p_this, - char const *psz_var, - vlc_value_t oldvar, vlc_value_t newvar, - void *data ) -{ - if( !strcmp( psz_var, "item-change" ) ) - { - CoverArtLabel *art = static_cast< CoverArtLabel* >( data ); - if( art ) - art->requestUpdate(); - } - return VLC_SUCCESS; -} - -CoverArtLabel::CoverArtLabel( QWidget *parent, - vlc_object_t *_p_this, - input_item_t *_p_input ) - : QLabel( parent ), p_this( _p_this), p_input( _p_input ), prevArt() +CoverArtLabel::CoverArtLabel( QWidget *parent, intf_thread_t *_p_i ) + : QLabel( parent ), p_intf( _p_i ) { setContextMenuPolicy( Qt::ActionsContextMenu ); - CONNECT( this, updateRequested(), this, doUpdate() ); - - playlist_t *p_playlist = pl_Hold( p_this ); - var_AddCallback( p_playlist, "item-change", - downloadCoverCallback, this ); - pl_Release( p_this ); + CONNECT( this, updateRequested(), this, askForUpdate() ); setMinimumHeight( 128 ); setMinimumWidth( 128 ); setMaximumHeight( 128 ); setMaximumWidth( 128 ); - setScaledContents( true ); + setScaledContents( false ); + setAlignment( Qt::AlignCenter ); - doUpdate(); + QList< QAction* > artActions = actions(); + QAction *action = new QAction( qtr( "Download cover art" ), this ); + CONNECT( action, triggered(), this, askForUpdate() ); + addAction( action ); + + showArtUpdate( "" ); } -void CoverArtLabel::downloadCover() +CoverArtLabel::~CoverArtLabel() { - if( p_input ) - { - playlist_t *p_playlist = pl_Hold( p_this ); - playlist_AskForArtEnqueue( p_playlist, p_input ); - pl_Release( p_this ); - } + QList< QAction* > artActions = actions(); + foreach( QAction *act, artActions ) + removeAction( act ); } -void CoverArtLabel::doUpdate() +void CoverArtLabel::showArtUpdate( const QString& url ) { - if( !p_input ) + QPixmap pix; + if( !url.isEmpty() && pix.load( url ) ) { - setPixmap( QPixmap( ":/noart.png" ) ); - QList< QAction* > artActions = actions(); - if( !artActions.isEmpty() ) - foreach( QAction *act, artActions ) - removeAction( act ); - prevArt = ""; + pix = pix.scaled( maximumWidth(), maximumHeight(), + Qt::KeepAspectRatioByExpanding ); } else { - char *psz_meta = input_item_GetArtURL( p_input ); - if( psz_meta && !strncmp( psz_meta, "file://", 7 ) ) - { - QString artUrl = qfu( psz_meta ).replace( "file://", "" ); - if( artUrl != prevArt ) - { - QPixmap pix; - if( pix.load( artUrl ) ) - setPixmap( pix ); - else - { - msg_Dbg( p_this, "Qt could not load image '%s'", - qtu( artUrl ) ); - setPixmap( QPixmap( ":/noart.png" ) ); - } - } - QList< QAction* > artActions = actions(); - if( !artActions.isEmpty() ) - { - foreach( QAction *act, artActions ) - removeAction( act ); - } - prevArt = artUrl; - } - else - { - if( prevArt != "" ) - setPixmap( QPixmap( ":/noart.png" ) ); - prevArt = ""; - QList< QAction* > artActions = actions(); - if( artActions.isEmpty() ) - { - QAction *action = new QAction( qtr( "Download cover art" ), - this ); - addAction( action ); - CONNECT( action, triggered(), - this, downloadCover() ); - } - } - free( psz_meta ); + pix = QPixmap( ":/noart.png" ); } + setPixmap( pix ); } -TimeLabel::TimeLabel( intf_thread_t *_p_intf ) :QLabel(), p_intf( _p_intf ) +void CoverArtLabel::askForUpdate() { - b_remainingTime = false; - setText( " --:--/--:-- " ); - setAlignment( Qt::AlignRight | Qt::AlignVCenter ); - setToolTip( qtr( "Toggle between elapsed and remaining time" ) ); - + THEMIM->getIM()->requestArtUpdate(); +} -/* CONNECT( THEMIM->getIM(), statusChanged( int ), - this, setStatus( int ) ); Remove */ - CONNECT( THEMIM->getIM(), positionUpdated( float, int, int ), - this, setDisplayPosition( float, int, int ) ); +TimeLabel::TimeLabel( intf_thread_t *_p_intf ) + : QLabel(), p_intf( _p_intf ), bufTimer( new QTimer(this) ), + buffering( false ), showBuffering(false), bufVal( -1 ) +{ + b_remainingTime = false; + setText( " --:--/--:-- " ); + setAlignment( Qt::AlignRight | Qt::AlignVCenter ); + setToolTip( qtr( "Toggle between elapsed and remaining time" ) ); + bufTimer->setSingleShot( true ); + + CONNECT( THEMIM->getIM(), positionUpdated( float, int64_t, int ), + this, setDisplayPosition( float, int64_t, int ) ); + CONNECT( THEMIM->getIM(), cachingChanged( float ), + this, updateBuffering( float ) ); + CONNECT( bufTimer, timeout(), this, updateBuffering() ); } -void TimeLabel::setDisplayPosition( float pos, int time, int length ) +void TimeLabel::setDisplayPosition( float pos, int64_t t, int length ) { - char psz_length[MSTRTIME_MAX_SIZE], psz_time[MSTRTIME_MAX_SIZE]; + showBuffering = false; + bufTimer->stop(); + + if( pos == -1.f ) + { + setText( " --:--/--:-- " ); + return; + } + + int time = t / 1000000; + secstotimestr( psz_length, length ); secstotimestr( psz_time, ( b_remainingTime && length ) ? length - time : time ); QString timestr; - timestr.sprintf( "%s/%s", psz_time, - ( !length && time ) ? "--:--" : psz_length ); + timestr.sprintf( " %s%s/%s ", (b_remainingTime && length) ? "-" : "", + psz_time, ( !length && time ) ? "--:--" : psz_length ); + + setText( timestr ); + + cachedLength = length; +} + +void TimeLabel::setDisplayPosition( float pos ) +{ + if( pos == -1.f || cachedLength == 0 ) + { + setText( " --:--/--:-- " ); + return; + } + + int time = pos * cachedLength; + 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 ); - /* Add a minus to remaining time*/ - if( b_remainingTime && length ) setText( " -"+timestr+" " ); - else setText( " "+timestr+" " ); + setText( timestr ); } + void TimeLabel::toggleTimeDisplay() { b_remainingTime = !b_remainingTime; } -/* This is wrong remove */ -void TimeLabel::setStatus( int i_status ) -{ - msg_Warn( p_intf, "Status: %i", i_status ); - if( i_status == OPENING_S ) - setText( "Buffering" ); + +void TimeLabel::updateBuffering( float _buffered ) +{ + bufVal = _buffered; + if( !buffering || bufVal == 0 ) + { + showBuffering = false; + buffering = true; + bufTimer->start(200); + } + else if( bufVal == 1 ) + { + showBuffering = buffering = false; + bufTimer->stop(); + } + update(); } +void TimeLabel::updateBuffering() +{ + showBuffering = true; + update(); +} +void TimeLabel::paintEvent( QPaintEvent* event ) +{ + if( showBuffering ) + { + QRect r( rect() ); + r.setLeft( r.width() * bufVal ); + QPainter p( this ); + p.setOpacity( 0.4 ); + p.fillRect( r, palette().color( QPalette::Highlight ) ); + } + QLabel::paintEvent( event ); +}