File indexing completed on 2024-05-05 04:49:28

0001 /****************************************************************************************
0002  * Copyright (c) 2007 Dan Meltzer <parallelgrapefruit@gmail.com>                        *
0003  *                                                                                      *
0004  * This program is free software; you can redistribute it and/or modify it under        *
0005  * the terms of the GNU General Public License as published by the Free Software        *
0006  * Foundation; either version 2 of the License, or (at your option) any later           *
0007  * version.                                                                             *
0008  *                                                                                      *
0009  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0010  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0011  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0012  *                                                                                      *
0013  * You should have received a copy of the GNU General Public License along with         *
0014  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0015  ****************************************************************************************/
0016 
0017 #include "ProgressWidget.h"
0018 
0019 #include "amarokconfig.h"
0020 #include "core/support/Debug.h"
0021 #include "EngineController.h"
0022 #include "SliderWidget.h"
0023 #include "TimeLabel.h"
0024 #include "amarokurls/AmarokUrl.h"
0025 #include "amarokurls/AmarokUrlHandler.h"
0026 #include "core/meta/Meta.h"
0027 #include "core/meta/support/MetaUtility.h"
0028 #include "core-impl/capabilities/timecode/TimecodeLoadCapability.h"
0029 
0030 #include <KLocalizedString>
0031 
0032 #include <QHBoxLayout>
0033 #include <QMouseEvent>
0034 
0035 ProgressWidget::ProgressWidget( QWidget *parent )
0036         : QWidget( parent )
0037 {
0038     QHBoxLayout *box = new QHBoxLayout( this );
0039     setLayout( box );
0040     box->setMargin( 0 );
0041     box->setSpacing( 4 );
0042 
0043     m_slider = new Amarok::TimeSlider( this );
0044     m_slider->setToolTip( i18n( "Track Progress" ) );
0045     m_slider->setMaximumSize( 600000, 20 );
0046 
0047     m_timeLabelLeft = new TimeLabel( this );
0048 
0049     m_timeLabelRight = new TimeLabel( this );
0050     m_timeLabelRight->setAlignment( Qt::AlignRight );
0051 
0052     updateTimeLabelTooltips();
0053 
0054     m_timeLabelLeft->setShowTime( false );
0055     m_timeLabelLeft->setAlignment( Qt::AlignRight );
0056     m_timeLabelRight->setShowTime( false );
0057     m_timeLabelRight->setAlignment( Qt::AlignLeft );
0058     m_timeLabelLeft->show();
0059     m_timeLabelRight->show();
0060 
0061     box->addSpacing( 3 );
0062     box->addWidget( m_timeLabelLeft );
0063     box->addWidget( m_slider );
0064     box->addWidget( m_timeLabelRight );
0065 
0066     EngineController *engine = The::engineController();
0067 
0068     if( engine->isPaused() )
0069         paused();
0070     else if( engine->isPlaying() )
0071         trackPlaying();
0072     else
0073         stopped();
0074 
0075     connect( engine, &EngineController::stopped,
0076              this, &ProgressWidget::stopped );
0077     connect( engine, &EngineController::paused,
0078              this, &ProgressWidget::paused );
0079     connect( engine, &EngineController::trackPlaying,
0080              this, &ProgressWidget::trackPlaying );
0081     connect( engine, &EngineController::trackLengthChanged,
0082              this, &ProgressWidget::trackLengthChanged );
0083     connect( engine, &EngineController::trackPositionChanged,
0084              this, &ProgressWidget::trackPositionChanged );
0085 
0086     connect( m_slider, &Amarok::TimeSlider::sliderReleased,
0087              engine, &EngineController::seekTo );
0088 
0089     connect( m_slider, &Amarok::TimeSlider::valueChanged,
0090              this, &ProgressWidget::drawTimeDisplay );
0091 
0092     setBackgroundRole( QPalette::BrightText );
0093 
0094     connect ( The::amarokUrlHandler(), &AmarokUrlHandler::timecodesUpdated,
0095               this, &ProgressWidget::redrawBookmarks );
0096     connect ( The::amarokUrlHandler(), &AmarokUrlHandler::timecodeAdded,
0097               this, &ProgressWidget::addBookmarkNoPopup );
0098 }
0099 
0100 void
0101 ProgressWidget::addBookmarkNoPopup( const QString &name, int milliSeconds )
0102 {
0103     addBookmark( name, milliSeconds, false );
0104 }
0105 
0106 void
0107 ProgressWidget::addBookmark( const QString &name, int milliSeconds, bool showPopup )
0108 {
0109     DEBUG_BLOCK
0110     if ( m_slider )
0111         m_slider->drawTriangle( name, milliSeconds, showPopup );
0112 }
0113 
0114 void
0115 ProgressWidget::updateTimeLabelTooltips()
0116 {
0117     TimeLabel *elapsedLabel = AmarokConfig::leftTimeDisplayRemaining() ? m_timeLabelRight : m_timeLabelLeft;
0118     TimeLabel *remainingLabel = AmarokConfig::leftTimeDisplayRemaining() ? m_timeLabelLeft : m_timeLabelRight;
0119 
0120     elapsedLabel->setToolTip( i18n( "The amount of time elapsed in current track" ) );
0121     remainingLabel->setToolTip( i18n( "The amount of time remaining in current track" ) );
0122 }
0123 
0124 void
0125 ProgressWidget::drawTimeDisplay( int ms )  //SLOT
0126 {
0127     if ( !isVisible() )
0128         return;
0129 
0130     const qint64 trackLength = The::engineController()->trackLength();
0131 
0132     //sometimes the engine gives negative position and track length values for streams
0133     //which causes the time sliders to show 'interesting' values like -322:0-35:0-59
0134     int seconds = qMax(0, ms / 1000);
0135     int remainingSeconds = qMax(0, int((trackLength - ms) / 1000));
0136 
0137     QString sSeconds = Meta::secToPrettyTime( seconds );
0138     QString sRemainingSeconds = '-' + Meta::secToPrettyTime( remainingSeconds );
0139 
0140     if( AmarokConfig::leftTimeDisplayRemaining() )
0141     {
0142         m_timeLabelLeft->setText( sRemainingSeconds );
0143         m_timeLabelLeft->setEnabled( remainingSeconds > 0 );
0144 
0145         m_timeLabelRight->setText( sSeconds );
0146         m_timeLabelRight->setEnabled( seconds > 0 );
0147     }
0148     else
0149     {
0150         m_timeLabelRight->setText( sRemainingSeconds );
0151         m_timeLabelRight->setEnabled( remainingSeconds > 0 );
0152 
0153         m_timeLabelLeft->setText( sSeconds );
0154         m_timeLabelLeft->setEnabled( seconds > 0 );
0155     }
0156 }
0157 
0158 void
0159 ProgressWidget::stopped()
0160 {
0161     m_slider->setEnabled( false );
0162     m_slider->setMinimum( 0 ); //needed because setMaximum() calls with bogus values can change minValue
0163     m_slider->setMaximum( 0 );
0164     m_timeLabelLeft->setEnabled( false );
0165     m_timeLabelLeft->setEnabled( false );
0166     m_timeLabelLeft->setShowTime( false );
0167     m_timeLabelRight->setShowTime( false );
0168 
0169     m_currentUrlId.clear();
0170     m_slider->clearTriangles();
0171 }
0172 
0173 void
0174 ProgressWidget::paused()
0175 {
0176     // I am wondering, is there a way that the track can get paused
0177     // directly?
0178     m_timeLabelLeft->setEnabled( true );
0179     m_timeLabelRight->setEnabled( true );
0180 }
0181 
0182 void
0183 ProgressWidget::trackPlaying()
0184 {
0185     m_timeLabelLeft->setEnabled( true );
0186     m_timeLabelLeft->setEnabled( true );
0187     m_timeLabelLeft->setShowTime( true );
0188     m_timeLabelRight->setShowTime( true );
0189 
0190     //in some cases (for streams mostly), we do not get an event for track length changes once
0191     //loading is done, causing maximum() to return 0 at when playback starts. In this case we need
0192     //to make sure that maximum is set correctly or the slider will not move.
0193     trackLengthChanged( The::engineController()->trackLength() );
0194 }
0195 
0196 void
0197 ProgressWidget::trackLengthChanged( qint64 milliseconds )
0198 {
0199     m_slider->setMinimum( 0 );
0200     m_slider->setMaximum( milliseconds );
0201 
0202     const int timeLength = Meta::msToPrettyTime( milliseconds ).length() + 1; // account for - in remaining time
0203     QFontMetrics tFm( m_timeLabelRight->font() );
0204     const int labelSize = tFm.horizontalAdvance(QChar('0')) * timeLength;
0205 
0206     //set the sizes of the labels to the max needed by the length of the track
0207     //this way the progressbar will not change size during playback of a track
0208     m_timeLabelRight->setFixedWidth( labelSize );
0209     m_timeLabelLeft->setFixedWidth( labelSize );
0210 
0211     //get the urlid of the current track as the engine might stop and start several times
0212     //when skipping lst.fm tracks, so we need to know if we are still on the same track...
0213     if ( The::engineController()->currentTrack() )
0214         m_currentUrlId = The::engineController()->currentTrack()->uidUrl();
0215 
0216     redrawBookmarks();
0217 }
0218 
0219 void
0220 ProgressWidget::trackPositionChanged( qint64 position )
0221 {
0222     m_slider->setSliderValue( position );
0223 
0224     // update the enabled state. Phonon determines isSeekable sometimes too late.
0225     m_slider->setEnabled( (m_slider->maximum() > 0) && The::engineController()->isSeekable() );
0226     if ( !m_slider->isEnabled() )
0227         drawTimeDisplay( position );
0228 }
0229 
0230 
0231 void
0232 ProgressWidget::redrawBookmarks( const QString *BookmarkName )
0233 {
0234     DEBUG_BLOCK
0235     m_slider->clearTriangles();
0236     if ( The::engineController()->currentTrack() )
0237     {
0238         Meta::TrackPtr track = The::engineController()->currentTrack();
0239         if ( track->has<Capabilities::TimecodeLoadCapability>() )
0240         {
0241             Capabilities::TimecodeLoadCapability *tcl = track->create<Capabilities::TimecodeLoadCapability>();
0242             BookmarkList list = tcl->loadTimecodes();
0243             debug() << "found " << list.count() << " timecodes on this track";
0244             foreach( AmarokUrlPtr url, list )
0245             {
0246                 if ( url->command() == "play" )
0247                 {
0248 
0249                     if ( url->args().keys().contains( "pos" ) )
0250                     {
0251                         int pos = url->args().value( "pos" ).toDouble() * 1000;
0252                         debug() << "showing timecode: " << url->name() << " at " << pos ;
0253                         addBookmark( url->name(), pos, ( BookmarkName && BookmarkName == url->name() ));
0254                     }
0255                 }
0256             }
0257             delete tcl;
0258         }
0259     }
0260 }
0261 
0262 void ProgressWidget::mousePressEvent(QMouseEvent* e)
0263 {
0264     QWidget* widgetUnderCursor = childAt(e->pos());
0265     if( widgetUnderCursor == m_timeLabelLeft ||
0266         widgetUnderCursor == m_timeLabelRight )
0267     {
0268         // user clicked on one of the time labels, switch display
0269         AmarokConfig::setLeftTimeDisplayRemaining( !AmarokConfig::leftTimeDisplayRemaining() );
0270         drawTimeDisplay( The::engineController()->trackPositionMs() );
0271         updateTimeLabelTooltips();
0272     }
0273 
0274     QWidget::mousePressEvent(e);
0275 }
0276 
0277 QSize ProgressWidget::sizeHint() const
0278 {
0279     //int height = fontMetrics().boundingRect( "123456789:-" ).height();
0280     return QSize( width(), 12 );
0281 }
0282