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"