X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fgui%2Fqt4%2Fcomponents%2Finterface_widgets.cpp;h=d134256af32f0a4c6daae8d51dacbf0df5fded00;hb=9604904129876dde2dfbb8a1fac864d28a2f7235;hp=d78ef8c0e426650234492a95940cf1342e826924;hpb=b95136f6ea88b18b0d05bd8f3b7dc8475ab3f7f9;p=vlc diff --git a/modules/gui/qt4/components/interface_widgets.cpp b/modules/gui/qt4/components/interface_widgets.cpp index d78ef8c0e4..d134256af3 100644 --- a/modules/gui/qt4/components/interface_widgets.cpp +++ b/modules/gui/qt4/components/interface_widgets.cpp @@ -1,7 +1,7 @@ /***************************************************************************** * interface_widgets.cpp : Custom widgets for the main interface **************************************************************************** - * Copyright ( C ) 2006 the VideoLAN team + * Copyright (C) 2006-2008 the VideoLAN team * $Id$ * * Authors: Clément Stenac @@ -28,35 +28,52 @@ # include "config.h" #endif -#include - -#include "dialogs_provider.hpp" #include "components/interface_widgets.hpp" -#include "main_interface.hpp" -#include "input_manager.hpp" -#include "menus.hpp" -#include "util/input_slider.hpp" -#include "util/customwidgets.hpp" + +#include "menus.hpp" /* Popup menu on bgWidget */ + +#include #include -#include -#include -#include #include -#include -#include #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 -#define I_PLAY_TOOLTIP N_("Play\nIf the playlist is empty, open a media") +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 @@ -66,7 +83,7 @@ VideoWidget::VideoWidget( intf_thread_t *_p_i ) : QFrame( NULL ), p_intf( _p_i ) { /* Init */ - p_vout = NULL; + reparentable = NULL; videoSize.rwidth() = -1; videoSize.rheight() = -1; @@ -75,85 +92,192 @@ VideoWidget::VideoWidget( intf_thread_t *_p_i ) : QFrame( NULL ), p_intf( _p_i ) /* 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. */ -#if HAS_QT43 - connect( this, SIGNAL(askVideoWidgetToShow( unsigned int, unsigned int)), - this, SLOT(SetSizing(unsigned int, unsigned int )), - Qt::BlockingQueuedConnection ); -#else -#warning This is broken. Fix it with a QEventLoop with a processEvents () - connect( this, SIGNAL(askVideoWidgetToShow( unsigned int, unsigned int)), - this, SLOT(SetSizing(unsigned int, unsigned int )) ); -#endif -} - -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 ); + + reparentable->setLayout( innerLayout ); + 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(); + 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 = config_GetInt( 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 ); + reparentable->setWindowState( newstate ); + reparentable->setWindowFlags( newflags ); + /* 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; + //layout->removeWidget( reparentable ); + +#ifdef WIN32 + /* Come back to default thumbnail for Windows 7 taskbar */ + LPTASKBARLIST3 p_taskbl; + OSVERSIONINFO winVer; + winVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + if( GetVersionEx(&winVer) && winVer.dwMajorVersion > 5 && winVer.dwMajorVersion > 0 ) + { + 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.rwidth() = 0; videoSize.rheight() = 0; updateGeometry(); hide(); } + QSize VideoWidget::sizeHint() const { return videoSize; @@ -187,18 +311,19 @@ BackgroundWidget::BackgroundWidget( intf_thread_t *_p_i ) label->setMaximumWidth( MAX_BG_SIZE ); label->setMinimumHeight( 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() @@ -212,38 +337,39 @@ 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 +#include +#include + /********************************************************************** * Visualization selector panel **********************************************************************/ @@ -294,26 +420,71 @@ void VisualSelector::next() } #endif +SpeedLabel::SpeedLabel( intf_thread_t *_p_intf, const QString& text, + QWidget *parent ) + : QLabel( text, parent ), p_intf( _p_intf ) +{ + setToolTip( qtr( "Current playback speed.\nClick to adjust" ) ); + + /* Create the Speed Control Widget */ + speedControl = new SpeedControlWidget( p_intf, this ); + speedControlMenu = new QMenu( this ); + + QWidgetAction *widgetAction = new QWidgetAction( speedControl ); + widgetAction->setDefaultWidget( speedControl ); + speedControlMenu->addAction( widgetAction ); + + /* Change the SpeedRate in the Status Bar */ + CONNECT( THEMIM->getIM(), rateChanged( int ), this, setRate( int ) ); + + CONNECT( 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 + + QPoint( 0, height() ) ); +} + +void SpeedLabel::setRate( int rate ) +{ + QString str; + str.setNum( ( 1000 / (double)rate ), 'f', 2 ); + str.append( "x" ); + setText( str ); + setToolTip( str ); + speedControl->updateControls( rate ); +} + /********************************************************************** * Speed control widget **********************************************************************/ -SpeedControlWidget::SpeedControlWidget( intf_thread_t *_p_i ) : - QFrame( NULL ), p_intf( _p_i ) +SpeedControlWidget::SpeedControlWidget( intf_thread_t *_p_i, QWidget *_parent ) + : QFrame( _parent ), p_intf( _p_i ) { QSizePolicy sizePolicy( QSizePolicy::Maximum, QSizePolicy::Fixed ); 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 ); speedSlider->setTickPosition( QSlider::TicksRight ); - speedSlider->setRange( -24, 24 ); + speedSlider->setRange( -34, 34 ); speedSlider->setSingleStep( 1 ); speedSlider->setPageStep( 1 ); - speedSlider->setTickInterval( 12 ); + speedSlider->setTickInterval( 17 ); CONNECT( speedSlider, valueChanged( int ), this, updateRate( int ) ); @@ -325,20 +496,18 @@ SpeedControlWidget::SpeedControlWidget( intf_thread_t *_p_i ) : CONNECT( normalSpeedButton, clicked(), this, resetRate() ); - QVBoxLayout *speedControlLayout = new QVBoxLayout; + QVBoxLayout *speedControlLayout = new QVBoxLayout( this ); speedControlLayout->setLayoutMargins( 4, 4, 4, 4, 4 ); speedControlLayout->setSpacing( 4 ); speedControlLayout->addWidget( speedSlider ); speedControlLayout->addWidget( normalSpeedButton ); - setLayout( speedControlLayout ); -} -SpeedControlWidget::~SpeedControlWidget() -{} + activateOnState(); +} -void SpeedControlWidget::setEnable( bool b_enable ) +void SpeedControlWidget::activateOnState() { - speedSlider->setEnabled( b_enable ); + speedSlider->setEnabled( THEMIM->getIM()->hasInput() ); } void SpeedControlWidget::updateControls( int rate ) @@ -349,7 +518,7 @@ void SpeedControlWidget::updateControls( int rate ) return; } - double value = 12 * log( (double)INPUT_RATE_DEFAULT / rate ) / log( 2 ); + double value = 17 * log( (double)INPUT_RATE_DEFAULT / rate ) / log( 2 ); int sliderValue = (int) ( ( value > 0 ) ? value + .5 : value - .5 ); if( sliderValue < speedSlider->minimum() ) @@ -369,7 +538,7 @@ void SpeedControlWidget::updateControls( int rate ) void SpeedControlWidget::updateRate( int sliderValue ) { - double speed = pow( 2, (double)sliderValue / 12 ); + double speed = pow( 2, (double)sliderValue / 17 ); int rate = INPUT_RATE_DEFAULT / speed; THEMIM->getIM()->setRate(rate); @@ -377,111 +546,55 @@ void SpeedControlWidget::updateRate( int sliderValue ) 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; + THEMIM->getIM()->setRate( INPUT_RATE_DEFAULT ); } -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 ); + + QList< QAction* > artActions = actions(); + QAction *action = new QAction( qtr( "Download cover art" ), this ); + CONNECT( action, triggered(), this, askForUpdate() ); + addAction( action ); - doUpdate(); + 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 ); +} + +void CoverArtLabel::askForUpdate() +{ + THEMIM->getIM()->requestArtUpdate(); } TimeLabel::TimeLabel( intf_thread_t *_p_intf ) :QLabel(), p_intf( _p_intf ) @@ -492,12 +605,20 @@ TimeLabel::TimeLabel( intf_thread_t *_p_intf ) :QLabel(), p_intf( _p_intf ) setToolTip( qtr( "Toggle between elapsed and remaining time" ) ); + CONNECT( THEMIM->getIM(), cachingChanged( float ), + this, setCaching( float ) ); CONNECT( THEMIM->getIM(), positionUpdated( float, int, int ), this, setDisplayPosition( float, int, int ) ); } void TimeLabel::setDisplayPosition( float pos, int time, int length ) { + if( pos == -1.f ) + { + setText( " --:--/--:-- " ); + return; + } + char psz_length[MSTRTIME_MAX_SIZE], psz_time[MSTRTIME_MAX_SIZE]; secstotimestr( psz_length, length ); secstotimestr( psz_time, ( b_remainingTime && length ) ? length - time @@ -517,16 +638,12 @@ void TimeLabel::toggleTimeDisplay() b_remainingTime = !b_remainingTime; } -bool VolumeClickHandler::eventFilter( QObject *obj, QEvent *e ) +void TimeLabel::setCaching( float f_cache ) { - if (e->type() == QEvent::MouseButtonPress ) - { - aout_VolumeMute( p_intf, NULL ); - audio_volume_t i_volume; - aout_VolumeGet( p_intf, &i_volume ); -// m->updateVolume( i_volume * VOLUME_MAX / (AOUT_VOLUME_MAX/2) ); - return true; - } - return false; + QString amount; + amount.setNum( (int)(100 * f_cache) ); + msg_Dbg( p_intf, "New caching: %d", (int)(100*f_cache)); + setText( "Buff: " + amount + "%" ); } +