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 "TransactionScope.h"
31//#include "NoteTrack.h" // for Sonify* function declarations
32
33
35
37{
38 return true;
39}
40
41namespace {
42 using Savers = std::vector<UndoRedoExtensionRegistry::Saver>;
43 static Savers &GetSavers()
44 {
45 static Savers theSavers;
46 return theSavers;
47 }
48
50 {
52 for (auto &saver : GetSavers())
53 if (saver)
54 result.emplace_back(saver(project));
55 return result;
56 }
57}
58
60{
61 GetSavers().emplace_back(saver);
62}
63
64using SampleBlockID = long long;
65
68 { return std::make_unique<UndoManager>( project ); }
69};
70
72{
73 return project.AttachedObjects::Get< UndoManager >( key );
74}
75
77{
78 return Get( const_cast< AudacityProject & >( project ) );
79}
80
82 : mProject{ project }
83{
84 current = -1;
85 saved = -1;
86}
87
89{
90 wxASSERT( stack.empty() );
91}
92
94{
95 wxASSERT(n < stack.size());
96
97 *desc = stack[n]->shortDescription;
98}
99
101 unsigned int n, const TranslatableString &desc)
102{
103 n -= 1;
104
105 wxASSERT(n < stack.size());
106
107 stack[n]->description = desc;
108}
109
111{
112 // Remove the state from the array first, and destroy it at function exit.
113 // Because in case of callbacks from destruction of Sample blocks, there
114 // might be a yield to GUI and other events might inspect the undo stack
115 // (such as history window update). Don't expose an inconsistent stack
116 // state.
117 auto iter = stack.begin() + n;
118 auto state = std::move(*iter);
119 stack.erase(iter);
120}
121
123{
124 BasicUI::CallAfter([wThis = weak_from_this(), message]{
125 if (auto pThis = wThis.lock())
126 pThis->Publish(message);
127 });
128}
129
131{
133 auto cleanup =
134 finally([&]{ Publish({ UndoRedoMessage::EndPurge }); });
135
136 // Wrap the whole in a savepoint for better performance
137 TransactionScope trans{mProject, "DiscardingUndoStates"};
138
139 for (size_t ii = begin; ii < end; ++ii) {
141
142 if (current > begin)
143 --current;
144 if (saved > static_cast<int>(begin))
145 --saved;
146 }
147
148 // Success, commit the savepoint
149 trans.Commit();
150
151 if (begin != end)
153}
154
156{
157 RemoveStates(0, stack.size());
158 current = -1;
159 saved = -1;
160}
161
163{
164 return stack.size();
165}
166
168{
169 return current;
170}
171
173{
174 return CheckAvailable(current - 1);
175}
176
178{
179 return CheckAvailable(current + 1);
180}
181
183{
184 if (index < 0 || index >= (int)stack.size())
185 return false;
186 auto &extensions = stack[index]->state.extensions;
187 return std::all_of(extensions.begin(), extensions.end(), [&](auto &ext){
188 return !ext || ext->CanUndoOrRedo(mProject);
189 });
190}
191
193{
194 if (current == wxNOT_FOUND) {
195 return;
196 }
197
198// SonifyBeginModifyState();
199 auto &state = stack[current]->state;
200
201 // Re-create all captured project state
202 state.extensions = GetExtensions(mProject);
203
204// SonifyEndModifyState();
205
207}
208
210 const TranslatableString &longDescription,
211 const TranslatableString &shortDescription)
212{
213 if (state >= 0 && state < stack.size() ) {
214 auto &theState = *stack[state];
215 theState.description = longDescription;
216 theState.shortDescription = shortDescription;
217
219 }
220}
221
222void UndoManager::PushState(const TranslatableString &longDescription,
223 const TranslatableString &shortDescription,
224 UndoPush flags)
225{
226 if ( (flags & UndoPush::CONSOLIDATE) != UndoPush::NONE &&
227 // compare full translations not msgids!
228 lastAction.Translation() == longDescription.Translation() &&
230 ModifyState();
231 // MB: If the "saved" state was modified by ModifyState, reset
232 // it so that UnsavedChanges returns true.
233 if (current == saved) {
234 saved = -1;
235 }
236 return;
237 }
238
239 mayConsolidate = true;
240
241 AbandonRedo();
242
243 stack.push_back(
244 std::make_unique<UndoStackElem>
245 (GetExtensions(mProject), longDescription, shortDescription)
246 );
247
248 current++;
249
250 lastAction = longDescription;
251
253}
254
256{
257 if (saved > current) {
258 saved = -1;
259 }
260 RemoveStates( current + 1, stack.size() );
261}
262
263void UndoManager::SetStateTo(unsigned int n, const Consumer &consumer)
264{
265 wxASSERT(n < stack.size());
266
267 current = n;
268
269 lastAction = {};
270 mayConsolidate = false;
271
272 consumer( *stack[current] );
273
275}
276
277void UndoManager::Undo(const Consumer &consumer)
278{
279 wxASSERT(UndoAvailable());
280
281 current--;
282
283 lastAction = {};
284 mayConsolidate = false;
285
286 consumer( *stack[current] );
287
289}
290
291void UndoManager::Redo(const Consumer &consumer)
292{
293 wxASSERT(RedoAvailable());
294
295 current++;
296
297 /*
298 if (!RedoAvailable()) {
299 *sel0 = stack[current]->sel0;
300 *sel1 = stack[current]->sel1;
301 }
302 else {
303 current++;
304 *sel0 = stack[current]->sel0;
305 *sel1 = stack[current]->sel1;
306 current--;
307 }
308 */
309
310 lastAction = {};
311 mayConsolidate = false;
312
313 consumer( *stack[current] );
314
316}
317
318void UndoManager::VisitStates( const Consumer &consumer, bool newestFirst )
319{
320 auto fn = [&]( decltype(stack[0]) &ptr ){ consumer( *ptr ); };
321 if (newestFirst)
322 std::for_each(stack.rbegin(), stack.rend(), fn);
323 else
324 std::for_each(stack.begin(), stack.end(), fn);
325}
326
328 const Consumer &consumer, size_t begin, size_t end )
329{
330 auto size = stack.size();
331 if (begin < end) {
332 end = std::min(end, size);
333 for (auto ii = begin; ii < end; ++ii)
334 consumer(*stack[ii]);
335 }
336 else {
337 if (size == 0)
338 return;
339 begin = std::min(begin, size - 1);
340 for (auto ii = begin; ii > end; --ii)
341 consumer(*stack[ii]);
342 }
343}
344
346{
347 saved = -1;
348}
349
351{
352 return (saved != current);
353}
354
356{
357 saved = current;
358}
359
361{
362 return saved;
363}
364
365// currently unused
366//void UndoManager::Debug()
367//{
368// for (unsigned int i = 0; i < stack.size(); i++) {
369// for (auto t : stack[i]->tracks->Any())
370// wxPrintf(wxT("*%d* %s %f\n"),
371// i, (i == (unsigned int)current) ? wxT("-->") : wxT(" "),
372// t ? t->GetEndTime()-t->GetStartTime() : 0);
373// }
374//}
Toolkit-neutral facade for basic user interface services.
int min(int a, int b)
long long SampleBlockID
Definition: ProjectFileIO.h:43
const auto project
static const AudacityProject::AttachedObjects::RegisteredFactory key
Definition: UndoManager.cpp:66
UndoPush
Definition: UndoManager.h:138
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:90
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Definition: ClientData.h:274
CallbackReturn Publish(const UndoRedoMessage &message)
Send a message to connected callbacks.
Definition: Observer.h:207
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:155
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)
UndoManager(AudacityProject &project)
Definition: UndoManager.cpp:81
void GetShortDescription(unsigned int n, TranslatableString *desc)
Definition: UndoManager.cpp:93
void SetStateTo(unsigned int n, const Consumer &consumer)
void Undo(const Consumer &consumer)
AudacityProject & mProject
Definition: UndoManager.h:219
void EnqueueMessage(UndoRedoMessage message)
bool mayConsolidate
Definition: UndoManager.h:227
void Redo(const Consumer &consumer)
void ClearStates()
void AbandonRedo()
TranslatableString lastAction
Definition: UndoManager.h:226
UndoStack stack
Definition: UndoManager.h:224
void RemoveStates(size_t begin, size_t end)
void SetLongDescription(unsigned int n, const TranslatableString &desc)
void PushState(const TranslatableString &longDescription, const TranslatableString &shortDescription, UndoPush flags=UndoPush::NONE)
bool CheckAvailable(int index)
static UndoManager & Get(AudacityProject &project)
Definition: UndoManager.cpp:71
std::function< void(const UndoStackElem &) > Consumer
Definition: UndoManager.h:191
void ModifyState()
unsigned int GetCurrentState()
void MarkUnsaved()
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:99
virtual bool CanUndoOrRedo(const AudacityProject &project)
Whether undo or redo is now permitted; default returns true.
Definition: UndoManager.cpp:36
virtual ~UndoStateExtension()
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:208
Services * Get()
Fetch the global instance, or nullptr if none is yet installed.
Definition: BasicUI.cpp:196
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
const TranslatableString desc
Definition: ExportPCM.cpp:51
std::vector< UndoRedoExtensionRegistry::Saver > Savers
Definition: UndoManager.cpp:42
UndoState::Extensions GetExtensions(AudacityProject &project)
Definition: UndoManager.cpp:49
Entry(const Saver &saver)
Definition: UndoManager.cpp:59
Type of message published by UndoManager.
Definition: UndoManager.h:55
@ EndPurge
End elimination of old undo states.
Definition: UndoManager.h:73
@ Purge
Undo or redo states eliminated.
Definition: UndoManager.h:69
@ BeginPurge
Begin elimination of old undo states.
Definition: UndoManager.h:72
std::vector< std::shared_ptr< UndoStateExtension > > Extensions
Definition: UndoManager.h:108