File indexing completed on 2024-04-28 15:16:14

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2014 Sanjiban Bairagya <sanjiban22393@gmail.com>
0004 //
0005 
0006 #include "TourPlayback.h"
0007 
0008 #include <QList>
0009 #include <QUrl>
0010 #include <QPointer>
0011 
0012 #include "MarbleDebug.h"
0013 #include "MarbleWidget.h"
0014 #include "PopupLayer.h"
0015 #include "GeoDataPoint.h"
0016 #include "GeoDataPlacemark.h"
0017 #include "GeoDataPlaylist.h"
0018 #include "GeoDataTour.h"
0019 #include "GeoDataWait.h"
0020 #include "GeoDataFlyTo.h"
0021 #include "GeoDataLookAt.h"
0022 #include "GeoDataTourControl.h"
0023 #include "GeoDataSoundCue.h"
0024 #include "GeoDataAnimatedUpdate.h"
0025 #include "MarbleModel.h"
0026 #include "GeoDataTreeModel.h"
0027 #include "PlaybackFlyToItem.h"
0028 #include "PlaybackAnimatedUpdateItem.h"
0029 #include "PlaybackWaitItem.h"
0030 #include "PlaybackTourControlItem.h"
0031 #include "PlaybackSoundCueItem.h"
0032 #include "SerialTrack.h"
0033 #include "SoundTrack.h"
0034 #include "AnimatedUpdateTrack.h"
0035 
0036 namespace Marble
0037 {
0038 
0039 class TourPlaybackPrivate
0040 {
0041 public:
0042     TourPlaybackPrivate();
0043     ~TourPlaybackPrivate();
0044 
0045     GeoDataTour *m_tour;
0046     bool m_pause;
0047     SerialTrack m_mainTrack;
0048     QList<SoundTrack*> m_soundTracks;
0049     QList<AnimatedUpdateTrack*> m_animatedUpdateTracks;
0050     GeoDataFlyTo m_mapCenter;
0051     QPointer<MarbleWidget> m_widget;
0052     QUrl m_baseUrl;
0053 };
0054 
0055 TourPlaybackPrivate::TourPlaybackPrivate() :
0056     m_tour( nullptr ),
0057     m_pause( false ),
0058     m_mainTrack(),
0059     m_widget( nullptr )
0060 {
0061     // do nothing
0062 }
0063 
0064 TourPlaybackPrivate::~TourPlaybackPrivate()
0065 {
0066     qDeleteAll(m_soundTracks);
0067     qDeleteAll(m_animatedUpdateTracks);
0068 }
0069 
0070 TourPlayback::TourPlayback(QObject *parent) :
0071     QObject(parent),
0072     d(new TourPlaybackPrivate())
0073 {
0074     connect( &d->m_mainTrack, SIGNAL(centerOn(GeoDataCoordinates)), this, SLOT(centerOn(GeoDataCoordinates)) );
0075     connect( &d->m_mainTrack, SIGNAL(progressChanged(double)), this, SIGNAL(progressChanged(double)) );
0076     connect( &d->m_mainTrack, SIGNAL(finished()), this, SLOT(stopTour()) );
0077     connect( &d->m_mainTrack, SIGNAL(itemFinished(int)), this, SLOT(handleFinishedItem(int)) );
0078 
0079 
0080 }
0081 
0082 TourPlayback::~TourPlayback()
0083 {
0084     stop();
0085     delete d;
0086 }
0087 
0088 void TourPlayback::handleFinishedItem( int index )
0089 {
0090     emit itemFinished( index );
0091 }
0092 
0093 void TourPlayback::stopTour()
0094 {
0095     for( SoundTrack* track: d->m_soundTracks ){
0096         track->stop();
0097         track->setPaused( false );
0098     }
0099     for( int i = d->m_animatedUpdateTracks.size()-1; i >= 0; i-- ){
0100         d->m_animatedUpdateTracks[ i ]->stop();
0101         d->m_animatedUpdateTracks[ i ]->setPaused( false );
0102     }
0103     emit finished();
0104 }
0105 
0106 void TourPlayback::showBalloon( GeoDataPlacemark* placemark )
0107 {
0108     GeoDataPoint* point = static_cast<GeoDataPoint*>( placemark->geometry() );
0109     d->m_widget->popupLayer()->setCoordinates( point->coordinates(), Qt::AlignRight | Qt::AlignVCenter );
0110     d->m_widget->popupLayer()->setContent( placemark->description(), d->m_baseUrl );
0111     d->m_widget->popupLayer()->setVisible( true );
0112     d->m_widget->popupLayer()->setSize(QSizeF(500, 520));
0113 }
0114 
0115 void TourPlayback::hideBalloon()
0116 {
0117     if( d->m_widget ){
0118         d->m_widget->popupLayer()->setVisible( false );
0119     }
0120 }
0121 
0122 bool TourPlayback::isPlaying() const
0123 {
0124     return !d->m_pause;
0125 }
0126 
0127 void TourPlayback::setMarbleWidget(MarbleWidget* widget)
0128 {
0129     d->m_widget = widget;
0130 
0131     connect( this, SIGNAL(added(GeoDataContainer*,GeoDataFeature*,int)),
0132                       d->m_widget->model()->treeModel(), SLOT(addFeature(GeoDataContainer*,GeoDataFeature*,int)) );
0133     connect( this, SIGNAL(removed(GeoDataFeature*)),
0134                       d->m_widget->model()->treeModel(), SLOT(removeFeature(GeoDataFeature*)) );
0135     connect( this, SIGNAL(updated(GeoDataFeature*)),
0136                       d->m_widget->model()->treeModel(), SLOT(updateFeature(GeoDataFeature*)) );
0137 }
0138 
0139 void TourPlayback::setBaseUrl( const QUrl &baseUrl )
0140 {
0141     d->m_baseUrl = baseUrl;
0142 }
0143 
0144 QUrl TourPlayback::baseUrl() const
0145 {
0146     return d->m_baseUrl;
0147 }
0148 
0149 void TourPlayback::centerOn( const GeoDataCoordinates &coordinates )
0150 {
0151     if ( d->m_widget ) {
0152         GeoDataLookAt lookat;
0153         lookat.setCoordinates( coordinates );
0154         lookat.setRange( coordinates.altitude() );
0155         d->m_widget->flyTo( lookat, Instant );
0156     }
0157 }
0158 
0159 void TourPlayback::setTour(GeoDataTour *tour)
0160 {
0161     d->m_tour = tour;
0162     if ( !d->m_tour ) {
0163         clearTracks();
0164         return;
0165     }
0166 
0167     updateTracks();
0168 }
0169 
0170 void TourPlayback::play()
0171 {
0172     d->m_pause = false;
0173     GeoDataLookAt* lookat = new GeoDataLookAt( d->m_widget->lookAt() );
0174     lookat->setAltitude( lookat->range() );
0175     d->m_mapCenter.setView( lookat );
0176     d->m_mainTrack.play();
0177     for( SoundTrack* track: d->m_soundTracks) {
0178         track->play();
0179     }
0180     for( AnimatedUpdateTrack* track: d->m_animatedUpdateTracks) {
0181         track->play();
0182     }
0183 }
0184 
0185 void TourPlayback::pause()
0186 {
0187     d->m_pause = true;
0188     d->m_mainTrack.pause();
0189     for( SoundTrack* track: d->m_soundTracks) {
0190         track->pause();
0191     }
0192     for( AnimatedUpdateTrack* track: d->m_animatedUpdateTracks) {
0193         track->pause();
0194     }
0195 }
0196 
0197 void TourPlayback::stop()
0198 {
0199     d->m_pause = true;
0200     d->m_mainTrack.stop();
0201     for( SoundTrack* track: d->m_soundTracks) {
0202         track->stop();
0203     }
0204     for( int i = d->m_animatedUpdateTracks.size()-1; i >= 0; i-- ){
0205         d->m_animatedUpdateTracks[ i ]->stop();
0206     }
0207     hideBalloon();
0208 }
0209 
0210 void TourPlayback::seek( double value )
0211 {
0212     double const offset = qBound( 0.0, value, d->m_mainTrack.duration() );
0213     d->m_mainTrack.seek( offset );
0214     for( SoundTrack* track: d->m_soundTracks ){
0215         track->seek( offset );
0216     }
0217     for( AnimatedUpdateTrack* track: d->m_animatedUpdateTracks ){
0218         track->seek( offset );
0219     }
0220 }
0221 
0222 int TourPlayback::mainTrackSize()
0223 {
0224     return d->m_mainTrack.size();
0225 }
0226 
0227 PlaybackItem* TourPlayback::mainTrackItemAt( int i )
0228 {
0229     return d->m_mainTrack.at( i );
0230 }
0231 
0232 void TourPlayback::updateTracks()
0233 {
0234     clearTracks();
0235     double delay = 0;
0236     for( int i = 0; i < d->m_tour->playlist()->size(); i++){
0237         GeoDataTourPrimitive* primitive = d->m_tour->playlist()->primitive( i );
0238         if (const auto flyTo = geodata_cast<GeoDataFlyTo>(primitive)){
0239             d->m_mainTrack.append( new PlaybackFlyToItem( flyTo ) );
0240             delay += flyTo->duration();
0241         }
0242         else if (const auto wait = geodata_cast<GeoDataWait>(primitive)) {
0243             d->m_mainTrack.append( new PlaybackWaitItem( wait ) );
0244             delay += wait->duration();
0245         }
0246         else if (const auto tourControl = geodata_cast<GeoDataTourControl>(primitive)) {
0247             d->m_mainTrack.append( new PlaybackTourControlItem( tourControl ) );
0248         }
0249         else if (const auto soundCue = geodata_cast<GeoDataSoundCue>(primitive)) {
0250             PlaybackSoundCueItem *item = new PlaybackSoundCueItem( soundCue );
0251             SoundTrack *track = new SoundTrack( item );
0252             track->setDelayBeforeTrackStarts( delay );
0253             d->m_soundTracks.append( track );
0254         }
0255         else if (const auto animatedUpdate = geodata_cast<GeoDataAnimatedUpdate>(primitive)) {
0256             PlaybackAnimatedUpdateItem *item = new PlaybackAnimatedUpdateItem( animatedUpdate );
0257             AnimatedUpdateTrack *track = new AnimatedUpdateTrack( item );
0258             track->setDelayBeforeTrackStarts( delay + animatedUpdate->delayedStart() );
0259             d->m_animatedUpdateTracks.append( track );
0260             connect( track, SIGNAL(balloonHidden()), this, SLOT(hideBalloon()) );
0261             connect( track, SIGNAL(balloonShown(GeoDataPlacemark*)), this, SLOT(showBalloon(GeoDataPlacemark*)) );
0262             connect( track, SIGNAL(updated(GeoDataFeature*)), this, SIGNAL(updated(GeoDataFeature*)) );
0263             connect( track, SIGNAL(added(GeoDataContainer*,GeoDataFeature*,int)), this, SIGNAL(added(GeoDataContainer*,GeoDataFeature*,int)) );
0264             connect( track, SIGNAL(removed(const GeoDataFeature*)), this, SIGNAL(removed(const GeoDataFeature*)) );
0265         }
0266     }
0267     Q_ASSERT( d->m_widget );
0268     GeoDataLookAt* lookat = new GeoDataLookAt( d->m_widget->lookAt() );
0269     lookat->setAltitude( lookat->range() );
0270     d->m_mapCenter.setView( lookat );
0271     PlaybackFlyToItem* mapCenterItem = new PlaybackFlyToItem( &d->m_mapCenter );
0272     PlaybackFlyToItem* before = mapCenterItem;
0273     for ( int i=0; i<d->m_mainTrack.size(); ++i ) {
0274         PlaybackFlyToItem* item = qobject_cast<PlaybackFlyToItem*>( d->m_mainTrack.at(i) );
0275         if ( item ) {
0276             item->setBefore( before );
0277             before = item;
0278         }
0279     }
0280     PlaybackFlyToItem* next = nullptr;
0281     for ( int i=d->m_mainTrack.size()-1; i>=0; --i ) {
0282         PlaybackFlyToItem* item = qobject_cast<PlaybackFlyToItem*>( d->m_mainTrack.at(i) );
0283         if ( item ) {
0284             item->setNext( next );
0285             next = item;
0286         }
0287     }
0288 }
0289 
0290 void TourPlayback::clearTracks()
0291 {
0292     d->m_mainTrack.clear();
0293     qDeleteAll(d->m_soundTracks);
0294     qDeleteAll(d->m_animatedUpdateTracks);
0295     d->m_soundTracks.clear();
0296     d->m_animatedUpdateTracks.clear();
0297 }
0298 
0299 double TourPlayback::duration() const
0300 {
0301     return d->m_mainTrack.duration();
0302 }
0303 
0304 } // namespace Marble
0305 
0306 #include "moc_TourPlayback.cpp"