Audacity 3.2.0
UndoManager.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 UndoManager.cpp
6
7 Dominic Mazzoni
8
9*******************************************************************//****************************************************************//*******************************************************************/
21
22
23
24#include "UndoManager.h"
25
26#include <wx/hashset.h>
27
28#include "BasicUI.h"
29#include "Project.h"
30#include "Track.h"
31#include "TransactionScope.h"
32//#include "NoteTrack.h" // for Sonify* function declarations
33
34
36
37namespace {
38 using Savers = std::vector<UndoRedoExtensionRegistry::Saver>;
39 static Savers &GetSavers()
40 {
41 static Savers theSavers;
42 return theSavers;
43 }
44
46 {
48 for (auto &saver : GetSavers())
49 if (saver)
50 result.emplace_back(saver(project));
51 return result;
52 }
53}
54
56{
57 GetSavers().emplace_back(saver);
58}
59
60using SampleBlockID = long long;
61
63 [](AudacityProject &project)
64 { return std::make_unique<UndoManager>( project ); }
65};
66
68{
69 return project.AttachedObjects::Get< UndoManager >( key );
70}
71
73{
74 return Get( const_cast< AudacityProject & >( project ) );
75}
76
78 : mProject{ project }
79{
80 current = -1;
81 saved = -1;
82}
83
85{
86 wxASSERT( stack.empty() );
87}
88
90{
91 wxASSERT(n < stack.size());
92
93 *desc = stack[n]->shortDescription;
94}
95
97 unsigned int n, const TranslatableString &desc)
98{
99 n -= 1;
100
101 wxASSERT(n < stack.size());
102
103 stack[n]->description = desc;
104}
105
107{
108 // Remove the state from the array first, and destroy it at function exit.
109 // Because in case of callbacks from destruction of Sample blocks, there
110 // might be a yield to GUI and other events might inspect the undo stack
111 // (such as history window update). Don't expose an inconsistent stack
112 // state.
113 auto iter = stack.begin() + n;
114 auto state = std::move(*iter);
115 stack.erase(iter);
116}
117
119{
120 BasicUI::CallAfter([wThis = weak_from_this(), message]{
121 if (auto pThis = wThis.lock())
122 pThis->Publish(message);
123 });
124}
125
127{
129 auto cleanup =
130 finally([&]{ Publish({ UndoRedoMessage::EndPurge }); });
131
132 // Wrap the whole in a savepoint for better performance
133 TransactionScope trans{mProject, "DiscardingUndoStates"};
134
135 for (size_t ii = begin; ii < end; ++ii) {
137
138 if (current > begin)
139 --current;
140 if (saved > static_cast<int>(begin))
141 --saved;
142 }
143
144 // Success, commit the savepoint
145 trans.Commit();
146
147 if (begin != end)
149}
150
152{
153 RemoveStates(0, stack.size());
154 current = -1;
155 saved = -1;
156}
157
159{
160 return stack.size();
161}
162
164{
165 return current;
166}
167
169{
170 return (current > 0);
171}
172
174{
175 return (current < (int)stack.size() - 1);
176}
177
179 const SelectedRegion &selectedRegion)
180{
181 if (current == wxNOT_FOUND) {
182 return;
183 }
184
185// SonifyBeginModifyState();
186 // Delete current -- not necessary, but let's reclaim space early
187 stack[current]->state.tracks.reset();
188
189 // Duplicate
190 auto tracksCopy = TrackList::Create( nullptr );
191 for (auto t : l) {
192 if ( t->GetId() == TrackId{} )
193 // Don't copy a pending added track
194 continue;
195 tracksCopy->Add(t->Duplicate());
196 }
197
198 // Replace
199 stack[current]->state.extensions = GetExtensions(mProject);
200 stack[current]->state.tracks = std::move(tracksCopy);
201
202 stack[current]->state.selectedRegion = selectedRegion;
203// SonifyEndModifyState();
204
206}
207
209 const TranslatableString &longDescription,
210 const TranslatableString &shortDescription)
211{
212 if (state >= 0 && state < stack.size() ) {
213 auto &theState = *stack[state];
214 theState.description = longDescription;
215 theState.shortDescription = shortDescription;
216
218 }
219}
220
222 const SelectedRegion &selectedRegion,
223 const TranslatableString &longDescription,
224 const TranslatableString &shortDescription,
225 UndoPush flags)
226{
227 if ( (flags & UndoPush::CONSOLIDATE) != UndoPush::NONE &&
228 // compare full translations not msgids!
229 lastAction.Translation() == longDescription.Translation() &&
231 ModifyState(l, selectedRegion);
232 // MB: If the "saved" state was modified by ModifyState, reset
233 // it so that UnsavedChanges returns true.
234 if (current == saved) {
235 saved = -1;
236 }
237 return;
238 }
239
240 auto tracksCopy = TrackList::Create( nullptr );
241 for (auto t : l) {
242 if ( t->GetId() == TrackId{} )
243 // Don't copy a pending added track
244 continue;
245 tracksCopy->Add(t->Duplicate());
246 }
247
248 mayConsolidate = true;
249
250 AbandonRedo();
251
252 stack.push_back(
253 std::make_unique<UndoStackElem>
254 (GetExtensions(mProject), std::move(tracksCopy),
255 longDescription, shortDescription, selectedRegion)
256 );
257
258 current++;
259
260 lastAction = longDescription;
261
263}
264
266{
267 if (saved > current) {
268 saved = -1;
269 }
270 RemoveStates( current + 1, stack.size() );
271}
272
273void UndoManager::SetStateTo(unsigned int n, const Consumer &consumer)
274{
275 wxASSERT(n < stack.size());
276
277 current = n;
278
279 lastAction = {};
280 mayConsolidate = false;
281
282 consumer( *stack[current] );
283
285}
286
287void UndoManager::Undo(const Consumer &consumer)
288{
289 wxASSERT(UndoAvailable());
290
291 current--;
292
293 lastAction = {};
294 mayConsolidate = false;
295
296 consumer( *stack[current] );
297
299}
300
301void UndoManager::Redo(const Consumer &consumer)
302{
303 wxASSERT(RedoAvailable());
304
305 current++;
306
307 /*
308 if (!RedoAvailable()) {
309 *sel0 = stack[current]->sel0;
310 *sel1 = stack[current]->sel1;
311 }
312 else {
313 current++;
314 *sel0 = stack[current]->sel0;
315 *sel1 = stack[current]->sel1;
316 current--;
317 }
318 */
319
320 lastAction = {};
321 mayConsolidate = false;
322
323 consumer( *stack[current] );
324
326}
327
328void UndoManager::VisitStates( const Consumer &consumer, bool newestFirst )
329{
330 auto fn = [&]( decltype(stack[0]) &ptr ){ consumer( *ptr ); };
331 if (newestFirst)
332 std::for_each(stack.rbegin(), stack.rend(), fn);
333 else
334 std::for_each(stack.begin(), stack.end(), fn);
335}
336
338 const Consumer &consumer, size_t begin, size_t end )
339{
340 auto size = stack.size();
341 if (begin < end) {
342 end = std::min(end, size);
343 for (auto ii = begin; ii < end; ++ii)
344 consumer(*stack[ii]);
345 }
346 else {
347 if (size == 0)
348 return;
349 begin = std::min(begin, size - 1);
350 for (auto ii = begin; ii > end; --ii)
351 consumer(*stack[ii]);
352 }
353}
354
356{
357 return (saved != current);
358}
359
361{
362 saved = current;
363}
364
366{
367 return saved;
368}
369
370// currently unused
371//void UndoManager::Debug()
372//{
373// for (unsigned int i = 0; i < stack.size(); i++) {
374// for (auto t : stack[i]->tracks->Any())
375// wxPrintf(wxT("*%d* %s %f\n"),
376// i, (i == (unsigned int)current) ? wxT("-->") : wxT(" "),
377// t ? t->GetEndTime()-t->GetStartTime() : 0);
378// }
379//}
Toolkit-neutral facade for basic user interface services.
int min(int a, int b)
const TranslatableString desc
Definition: ExportPCM.cpp:58
long long SampleBlockID
Definition: ProjectFileIO.h:41
declares abstract base class Track, TrackList, and iterators over TrackList
static const AudacityProject::AttachedObjects::RegisteredFactory key
Definition: UndoManager.cpp:62
UndoPush
Definition: UndoManager.h:150
static const auto fn
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:89
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Definition: ClientData.h:266
CallbackReturn Publish(const UndoRedoMessage &message)
Send a message to connected callbacks.
Definition: Observer.h:207
Defines a selected portion of a project.
An in-session identifier of track objects across undo states. It does not persist between sessions.
Definition: Track.h:151
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
Definition: Track.h:1336
static std::shared_ptr< TrackList > Create(AudacityProject *pOwner)
Definition: Track.cpp:502
RAII for a database transaction, possibly nested.
Holds a msgid for the translation catalog; may also bind format arguments.
wxString Translation() const
Maintain a non-persistent list of states of the project, to support undo and redo commands.
Definition: UndoManager.h:167
void VisitStates(const Consumer &consumer, bool newestFirst)
Give read-only access to all states.
bool RedoAvailable()
void RenameState(int state, const TranslatableString &longDescription, const TranslatableString &shortDescription)
int GetSavedState() const
unsigned int GetNumStates()
void RemoveStateAt(int n)
void ModifyState(const TrackList &l, const SelectedRegion &selectedRegion)
UndoManager(AudacityProject &project)
Definition: UndoManager.cpp:77
void GetShortDescription(unsigned int n, TranslatableString *desc)
Definition: UndoManager.cpp:89
void SetStateTo(unsigned int n, const Consumer &consumer)
void Undo(const Consumer &consumer)
AudacityProject & mProject
Definition: UndoManager.h:231
void EnqueueMessage(UndoRedoMessage message)
bool mayConsolidate
Definition: UndoManager.h:240
void Redo(const Consumer &consumer)
void PushState(const TrackList &l, const SelectedRegion &selectedRegion, const TranslatableString &longDescription, const TranslatableString &shortDescription, UndoPush flags=UndoPush::NONE)
void ClearStates()
void AbandonRedo()
TranslatableString lastAction
Definition: UndoManager.h:239
UndoStack stack
Definition: UndoManager.h:237
void RemoveStates(size_t begin, size_t end)
void SetLongDescription(unsigned int n, const TranslatableString &desc)
Definition: UndoManager.cpp:96
static UndoManager & Get(AudacityProject &project)
Definition: UndoManager.cpp:67
std::function< void(const UndoStackElem &) > Consumer
Definition: UndoManager.h:206
unsigned int GetCurrentState()
void StateSaved()
bool UnsavedChanges() const
bool UndoAvailable()
std::function< std::shared_ptr< UndoStateExtension >(AudacityProject &)> Saver
Type of function that produces an UndoStateExtension object when saving state of a project.
Definition: UndoManager.h:104
virtual ~UndoStateExtension()
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:38
Services * Get()
Fetch the global instance, or nullptr if none is yet installed.
Definition: BasicUI.cpp:26
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
auto begin(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:150
std::vector< UndoRedoExtensionRegistry::Saver > Savers
Definition: UndoManager.cpp:38
UndoState::Extensions GetExtensions(AudacityProject &project)
Definition: UndoManager.cpp:45
Entry(const Saver &saver)
Definition: UndoManager.cpp:55
Type of message published by UndoManager.
Definition: UndoManager.h:61
@ EndPurge
End elimination of old undo states.
Definition: UndoManager.h:79
@ Purge
Undo or redo states eliminated.
Definition: UndoManager.h:75
@ BeginPurge
Begin elimination of old undo states.
Definition: UndoManager.h:78
std::vector< std::shared_ptr< UndoStateExtension > > Extensions
Definition: UndoManager.h:113