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 if (saved == current)
208 saved = -1;
209}
210
212 const TranslatableString &longDescription,
213 const TranslatableString &shortDescription)
214{
215 if (state >= 0 && state < stack.size() ) {
216 auto &theState = *stack[state];
217 theState.description = longDescription;
218 theState.shortDescription = shortDescription;
219
221 }
222}
223
225 const SelectedRegion &selectedRegion,
226 const TranslatableString &longDescription,
227 const TranslatableString &shortDescription,
228 UndoPush flags)
229{
230 if ( (flags & UndoPush::CONSOLIDATE) != UndoPush::NONE &&
231 // compare full translations not msgids!
232 lastAction.Translation() == longDescription.Translation() &&
234 ModifyState(l, selectedRegion);
235 // MB: If the "saved" state was modified by ModifyState, reset
236 // it so that UnsavedChanges returns true.
237 if (current == saved) {
238 saved = -1;
239 }
240 return;
241 }
242
243 auto tracksCopy = TrackList::Create( nullptr );
244 for (auto t : l) {
245 if ( t->GetId() == TrackId{} )
246 // Don't copy a pending added track
247 continue;
248 tracksCopy->Add(t->Duplicate());
249 }
250
251 mayConsolidate = true;
252
253 AbandonRedo();
254
255 stack.push_back(
256 std::make_unique<UndoStackElem>
257 (GetExtensions(mProject), std::move(tracksCopy),
258 longDescription, shortDescription, selectedRegion)
259 );
260
261 current++;
262
263 lastAction = longDescription;
264
266}
267
269{
270 if (saved > current) {
271 saved = -1;
272 }
273 RemoveStates( current + 1, stack.size() );
274}
275
276void UndoManager::SetStateTo(unsigned int n, const Consumer &consumer)
277{
278 wxASSERT(n < stack.size());
279
280 current = n;
281
282 lastAction = {};
283 mayConsolidate = false;
284
285 consumer( *stack[current] );
286
288}
289
290void UndoManager::Undo(const Consumer &consumer)
291{
292 wxASSERT(UndoAvailable());
293
294 current--;
295
296 lastAction = {};
297 mayConsolidate = false;
298
299 consumer( *stack[current] );
300
302}
303
304void UndoManager::Redo(const Consumer &consumer)
305{
306 wxASSERT(RedoAvailable());
307
308 current++;
309
310 /*
311 if (!RedoAvailable()) {
312 *sel0 = stack[current]->sel0;
313 *sel1 = stack[current]->sel1;
314 }
315 else {
316 current++;
317 *sel0 = stack[current]->sel0;
318 *sel1 = stack[current]->sel1;
319 current--;
320 }
321 */
322
323 lastAction = {};
324 mayConsolidate = false;
325
326 consumer( *stack[current] );
327
329}
330
331void UndoManager::VisitStates( const Consumer &consumer, bool newestFirst )
332{
333 auto fn = [&]( decltype(stack[0]) &ptr ){ consumer( *ptr ); };
334 if (newestFirst)
335 std::for_each(stack.rbegin(), stack.rend(), fn);
336 else
337 std::for_each(stack.begin(), stack.end(), fn);
338}
339
341 const Consumer &consumer, size_t begin, size_t end )
342{
343 auto size = stack.size();
344 if (begin < end) {
345 end = std::min(end, size);
346 for (auto ii = begin; ii < end; ++ii)
347 consumer(*stack[ii]);
348 }
349 else {
350 if (size == 0)
351 return;
352 begin = std::min(begin, size - 1);
353 for (auto ii = begin; ii > end; --ii)
354 consumer(*stack[ii]);
355 }
356}
357
359{
360 return (saved != current);
361}
362
364{
365 saved = current;
366}
367
369{
370 return saved;
371}
372
373// currently unused
374//void UndoManager::Debug()
375//{
376// for (unsigned int i = 0; i < stack.size(); i++) {
377// for (auto t : stack[i]->tracks->Any())
378// wxPrintf(wxT("*%d* %s %f\n"),
379// i, (i == (unsigned int)current) ? wxT("-->") : wxT(" "),
380// t ? t->GetEndTime()-t->GetStartTime() : 0);
381// }
382//}
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:1338
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:206
Services * Get()
Fetch the global instance, or nullptr if none is yet installed.
Definition: BasicUI.cpp:194
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