Marble

TourPlayback.cpp
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2014 Sanjiban Bairagya <[email protected]>
4 //
5 
6 #include "TourPlayback.h"
7 
8 #include <QList>
9 #include <QUrl>
10 #include <QPointer>
11 
12 #include "MarbleDebug.h"
13 #include "MarbleWidget.h"
14 #include "PopupLayer.h"
15 #include "GeoDataPoint.h"
16 #include "GeoDataPlacemark.h"
17 #include "GeoDataPlaylist.h"
18 #include "GeoDataTour.h"
19 #include "GeoDataWait.h"
20 #include "GeoDataFlyTo.h"
21 #include "GeoDataLookAt.h"
22 #include "GeoDataTourControl.h"
23 #include "GeoDataSoundCue.h"
24 #include "GeoDataAnimatedUpdate.h"
25 #include "MarbleModel.h"
26 #include "GeoDataTreeModel.h"
27 #include "PlaybackFlyToItem.h"
28 #include "PlaybackAnimatedUpdateItem.h"
29 #include "PlaybackWaitItem.h"
30 #include "PlaybackTourControlItem.h"
31 #include "PlaybackSoundCueItem.h"
32 #include "SerialTrack.h"
33 #include "SoundTrack.h"
34 #include "AnimatedUpdateTrack.h"
35 
36 namespace Marble
37 {
38 
39 class TourPlaybackPrivate
40 {
41 public:
42  TourPlaybackPrivate();
43  ~TourPlaybackPrivate();
44 
45  GeoDataTour *m_tour;
46  bool m_pause;
47  SerialTrack m_mainTrack;
48  QList<SoundTrack*> m_soundTracks;
49  QList<AnimatedUpdateTrack*> m_animatedUpdateTracks;
50  GeoDataFlyTo m_mapCenter;
51  QPointer<MarbleWidget> m_widget;
52  QUrl m_baseUrl;
53 };
54 
55 TourPlaybackPrivate::TourPlaybackPrivate() :
56  m_tour( nullptr ),
57  m_pause( false ),
58  m_mainTrack(),
59  m_widget( nullptr )
60 {
61  // do nothing
62 }
63 
64 TourPlaybackPrivate::~TourPlaybackPrivate()
65 {
66  qDeleteAll(m_soundTracks);
67  qDeleteAll(m_animatedUpdateTracks);
68 }
69 
70 TourPlayback::TourPlayback(QObject *parent) :
71  QObject(parent),
72  d(new TourPlaybackPrivate())
73 {
74  connect( &d->m_mainTrack, SIGNAL(centerOn(GeoDataCoordinates)), this, SLOT(centerOn(GeoDataCoordinates)) );
75  connect( &d->m_mainTrack, SIGNAL(progressChanged(double)), this, SIGNAL(progressChanged(double)) );
76  connect( &d->m_mainTrack, SIGNAL(finished()), this, SLOT(stopTour()) );
77  connect( &d->m_mainTrack, SIGNAL(itemFinished(int)), this, SLOT(handleFinishedItem(int)) );
78 
79 
80 }
81 
82 TourPlayback::~TourPlayback()
83 {
84  stop();
85  delete d;
86 }
87 
88 void TourPlayback::handleFinishedItem( int index )
89 {
90  emit itemFinished( index );
91 }
92 
93 void TourPlayback::stopTour()
94 {
95  for( SoundTrack* track: d->m_soundTracks ){
96  track->stop();
97  track->setPaused( false );
98  }
99  for( int i = d->m_animatedUpdateTracks.size()-1; i >= 0; i-- ){
100  d->m_animatedUpdateTracks[ i ]->stop();
101  d->m_animatedUpdateTracks[ i ]->setPaused( false );
102  }
103  emit finished();
104 }
105 
106 void TourPlayback::showBalloon( GeoDataPlacemark* placemark )
107 {
108  GeoDataPoint* point = static_cast<GeoDataPoint*>( placemark->geometry() );
109  d->m_widget->popupLayer()->setCoordinates( point->coordinates(), Qt::AlignRight | Qt::AlignVCenter );
110  d->m_widget->popupLayer()->setContent( placemark->description(), d->m_baseUrl );
111  d->m_widget->popupLayer()->setVisible( true );
112  d->m_widget->popupLayer()->setSize(QSizeF(500, 520));
113 }
114 
115 void TourPlayback::hideBalloon()
116 {
117  if( d->m_widget ){
118  d->m_widget->popupLayer()->setVisible( false );
119  }
120 }
121 
122 bool TourPlayback::isPlaying() const
123 {
124  return !d->m_pause;
125 }
126 
127 void TourPlayback::setMarbleWidget(MarbleWidget* widget)
128 {
129  d->m_widget = widget;
130 
131  connect( this, SIGNAL(added(GeoDataContainer*,GeoDataFeature*,int)),
132  d->m_widget->model()->treeModel(), SLOT(addFeature(GeoDataContainer*,GeoDataFeature*,int)) );
133  connect( this, SIGNAL(removed(GeoDataFeature*)),
134  d->m_widget->model()->treeModel(), SLOT(removeFeature(GeoDataFeature*)) );
135  connect( this, SIGNAL(updated(GeoDataFeature*)),
136  d->m_widget->model()->treeModel(), SLOT(updateFeature(GeoDataFeature*)) );
137 }
138 
139 void TourPlayback::setBaseUrl( const QUrl &baseUrl )
140 {
141  d->m_baseUrl = baseUrl;
142 }
143 
144 QUrl TourPlayback::baseUrl() const
145 {
146  return d->m_baseUrl;
147 }
148 
149 void TourPlayback::centerOn( const GeoDataCoordinates &coordinates )
150 {
151  if ( d->m_widget ) {
152  GeoDataLookAt lookat;
153  lookat.setCoordinates( coordinates );
154  lookat.setRange( coordinates.altitude() );
155  d->m_widget->flyTo( lookat, Instant );
156  }
157 }
158 
159 void TourPlayback::setTour(GeoDataTour *tour)
160 {
161  d->m_tour = tour;
162  if ( !d->m_tour ) {
163  clearTracks();
164  return;
165  }
166 
167  updateTracks();
168 }
169 
170 void TourPlayback::play()
171 {
172  d->m_pause = false;
173  GeoDataLookAt* lookat = new GeoDataLookAt( d->m_widget->lookAt() );
174  lookat->setAltitude( lookat->range() );
175  d->m_mapCenter.setView( lookat );
176  d->m_mainTrack.play();
177  for( SoundTrack* track: d->m_soundTracks) {
178  track->play();
179  }
180  for( AnimatedUpdateTrack* track: d->m_animatedUpdateTracks) {
181  track->play();
182  }
183 }
184 
185 void TourPlayback::pause()
186 {
187  d->m_pause = true;
188  d->m_mainTrack.pause();
189  for( SoundTrack* track: d->m_soundTracks) {
190  track->pause();
191  }
192  for( AnimatedUpdateTrack* track: d->m_animatedUpdateTracks) {
193  track->pause();
194  }
195 }
196 
197 void TourPlayback::stop()
198 {
199  d->m_pause = true;
200  d->m_mainTrack.stop();
201  for( SoundTrack* track: d->m_soundTracks) {
202  track->stop();
203  }
204  for( int i = d->m_animatedUpdateTracks.size()-1; i >= 0; i-- ){
205  d->m_animatedUpdateTracks[ i ]->stop();
206  }
207  hideBalloon();
208 }
209 
210 void TourPlayback::seek( double value )
211 {
212  double const offset = qBound( 0.0, value, d->m_mainTrack.duration() );
213  d->m_mainTrack.seek( offset );
214  for( SoundTrack* track: d->m_soundTracks ){
215  track->seek( offset );
216  }
217  for( AnimatedUpdateTrack* track: d->m_animatedUpdateTracks ){
218  track->seek( offset );
219  }
220 }
221 
222 int TourPlayback::mainTrackSize()
223 {
224  return d->m_mainTrack.size();
225 }
226 
227 PlaybackItem* TourPlayback::mainTrackItemAt( int i )
228 {
229  return d->m_mainTrack.at( i );
230 }
231 
232 void TourPlayback::updateTracks()
233 {
234  clearTracks();
235  double delay = 0;
236  for( int i = 0; i < d->m_tour->playlist()->size(); i++){
237  GeoDataTourPrimitive* primitive = d->m_tour->playlist()->primitive( i );
238  if (const auto flyTo = geodata_cast<GeoDataFlyTo>(primitive)){
239  d->m_mainTrack.append( new PlaybackFlyToItem( flyTo ) );
240  delay += flyTo->duration();
241  }
242  else if (const auto wait = geodata_cast<GeoDataWait>(primitive)) {
243  d->m_mainTrack.append( new PlaybackWaitItem( wait ) );
244  delay += wait->duration();
245  }
246  else if (const auto tourControl = geodata_cast<GeoDataTourControl>(primitive)) {
247  d->m_mainTrack.append( new PlaybackTourControlItem( tourControl ) );
248  }
249  else if (const auto soundCue = geodata_cast<GeoDataSoundCue>(primitive)) {
250  PlaybackSoundCueItem *item = new PlaybackSoundCueItem( soundCue );
251  SoundTrack *track = new SoundTrack( item );
252  track->setDelayBeforeTrackStarts( delay );
253  d->m_soundTracks.append( track );
254  }
255  else if (const auto animatedUpdate = geodata_cast<GeoDataAnimatedUpdate>(primitive)) {
256  PlaybackAnimatedUpdateItem *item = new PlaybackAnimatedUpdateItem( animatedUpdate );
257  AnimatedUpdateTrack *track = new AnimatedUpdateTrack( item );
258  track->setDelayBeforeTrackStarts( delay + animatedUpdate->delayedStart() );
259  d->m_animatedUpdateTracks.append( track );
260  connect( track, SIGNAL(balloonHidden()), this, SLOT(hideBalloon()) );
261  connect( track, SIGNAL(balloonShown(GeoDataPlacemark*)), this, SLOT(showBalloon(GeoDataPlacemark*)) );
262  connect( track, SIGNAL(updated(GeoDataFeature*)), this, SIGNAL(updated(GeoDataFeature*)) );
263  connect( track, SIGNAL(added(GeoDataContainer*,GeoDataFeature*,int)), this, SIGNAL(added(GeoDataContainer*,GeoDataFeature*,int)) );
264  connect( track, SIGNAL(removed(const GeoDataFeature*)), this, SIGNAL(removed(const GeoDataFeature*)) );
265  }
266  }
267  Q_ASSERT( d->m_widget );
268  GeoDataLookAt* lookat = new GeoDataLookAt( d->m_widget->lookAt() );
269  lookat->setAltitude( lookat->range() );
270  d->m_mapCenter.setView( lookat );
271  PlaybackFlyToItem* mapCenterItem = new PlaybackFlyToItem( &d->m_mapCenter );
272  PlaybackFlyToItem* before = mapCenterItem;
273  for ( int i=0; i<d->m_mainTrack.size(); ++i ) {
274  PlaybackFlyToItem* item = qobject_cast<PlaybackFlyToItem*>( d->m_mainTrack.at(i) );
275  if ( item ) {
276  item->setBefore( before );
277  before = item;
278  }
279  }
280  PlaybackFlyToItem* next = nullptr;
281  for ( int i=d->m_mainTrack.size()-1; i>=0; --i ) {
282  PlaybackFlyToItem* item = qobject_cast<PlaybackFlyToItem*>( d->m_mainTrack.at(i) );
283  if ( item ) {
284  item->setNext( next );
285  next = item;
286  }
287  }
288 }
289 
290 void TourPlayback::clearTracks()
291 {
292  d->m_mainTrack.clear();
293  qDeleteAll(d->m_soundTracks);
294  qDeleteAll(d->m_animatedUpdateTracks);
295  d->m_soundTracks.clear();
296  d->m_animatedUpdateTracks.clear();
297 }
298 
299 double TourPlayback::duration() const
300 {
301  return d->m_mainTrack.duration();
302 }
303 
304 } // namespace Marble
305 
306 #include "moc_TourPlayback.cpp"
AlignRight
void stop(Ekos::AlignState mode)
Q_SCRIPTABLE Q_NOREPLY void pause()
Binds a QML item to a specific geodetic location in screen coordinates.
@ Instant
Change camera position immediately (no interpolation)
Definition: MarbleGlobal.h:159
QAction * next(const QObject *recvr, const char *slot, QObject *parent)
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Thu May 26 2022 04:07:51 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.