1 /*****************************************************************************
2 * interface_widgets.cpp : Custom widgets for the main interface
3 ****************************************************************************
4 * Copyright (C) 2006-2010 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 *****************************************************************************/
32 #include "components/interface_widgets.hpp"
33 #include "dialogs_provider.hpp"
34 #include "util/customwidgets.hpp" // qtEventToVLCKey, QVLCStackedWidget
36 #include "menus.hpp" /* Popup menu on bgWidget */
39 #include <QToolButton>
42 #include <QResizeEvent>
45 #include <QWidgetAction>
46 #include <QDesktopWidget>
53 #if defined (QT5_HAS_X11)
54 # include <X11/Xlib.h>
56 # if defined(QT5_HAS_XCB)
57 # include <xcb/xproto.h>
60 #ifdef QT5_HAS_WAYLAND
67 #include <qpa/qplatformnativeinterface.h>
74 #include <vlc_vout_window.h>
76 /**********************************************************************
77 * Video Widget. A simple frame on which video is drawn
78 * This class handles resize issues
79 **********************************************************************/
81 VideoWidget::VideoWidget( intf_thread_t
*_p_i
, QWidget
* p_parent
)
82 : QFrame( p_parent
) , p_intf( _p_i
)
84 /* Set the policy to expand in both directions */
85 // setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
87 layout
= new QHBoxLayout( this );
88 layout
->setContentsMargins( 0, 0, 0, 0 );
94 VideoWidget::~VideoWidget()
96 /* Ensure we are not leaking the video output. This would crash. */
101 void VideoWidget::sync( void )
103 /* Make sure the X server has processed all requests.
104 * This protects other threads using distinct connections from getting
105 * the video widget window in an inconsistent states. */
107 if( QX11Info::isPlatformX11() )
108 XSync( QX11Info::display(), False
);
113 * Request the video to avoid the conflicts
115 bool VideoWidget::request( struct vout_window_t
*p_wnd
)
119 msg_Dbg( p_intf
, "embedded video already in use" );
124 /* The owner of the video window needs a stable handle (WinId). Reparenting
125 * in Qt4-X11 changes the WinId of the widget, so we need to create another
126 * dummy widget that stays within the reparentable widget. */
127 stable
= new QWidget();
128 stable
->setContextMenuPolicy( Qt::PreventContextMenu
);
129 QPalette plt
= palette();
130 plt
.setColor( QPalette::Window
, Qt::black
);
131 stable
->setPalette( plt
);
132 stable
->setAutoFillBackground(true);
133 /* Force the widget to be native so that it gets a winId() */
134 stable
->setAttribute( Qt::WA_NativeWindow
, true );
135 /* Indicates that the widget wants to draw directly onto the screen.
136 Widgets with this attribute set do not participate in composition
138 /* This is currently disabled on X11 as it does not seem to improve
139 * performance, but causes the video widget to be transparent... */
140 #if !defined (QT5_HAS_X11)
141 stable
->setAttribute( Qt::WA_PaintOnScreen
, true );
143 stable
->setMouseTracking( true );
144 setMouseTracking( true );
146 layout
->addWidget( stable
);
151 p_wnd
->type
= p_intf
->p_sys
->voutWindowType
;
152 switch( p_wnd
->type
)
154 case VOUT_WINDOW_TYPE_XID
:
155 p_wnd
->handle
.xid
= stable
->winId();
156 p_wnd
->display
.x11
= NULL
;
158 case VOUT_WINDOW_TYPE_HWND
:
159 p_wnd
->handle
.hwnd
= (void *)stable
->winId();
161 case VOUT_WINDOW_TYPE_NSOBJECT
:
162 p_wnd
->handle
.nsobject
= (void *)stable
->winId();
164 #ifdef QT5_HAS_WAYLAND
165 case VOUT_WINDOW_TYPE_WAYLAND
:
167 /* Ensure only the video widget is native (needed for Wayland) */
168 stable
->setAttribute( Qt::WA_DontCreateNativeAncestors
, true);
170 QWindow
*window
= stable
->windowHandle();
171 assert(window
!= NULL
);
174 QPlatformNativeInterface
*qni
= qApp
->platformNativeInterface();
177 p_wnd
->handle
.wl
= static_cast<wl_surface
*>(
178 qni
->nativeResourceForWindow(QByteArrayLiteral("surface"),
180 p_wnd
->display
.wl
= static_cast<wl_display
*>(
181 qni
->nativeResourceForIntegration(QByteArrayLiteral("wl_display")));
186 vlc_assert_unreachable();
191 QSize
VideoWidget::physicalSize() const
194 if ( QX11Info::isPlatformX11() )
196 Display
*p_x_display
= QX11Info::display();
197 Window x_window
= stable
->winId();
198 XWindowAttributes x_attributes
;
200 XGetWindowAttributes( p_x_display
, x_window
, &x_attributes
);
202 return QSize( x_attributes
.width
, x_attributes
.height
);
209 QWindow
*window
= windowHandle();
210 hwnd
= static_cast<HWND
>(QGuiApplication::platformNativeInterface()->nativeResourceForWindow("handle", window
));
212 GetClientRect(hwnd
, &rect
);
214 return QSize( rect
.right
, rect
.bottom
);
217 QSize current_size
= size();
220 /* Android-like scaling */
221 current_size
*= devicePixelRatioF();
223 /* OSX-like scaling */
224 current_size
*= devicePixelRatio();
230 void VideoWidget::reportSize()
235 QSize size
= physicalSize();
236 vout_window_ReportSize( p_window
, size
.width(), size
.height() );
239 /* Set the Widget to the correct Size */
240 /* Function has to be called by the parent
241 Parent has to care about resizing itself */
242 void VideoWidget::setSize( unsigned int w
, unsigned int h
)
244 /* If the size changed, resizeEvent will be called, otherwise not,
245 * in which case we need to tell the vout what the size actually is
247 if( (unsigned)size().width() == w
&& (unsigned)size().height() == h
)
254 emit
sizeChanged( w
, h
);
255 /* Work-around a bug?misconception? that would happen when vout core resize
256 twice to the same size and would make the vout not centered.
257 This cause a small flicker.
260 if( (unsigned)size().width() == w
&& (unsigned)size().height() == h
)
265 bool VideoWidget::nativeEvent( const QByteArray
& eventType
, void* message
, long* )
267 #if defined(QT5_HAS_X11)
268 # if defined(QT5_HAS_XCB)
269 if ( eventType
== "xcb_generic_event_t" )
271 const xcb_generic_event_t
* xev
= static_cast<const xcb_generic_event_t
*>( message
);
273 if ( xev
->response_type
== XCB_CONFIGURE_NOTIFY
)
279 if ( eventType
== "windows_generic_MSG" )
281 MSG
* msg
= static_cast<MSG
*>( message
);
282 if ( msg
->message
== WM_SIZE
)
286 // Let Qt handle that event in any case
290 void VideoWidget::resizeEvent( QResizeEvent
*event
)
292 QWidget::resizeEvent( event
);
294 if ( p_intf
->p_sys
->voutWindowType
== VOUT_WINDOW_TYPE_XID
||
295 p_intf
->p_sys
->voutWindowType
== VOUT_WINDOW_TYPE_HWND
)
300 int VideoWidget::qtMouseButton2VLC( Qt::MouseButton qtButton
)
302 if( p_window
== NULL
)
308 case Qt::RightButton
:
310 case Qt::MiddleButton
:
317 void VideoWidget::mouseReleaseEvent( QMouseEvent
*event
)
319 int vlc_button
= qtMouseButton2VLC( event
->button() );
320 if( vlc_button
>= 0 )
322 vout_window_ReportMouseReleased( p_window
, vlc_button
);
329 void VideoWidget::mousePressEvent( QMouseEvent
* event
)
331 int vlc_button
= qtMouseButton2VLC( event
->button() );
332 if( vlc_button
>= 0 )
334 vout_window_ReportMousePressed( p_window
, vlc_button
);
341 void VideoWidget::mouseMoveEvent( QMouseEvent
*event
)
343 if( p_window
!= NULL
)
345 vout_window_ReportMouseMoved( p_window
, event
->x(), event
->y() );
352 void VideoWidget::mouseDoubleClickEvent( QMouseEvent
*event
)
354 int vlc_button
= qtMouseButton2VLC( event
->button() );
355 if( vlc_button
>= 0 )
357 vout_window_ReportMouseDoubleClick( p_window
, vlc_button
);
365 void VideoWidget::release( void )
367 msg_Dbg( p_intf
, "Video is not needed anymore" );
371 layout
->removeWidget( stable
);
372 stable
->deleteLater();
380 /**********************************************************************
381 * Background Widget. Show a simple image background. Currently,
382 * it's album art if present or cone.
383 **********************************************************************/
385 BackgroundWidget::BackgroundWidget( intf_thread_t
*_p_i
)
386 :QWidget( NULL
), p_intf( _p_i
), b_expandPixmap( false ), b_withart( true )
388 /* A dark background */
389 setAutoFillBackground( true );
390 QPalette plt
= palette();
391 plt
.setColor( QPalette::Active
, QPalette::Window
, Qt::black
);
392 plt
.setColor( QPalette::Inactive
, QPalette::Window
, Qt::black
);
395 /* Init the cone art */
396 defaultArt
= QString( ":/logo/vlc128.png" );
399 /* fade in animator */
400 setProperty( "opacity", 1.0 );
401 fadeAnimation
= new QPropertyAnimation( this, "opacity", this );
402 fadeAnimation
->setDuration( 1000 );
403 fadeAnimation
->setStartValue( 0.0 );
404 fadeAnimation
->setEndValue( 1.0 );
405 fadeAnimation
->setEasingCurve( QEasingCurve::OutSine
);
406 CONNECT( fadeAnimation
, valueChanged( const QVariant
& ),
409 CONNECT( THEMIM
->getIM(), artChanged( QString
),
410 this, updateArt( const QString
& ) );
413 void BackgroundWidget::updateArt( const QString
& url
)
415 if ( !url
.isEmpty() )
418 pixmapUrl
= defaultArt
;
422 void BackgroundWidget::showEvent( QShowEvent
* e
)
425 if ( b_withart
) fadeAnimation
->start();
428 void BackgroundWidget::paintEvent( QPaintEvent
*e
)
432 /* we just want background autofill */
433 QWidget::paintEvent( e
);
437 int i_maxwidth
, i_maxheight
;
438 QPixmap pixmap
= QPixmap( pixmapUrl
);
439 QPainter
painter(this);
442 qreal dpr
= devicePixelRatioF();
444 qreal dpr
= devicePixelRatio();
446 pixmap
.setDevicePixelRatio( dpr
);
448 i_maxwidth
= __MIN( maximumWidth(), width() ) - MARGIN
* 2;
449 i_maxheight
= __MIN( maximumHeight(), height() ) - MARGIN
* 2;
451 painter
.setOpacity( property( "opacity" ).toFloat() );
453 if ( height() > MARGIN
* 2 )
455 /* Scale down the pixmap if the widget is too small */
456 if( pixmap
.width() > i_maxwidth
|| pixmap
.height() > i_maxheight
)
458 pixmap
= pixmap
.scaled( i_maxwidth
* dpr
, i_maxheight
* dpr
,
459 Qt::KeepAspectRatio
, Qt::SmoothTransformation
);
462 if ( b_expandPixmap
&&
463 pixmap
.width() < width() && pixmap
.height() < height() )
465 pixmap
= pixmap
.scaled(
466 (width() - MARGIN
* 2) * dpr
,
467 (height() - MARGIN
* 2) * dpr
,
468 Qt::KeepAspectRatio
, Qt::SmoothTransformation
);
472 pixmap
= pixmap
.scaled( pixmap
.width() * dpr
, pixmap
.height() * dpr
,
473 Qt::KeepAspectRatio
, Qt::SmoothTransformation
);
477 MARGIN
+ ( i_maxwidth
- ( pixmap
.width() / dpr
) ) / 2,
478 MARGIN
+ ( i_maxheight
- ( pixmap
.height() / dpr
) ) / 2,
481 QWidget::paintEvent( e
);
484 void BackgroundWidget::contextMenuEvent( QContextMenuEvent
*event
)
486 THEDP
->setPopupMenu();
490 EasterEggBackgroundWidget::EasterEggBackgroundWidget( intf_thread_t
*p_intf
)
491 : BackgroundWidget( p_intf
)
493 flakes
= new QLinkedList
<flake
*>();
497 timer
= new QTimer( this );
498 timer
->setInterval( 100 );
499 CONNECT( timer
, timeout(), this, spawnFlakes() );
500 if ( isVisible() && b_enabled
) timer
->start();
501 defaultArt
= QString( ":/logo/vlc128-xmas.png" );
505 EasterEggBackgroundWidget::~EasterEggBackgroundWidget()
513 void EasterEggBackgroundWidget::showEvent( QShowEvent
*e
)
515 if ( b_enabled
) timer
->start();
516 BackgroundWidget::showEvent( e
);
519 void EasterEggBackgroundWidget::hideEvent( QHideEvent
*e
)
523 BackgroundWidget::hideEvent( e
);
526 void EasterEggBackgroundWidget::resizeEvent( QResizeEvent
*e
)
529 BackgroundWidget::resizeEvent( e
);
532 void EasterEggBackgroundWidget::animate()
535 if ( isVisible() ) timer
->start();
538 void EasterEggBackgroundWidget::spawnFlakes()
540 if ( ! isVisible() ) return;
542 double w
= (double) width() / RAND_MAX
;
544 int i_spawn
= ( (double) qrand() / RAND_MAX
) * i_rate
;
546 QLinkedList
<flake
*>::iterator it
= flakes
->begin();
547 while( it
!= flakes
->end() )
549 flake
*current
= *it
;
550 current
->point
.setY( current
->point
.y() + i_speed
);
551 if ( current
->point
.y() + i_speed
>= height() )
554 it
= flakes
->erase( it
);
560 if ( flakes
->size() < MAX_FLAKES
)
561 for ( int i
=0; i
<i_spawn
; i
++ )
563 flake
*f
= new flake
;
564 f
->point
.setX( qrand() * w
);
565 f
->b_fat
= ( qrand() < ( RAND_MAX
* .33 ) );
571 void EasterEggBackgroundWidget::reset()
573 while ( !flakes
->isEmpty() )
574 delete flakes
->takeFirst();
577 void EasterEggBackgroundWidget::paintEvent( QPaintEvent
*e
)
579 QPainter
painter(this);
581 painter
.setBrush( QBrush( QColor(Qt::white
) ) );
582 painter
.setPen( QPen(Qt::white
) );
584 QLinkedList
<flake
*>::const_iterator it
= flakes
->constBegin();
585 while( it
!= flakes
->constEnd() )
587 const flake
* const f
= *(it
++);
591 painter
.drawPoint( f
->point
.x(), f
->point
.y() -1 );
592 painter
.drawPoint( f
->point
.x() + 1, f
->point
.y() );
593 painter
.drawPoint( f
->point
.x(), f
->point
.y() +1 );
594 painter
.drawPoint( f
->point
.x() - 1, f
->point
.y() );
598 painter
.drawPoint( f
->point
);
602 BackgroundWidget::paintEvent( e
);
605 SpeedLabel::SpeedLabel( intf_thread_t
*_p_intf
, QWidget
*parent
)
606 : QLabel( parent
), p_intf( _p_intf
)
608 tooltipStringPattern
= qtr( "Current playback speed: %1\nClick to adjust" );
610 /* Create the Speed Control Widget */
611 speedControl
= new SpeedControlWidget( p_intf
, this );
612 speedControlMenu
= new QMenu( this );
614 widgetAction
= new QWidgetAction( speedControl
);
615 widgetAction
->setDefaultWidget( speedControl
);
616 speedControlMenu
->addAction( widgetAction
);
618 /* Change the SpeedRate in the Label */
619 CONNECT( THEMIM
->getIM(), rateChanged( float ), this, setRate( float ) );
621 DCONNECT( THEMIM
, inputChanged( bool ),
622 speedControl
, activateOnState() );
624 setContentsMargins(4, 0, 4, 0);
625 setRate( var_InheritFloat( THEPL
, "rate" ) );
628 SpeedLabel::~SpeedLabel()
630 widgetAction
->setParent( this );
631 delete speedControlMenu
;
634 /****************************************************************************
635 * Small right-click menu for rate control
636 ****************************************************************************/
638 void SpeedLabel::showSpeedMenu( QPoint pos
)
640 speedControlMenu
->exec( QCursor::pos() - pos
641 + QPoint( -70 + width()/2, height() ) );
644 void SpeedLabel::setRate( float rate
)
647 str
.setNum( rate
, 'f', 2 );
650 setToolTip( tooltipStringPattern
.arg( str
) );
651 speedControl
->updateControls( rate
);
654 /**********************************************************************
655 * Speed control widget
656 **********************************************************************/
657 SpeedControlWidget::SpeedControlWidget( intf_thread_t
*_p_i
, QWidget
*_parent
)
658 : QFrame( _parent
), p_intf( _p_i
)
660 QSizePolicy
sizePolicy( QSizePolicy::Fixed
, QSizePolicy::Maximum
);
661 sizePolicy
.setHorizontalStretch( 0 );
662 sizePolicy
.setVerticalStretch( 0 );
664 speedSlider
= new QSlider( this );
665 speedSlider
->setSizePolicy( sizePolicy
);
666 speedSlider
->setMinimumSize( QSize( 140, 20 ) );
667 speedSlider
->setOrientation( Qt::Horizontal
);
668 speedSlider
->setTickPosition( QSlider::TicksBelow
);
670 speedSlider
->setRange( -34, 34 );
671 speedSlider
->setSingleStep( 1 );
672 speedSlider
->setPageStep( 1 );
673 speedSlider
->setTickInterval( 17 );
675 CONNECT( speedSlider
, valueChanged( int ), this, updateRate( int ) );
677 QToolButton
*normalSpeedButton
= new QToolButton( this );
678 normalSpeedButton
->setMaximumSize( QSize( 26, 16 ) );
679 normalSpeedButton
->setAutoRaise( true );
680 normalSpeedButton
->setText( "1x" );
681 normalSpeedButton
->setToolTip( qtr( "Revert to normal play speed" ) );
683 CONNECT( normalSpeedButton
, clicked(), this, resetRate() );
685 QToolButton
*slowerButton
= new QToolButton( this );
686 slowerButton
->setMaximumSize( QSize( 26, 16 ) );
687 slowerButton
->setAutoRaise( true );
688 slowerButton
->setToolTip( tooltipL
[SLOWER_BUTTON
] );
689 slowerButton
->setIcon( QIcon( iconL
[SLOWER_BUTTON
] ) );
690 CONNECT( slowerButton
, clicked(), THEMIM
->getIM(), slower() );
692 QToolButton
*fasterButton
= new QToolButton( this );
693 fasterButton
->setMaximumSize( QSize( 26, 16 ) );
694 fasterButton
->setAutoRaise( true );
695 fasterButton
->setToolTip( tooltipL
[FASTER_BUTTON
] );
696 fasterButton
->setIcon( QIcon( iconL
[FASTER_BUTTON
] ) );
697 CONNECT( fasterButton
, clicked(), THEMIM
->getIM(), faster() );
699 /* spinBox = new QDoubleSpinBox();
700 spinBox->setDecimals( 2 );
701 spinBox->setMaximum( 32 );
702 spinBox->setMinimum( 0.03F );
703 spinBox->setSingleStep( 0.10F );
704 spinBox->setAlignment( Qt::AlignRight );
706 CONNECT( spinBox, valueChanged( double ), this, updateSpinBoxRate( double ) ); */
708 QGridLayout
* speedControlLayout
= new QGridLayout( this );
709 speedControlLayout
->addWidget( speedSlider
, 0, 0, 1, 3 );
710 speedControlLayout
->addWidget( slowerButton
, 1, 0 );
711 speedControlLayout
->addWidget( normalSpeedButton
, 1, 1, 1, 1, Qt::AlignRight
);
712 speedControlLayout
->addWidget( fasterButton
, 1, 2, 1, 1, Qt::AlignRight
);
713 //speedControlLayout->addWidget( spinBox );
714 speedControlLayout
->setContentsMargins( 0, 0, 0, 0 );
715 speedControlLayout
->setSpacing( 0 );
722 void SpeedControlWidget::activateOnState()
724 speedSlider
->setEnabled( THEMIM
->getIM()->hasInput() );
725 //spinBox->setEnabled( THEMIM->getIM()->hasInput() );
728 void SpeedControlWidget::updateControls( float rate
)
730 if( speedSlider
->isSliderDown() )
732 //We don't want to change anything if the user is using the slider
736 double value
= 17 * log( rate
) / log( 2. );
737 int sliderValue
= (int) ( ( value
> 0 ) ? value
+ .5 : value
- .5 );
739 if( sliderValue
< speedSlider
->minimum() )
741 sliderValue
= speedSlider
->minimum();
743 else if( sliderValue
> speedSlider
->maximum() )
745 sliderValue
= speedSlider
->maximum();
747 lastValue
= sliderValue
;
749 speedSlider
->setValue( sliderValue
);
750 //spinBox->setValue( rate );
753 void SpeedControlWidget::updateRate( int sliderValue
)
755 if( sliderValue
== lastValue
)
758 double speed
= pow( 2, (double)sliderValue
/ 17 );
759 int rate
= INPUT_RATE_DEFAULT
/ speed
;
761 THEMIM
->getIM()->setRate(rate
);
762 //spinBox->setValue( var_InheritFloat( THEPL, "rate" ) );
765 void SpeedControlWidget::updateSpinBoxRate( double r
)
767 var_SetFloat( THEPL
, "rate", r
);
770 void SpeedControlWidget::resetRate()
772 THEMIM
->getIM()->setRate( INPUT_RATE_DEFAULT
);
775 CoverArtLabel::CoverArtLabel( QWidget
*parent
, intf_thread_t
*_p_i
)
776 : QLabel( parent
), p_intf( _p_i
), p_item( NULL
)
778 setContextMenuPolicy( Qt::ActionsContextMenu
);
779 CONNECT( THEMIM
->getIM(), artChanged( input_item_t
* ),
780 this, showArtUpdate( input_item_t
* ) );
782 setMinimumHeight( 128 );
783 setMinimumWidth( 128 );
784 setScaledContents( false );
785 setAlignment( Qt::AlignCenter
);
787 QAction
*action
= new QAction( qtr( "Download cover art" ), this );
788 CONNECT( action
, triggered(), this, askForUpdate() );
791 action
= new QAction( qtr( "Add cover art from file" ), this );
792 CONNECT( action
, triggered(), this, setArtFromFile() );
795 p_item
= THEMIM
->currentInputItem();
798 input_item_Hold( p_item
);
799 showArtUpdate( p_item
);
805 CoverArtLabel::~CoverArtLabel()
807 QList
< QAction
* > artActions
= actions();
808 foreach( QAction
*act
, artActions
)
810 if ( p_item
) input_item_Release( p_item
);
813 void CoverArtLabel::setItem( input_item_t
*_p_item
)
815 if ( p_item
) input_item_Release( p_item
);
817 if ( p_item
) input_item_Hold( p_item
);
820 void CoverArtLabel::showArtUpdate( const QString
& url
)
823 if( !url
.isEmpty() && pix
.load( url
) )
825 pix
= pix
.scaled( minimumWidth(), minimumHeight(),
826 Qt::KeepAspectRatioByExpanding
,
827 Qt::SmoothTransformation
);
831 pix
= QPixmap( ":/noart.png" );
836 void CoverArtLabel::showArtUpdate( input_item_t
*_p_item
)
839 if ( _p_item
!= p_item
)
843 if ( _p_item
) url
= THEMIM
->getIM()->decodeArtURL( _p_item
);
844 showArtUpdate( url
);
847 void CoverArtLabel::askForUpdate()
849 THEMIM
->getIM()->requestArtUpdate( p_item
, true );
852 void CoverArtLabel::setArtFromFile()
857 QString filePath
= QFileDialog::getOpenFileName( this, qtr( "Choose Cover Art" ),
858 p_intf
->p_sys
->filepath
, qtr( "Image Files (*.gif *.jpg *.jpeg *.png)" ) );
860 if( filePath
.isEmpty() )
863 QString fileUrl
= QUrl::fromLocalFile( filePath
).toString();
865 THEMIM
->getIM()->setArt( p_item
, fileUrl
);
868 void CoverArtLabel::clear()
873 TimeLabel::TimeLabel( intf_thread_t
*_p_intf
, TimeLabel::Display _displayType
)
874 : ClickableQLabel(), p_intf( _p_intf
), displayType( _displayType
)
876 b_remainingTime
= false;
877 if( _displayType
!= TimeLabel::Elapsed
)
878 b_remainingTime
= getSettings()->value( "MainWindow/ShowRemainingTime", false ).toBool();
879 switch( _displayType
) {
880 case TimeLabel::Elapsed
:
881 setText( " --:-- " );
882 setToolTip( qtr("Elapsed time") );
884 case TimeLabel::Remaining
:
885 setText( " --:-- " );
886 setToolTip( qtr("Total/Remaining time")
888 + qtr("Click to toggle between total and remaining time")
891 case TimeLabel::Both
:
892 setText( " --:--/--:-- " );
893 setToolTip( QString( "- " )
894 + qtr( "Click to toggle between elapsed and remaining time" )
896 + qtr( "Double click to jump to a chosen time position" ) );
899 setAlignment( Qt::AlignRight
| Qt::AlignVCenter
);
901 CONNECT( THEMIM
->getIM(), positionUpdated( float, int64_t, int ),
902 this, setDisplayPosition( float, int64_t, int ) );
904 connect( this, SIGNAL( broadcastRemainingTime( bool ) ),
905 THEMIM
->getIM(), SIGNAL( remainingTimeChanged( bool ) ) );
907 CONNECT( THEMIM
->getIM(), remainingTimeChanged( bool ),
908 this, setRemainingTime( bool ) );
910 setStyleSheet( "QLabel { padding-left: 4px; padding-right: 4px; }" );
913 void TimeLabel::setRemainingTime( bool remainingTime
)
915 b_remainingTime
= remainingTime
;
918 void TimeLabel::setDisplayPosition( float pos
, int64_t t
, int length
)
922 setMinimumSize( QSize( 0, 0 ) );
923 if( displayType
== TimeLabel::Both
)
924 setText( "--:--/--:--" );
930 int time
= t
/ 1000000;
932 secstotimestr( psz_length
, length
);
933 secstotimestr( psz_time
, ( b_remainingTime
&& length
) ? length
- time
936 // compute the minimum size that will be required for the psz_length
937 // and use it to enforce a minimal size to avoid "dancing" widgets
938 QSize
minsize( 0, 0 );
941 QMargins margins
= contentsMargins();
943 fontMetrics().size( 0, QString( psz_length
), 0, 0 ).width(),
946 minsize
+= QSize( margins
.left() + margins
.right() + 8, 0 ); /* +padding */
948 if ( b_remainingTime
)
949 minsize
+= QSize( fontMetrics().size( 0, "-", 0, 0 ).width(), 0 );
952 switch( displayType
)
954 case TimeLabel::Elapsed
:
955 setMinimumSize( minsize
);
956 setText( QString( psz_time
) );
958 case TimeLabel::Remaining
:
959 if( b_remainingTime
)
961 setMinimumSize( minsize
);
962 setText( QString("-") + QString( psz_time
) );
966 setMinimumSize( QSize( 0, 0 ) );
967 setText( QString( psz_length
) );
970 case TimeLabel::Both
:
972 QString timestr
= QString( "%1%2/%3" )
973 .arg( QString( (b_remainingTime
&& length
) ? "-" : "" ) )
974 .arg( QString( psz_time
) )
975 .arg( QString( ( !length
&& time
) ? "--:--" : psz_length
) );
980 cachedLength
= length
;
983 void TimeLabel::setDisplayPosition( float pos
)
985 if( pos
== -1.f
|| cachedLength
== 0 )
987 setText( " --:--/--:-- " );
991 int time
= pos
* cachedLength
;
992 secstotimestr( psz_time
,
993 ( b_remainingTime
&& cachedLength
?
994 cachedLength
- time
: time
) );
995 QString timestr
= QString( "%1%2/%3" )
996 .arg( QString( (b_remainingTime
&& cachedLength
) ? "-" : "" ) )
997 .arg( QString( psz_time
) )
998 .arg( QString( ( !cachedLength
&& time
) ? "--:--" : psz_length
) );
1004 void TimeLabel::toggleTimeDisplay()
1006 b_remainingTime
= !b_remainingTime
;
1007 getSettings()->setValue( "MainWindow/ShowRemainingTime", b_remainingTime
);
1008 emit
broadcastRemainingTime( b_remainingTime
);