Audacity 3.2.0
PendingTracks.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 @file PendingTracks.cpp
6
7 Paul Licameli
8
9 **********************************************************************/
10#include "PendingTracks.h"
11#include "Project.h"
12#include "Track.h"
13
17 return std::make_shared<PendingTracks>(project);
18 }
19};
20
22{
23 return project.AttachedObjects::Get<PendingTracks>(sPendingTracksKey);
24}
25
27{
28 return Get(const_cast<AudacityProject &>(project));
29}
30
32 : mTracks{ TrackList::Get(project) }
33 , mTrackListSubscription { mTracks.Subscribe(
34 [this](const TrackListEvent &event){
35 switch (event.mType) {
41 break;
42 default:
43 break;
44 }
45 // Pass along to downstream listeners
46 Publish(event);
47 })}
48 , mPendingUpdates{ TrackList::Temporary(mTracks.GetOwner()) }
49{}
50
52
54{
55 mTracks.Append(std::move(list), false);
56}
57
58std::pair<Track *, Channel *>
60 Track &track, size_t channelIndex) const
61{
62 // Linear search. Tracks in a project are usually very few.
63 auto pTrack = &track;
64 if (!mPendingUpdates->empty()) {
65 const auto end = mPendingUpdates->end();
66 // Find the shadow track with the id
67 const auto pred = [id = track.GetId()](const auto &pTrack){
68 return pTrack->GetId() == id; };
69 if (const auto it = std::find_if(mPendingUpdates->begin(), end, pred)
70 ; it != end)
71 {
72 pTrack = *it;
73 // Find the correct corresponding channel
74 const auto &channels = pTrack->Channels();
75 const auto size = channels.size();
76 // This should be provable from how RegisterPendingChangedTrack
77 // constructs the substitutes
78 assert(channelIndex < size);
79 auto channelIter = channels.begin();
80 std::advance(channelIter, std::min<int>(channelIndex, size - 1));
81 return { pTrack, (*channelIter).get() };
82 }
83 }
84 return {};
85}
86
88{
89 const auto pTrack = dynamic_cast<Track *>(&channel.GetChannelGroup());
90 if (!pTrack)
91 return channel;
92 const auto index = channel.GetChannelIndex();
93 auto [_, pChannel] = DoSubstitutePendingChangedChannel(*pTrack, index);
94 return pChannel ? *pChannel : channel;
95}
96
97const Channel &
99{
100 return SubstitutePendingChangedChannel(const_cast<Channel&>(channel));
101}
102
104{
105 auto [pTrack, _] = DoSubstitutePendingChangedChannel(track, 0);
106 return pTrack ? *pTrack : track;
107}
108
110const
111{
112 return SubstitutePendingChangedTrack(const_cast<Track&>(track));
113}
114
115std::pair<const Track *, const Channel*>
117 const Track &track, size_t channelIndex) const
118{
119 auto pTrack = &track;
120 if (!mPendingUpdates->empty()) {
121 const auto end = mPendingUpdates->end();
122 // Find the shadow track with the id
123 const auto pred = [id = track.GetId()](const auto &pTrack){
124 return pTrack->GetId() == id; };
125 if (const auto it =
126 std::find_if(mPendingUpdates->begin(), end, pred); it != end)
127 {
128 const auto end2 = mTracks.end();
129 // Find the original track with the id
130 if (const auto it2 = std::find_if(mTracks.begin(), end2, pred)
131 ; it2 != end2)
132 {
133 pTrack = *it2;
134 // Find the correct corresponding channel
135 const auto &channels = pTrack->Channels();
136 const auto size = channels.size();
137 // This should be provable from how RegisterPendingChangedTrack
138 // constructs the substitutes
139 assert(channelIndex < size);
140
141 auto channelIter = channels.begin();
142 std::advance(channelIter, std::min<int>(channelIndex, size - 1));
143 return { pTrack, (*channelIter).get() };
144 }
145 }
146 }
147 return {};
148}
149
151const
152{
153 const auto pTrack =
154 dynamic_cast<const Track *>(&channel.GetChannelGroup());
155 if (!pTrack)
156 return channel;
157 const auto index = channel.GetChannelIndex();
158 const auto [_, pChannel] = DoSubstituteOriginalChannel(*pTrack, index);
159 return pChannel ? *pChannel : channel;
160}
161
163{
164 const auto [pTrack, _] = DoSubstituteOriginalChannel(track, 0);
165 return pTrack ? *pTrack : track;
166}
167
169{
170 auto track =
172
173 mUpdaters.push_back(move(updater));
174 mPendingUpdates->Add(track);
175 return track.get();
176}
177
179{
180 if (mPendingUpdates->empty())
181 return;
182 auto pUpdater = mUpdaters.begin();
183 for (const auto &pendingTrack : *mPendingUpdates) {
184 auto src = mTracks.FindById(pendingTrack->GetId());
185 // Copy just a part of the track state, according to the update
186 // function
187 const auto &updater = *pUpdater;
188 if (pendingTrack && src) {
189 if (updater)
190 updater(*pendingTrack, *src);
191 }
192 ++pUpdater;
193 }
194}
195
198 std::vector<std::shared_ptr<Track>> *pAdded)
199{
200 mUpdaters.clear();
201 mPendingUpdates->Clear();
202
203 if (pAdded)
204 pAdded->clear();
205
206 auto [it, end] = mTracks.Any();
207 while (it != end) {
208 const auto pTrack = *it;
209 ++it;
210 if (pTrack->GetId() == TrackId{}) {
211 if (pAdded)
212 pAdded->emplace_back(mTracks.Remove(*pTrack));
213 }
214 else {
215 if (pAdded)
216 pAdded->push_back(nullptr);
217 }
218 }
219
220 if (pAdded)
221 // Remove trailing nulls
222 while (!pAdded->empty() && !pAdded->back())
223 pAdded->pop_back();
224}
225
228{
229 std::vector<std::shared_ptr<Track>> additions;
230 auto updated = TrackList::Temporary(mTracks.GetOwner());
231 {
232 // Always clear, even if one of the update functions throws
233 Finally Do{[&]{ ClearPendingTracks(&additions); }};
235 // Clear updaters before any more track list events are processed
236 mUpdaters.clear();
237 updated.swap(mPendingUpdates);
238 }
239
240 bool result = false;
241
242 // Remaining steps must be No-fail-guarantee so that this function
243 // gives Strong-guarantee
244
245 std::vector<std::shared_ptr<Track>> reinstated;
246
247 for (const auto pendingTrack : *updated)
248 pendingTrack->ReparentAllAttachments();
249
250 while (!updated->empty()) {
251 auto iter = updated->begin();
252 auto pendingTrack = *iter;
253 auto src = mTracks.FindById(pendingTrack->GetId());
254 if (src) {
255 mTracks.ReplaceOne(*src, std::move(*updated));
256 result = true;
257 }
258 else {
259 // Perhaps a track marked for pending changes got deleted by
260 // some other action. Recreate it so we don't lose the
261 // accumulated changes.
262 reinstated.push_back(pendingTrack->SharedPointer());
263 updated->Remove(*pendingTrack);
264 }
265 }
266
267 // If there are tracks to reinstate, append them to the list.
268 for (auto &pendingTrack : reinstated)
269 if (pendingTrack)
270 mTracks.Add(move(pendingTrack)), result = true;
271
272 // Put the pending added tracks back into the list, preserving their
273 // positions and assigning ids.
274 auto iter = mTracks.begin();
275 for (auto &pendingTrack : additions) {
276 auto next = iter;
277 ++next;
278 if (pendingTrack)
279 // This emits appropriate track list events
280 mTracks.Insert(*iter, pendingTrack, true);
281 else
282 assert(iter != mTracks.end()); // Deduce that from ClearPendingTrack
283 iter = next;
284 }
285
286 return result;
287}
288
290{
291 if (!mPendingUpdates->empty())
292 return true;
293 const auto end = mTracks.end();
294 return (end != std::find_if(mTracks.begin(), end, [](const Track *t){
295 return t->GetId() == TrackId{};
296 }));
297}
#define _(s)
Definition: Internat.h:73
static const AudacityProject::AttachedObjects::RegisteredFactory sPendingTracksKey
const auto project
declares abstract base class Track, TrackList, and iterators over TrackList
int id
static CustomUpdaterValue updater
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
ChannelGroup & GetChannelGroup()
Channel object's lifetime is assumed to be nested in its Track's.
Definition: Channel.cpp:43
size_t GetChannelIndex() const
Definition: Channel.cpp:25
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Definition: ClientData.h:275
CallbackReturn Publish(const TrackListEvent &message)
Send a message to connected callbacks.
Definition: Observer.h:207
bool HasPendingTracks() const
Track * RegisterPendingChangedTrack(Updater updater, Track *src)
Start a deferred update of the project.
std::pair< Track *, Channel * > DoSubstitutePendingChangedChannel(Track &track, size_t channelIndex) const
TrackList & mTracks
const Track & SubstituteOriginalTrack(const Track &track) const
Track & SubstitutePendingChangedTrack(Track &track) const
std::shared_ptr< TrackList > mPendingUpdates
std::vector< Updater > mUpdaters
static PendingTracks & Get(AudacityProject &project)
PendingTracks(AudacityProject &project)
void UpdatePendingTracks()
Channel & SubstitutePendingChangedChannel(Channel &channel) const
std::function< void(Track &dest, const Track &src)> Updater
Definition: PendingTracks.h:67
void ClearPendingTracks(std::vector< std::shared_ptr< Track > > *pAdded=nullptr)
Forget pending track additions and changes;.
void RegisterPendingNewTracks(TrackList &&list)
bool ApplyPendingTracks()
Change the state of the project.
std::pair< const Track *, const Channel * > DoSubstituteOriginalChannel(const Track &track, size_t channelIndex) const
const Channel & SubstituteOriginalChannel(const Channel &channel) const
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:110
TrackId GetId() const
Definition: Track.h:136
virtual Holder Duplicate(DuplicateOptions={}) const
public nonvirtual duplication function that invokes Clone()
Definition: Track.cpp:109
An in-session identifier of track objects across undo states. It does not persist between sessions.
Definition: Track.h:79
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
Definition: Track.h:850
void Insert(const Track *before, const Track::Holder &pSrc, bool assignIds=false)
Moves *pSrc to position where before is located. If before is nullptr the track is appended.
Definition: Track.cpp:478
iterator end()
Definition: Track.h:906
void Append(TrackList &&list, bool assignIds=true)
Remove all tracks from list and put them at the end of this
Definition: Track.cpp:870
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:950
Track::Holder ReplaceOne(Track &t, TrackList &&with)
Definition: Track.cpp:560
iterator begin()
Definition: Track.h:905
Track::Holder Remove(Track &track)
Remove a track and return it.
Definition: Track.cpp:584
Track * FindById(TrackId id)
Definition: Track.cpp:517
AudacityProject * GetOwner()
Definition: Track.h:887
TrackKind * Add(const std::shared_ptr< TrackKind > &t, bool assignIds=true)
Definition: Track.h:1048
static TrackListHolder Temporary(AudacityProject *pProject, const Track::Holder &pTrack={})
Definition: Track.cpp:858
Services * Get()
Fetch the global instance, or nullptr if none is yet installed.
Definition: BasicUI.cpp:201
std::optional< LogWindowUpdater > pUpdater
Definition: LogWindow.cpp:53
const char * end(const char *str) noexcept
Definition: StringUtils.h:106
"finally" as in The C++ Programming Language, 4th ed., p. 358 Useful for defining ad-hoc RAII actions...
Definition: MemoryX.h:175
Choices when duplicating a track.
Definition: Track.h:271
DuplicateOptions ShallowCopyAttachments() &&
Definition: Track.h:284
Notification of changes in individual tracks of TrackList, or of TrackList's composition.
Definition: Track.h:803
@ RESIZING
Posted when some track changed its height.
Definition: Track.h:816
@ DELETION
Posted when a track has been deleted from a tracklist. Also posted when one track replaces another.
Definition: Track.h:825
@ ADDITION
Posted when a track has been added to a tracklist. Also posted when one track replaces another.
Definition: Track.h:819
@ PERMUTED
Posted when tracks are reordered but otherwise unchanged.
Definition: Track.h:813