1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
|
/*
* Undo.h
* ------
* Purpose: Editor undo buffer functionality.
* Notes : (currently none)
* Authors: Olivier Lapicque
* OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include "openmpt/all/BuildSettings.hpp"
#include "../soundlib/ModChannel.h"
#include "../soundlib/modcommand.h"
OPENMPT_NAMESPACE_BEGIN
class CModDoc;
struct ModSample;
#define MAX_UNDO_LEVEL 100000 // 100,000 undo steps for each undo type!
/////////////////////////////////////////////////////////////////////////////////////////
// Pattern Undo
class CPatternUndo
{
protected:
static constexpr auto DELETE_PATTERN = PATTERNINDEX_INVALID;
struct UndoInfo
{
std::vector<ModChannelSettings> channelInfo; // Optional old channel information (pan / volume / etc.)
std::vector<ModCommand> content; // Rescued pattern content
const char *description; // Name of this undo action
ROWINDEX numPatternRows; // Original number of pattern rows (in case of resize, DELETE_PATTERN in case of deletion)
ROWINDEX firstRow, numRows;
PATTERNINDEX pattern;
CHANNELINDEX firstChannel, numChannels;
bool linkToPrevious; // This undo information is linked with the previous undo information
bool OnlyChannelSettings() const noexcept
{
return !channelInfo.empty() && numRows < 1 && !linkToPrevious;
}
};
using undobuf_t = std::vector<UndoInfo>;
undobuf_t UndoBuffer;
undobuf_t RedoBuffer;
CModDoc &modDoc;
// Pattern undo helper functions
PATTERNINDEX Undo(undobuf_t &fromBuf, undobuf_t &toBuf, bool linkedFromPrevious);
bool PrepareBuffer(undobuf_t &buffer, PATTERNINDEX pattern, CHANNELINDEX firstChn, ROWINDEX firstRow, CHANNELINDEX numChns, ROWINDEX numRows, const char *description, bool linkToPrevious, bool storeChannelInfo) const;
static CString GetName(const undobuf_t &buffer);
static void RearrangePatterns(undobuf_t &buffer, const std::vector<PATTERNINDEX> &newIndex);
public:
// Removes all undo steps from the buffer.
void ClearUndo();
// Adds a new action to the undo buffer.
bool PrepareUndo(PATTERNINDEX pattern, CHANNELINDEX firstChn, ROWINDEX firstRow, CHANNELINDEX numChns, ROWINDEX numRows, const char *description, bool linkToPrevious = false, bool storeChannelInfo = false);
// Adds a new action only affecting channel data to the undo buffer.
bool PrepareChannelUndo(CHANNELINDEX firstChn, CHANNELINDEX numChns, const char *description);
// Undoes the most recent action.
PATTERNINDEX Undo();
// Redoes the most recent action.
PATTERNINDEX Redo();
// Returns true if any actions can currently be undone.
bool CanUndo() const { return !UndoBuffer.empty(); }
// Returns true if any actions can currently be redone.
bool CanRedo() const { return !RedoBuffer.empty(); }
// Returns true if a channel-specific action (no pattern data) can currently be undone.
bool CanUndoChannelSettings() const { return !UndoBuffer.empty() && UndoBuffer.back().OnlyChannelSettings(); }
// Returns true if a channel-specific action (no pattern data) actions can currently be redone.
bool CanRedoChannelSettings() const { return !RedoBuffer.empty() && RedoBuffer.back().OnlyChannelSettings(); }
// Remove the latest added undo step from the undo buffer
void RemoveLastUndoStep();
// Get name of next undo item
CString GetUndoName() const { return GetName(UndoBuffer); }
// Get name of next redo item
CString GetRedoName() const { return GetName(RedoBuffer); }
// Adjust undo buffers for rearranged patterns
void RearrangePatterns(const std::vector<PATTERNINDEX> &newIndex);
CPatternUndo(CModDoc &parent) : modDoc(parent) { }
};
/////////////////////////////////////////////////////////////////////////////////////////
// Sample Undo
// We will differentiate between different types of undo actions so that we don't have to copy the whole sample everytime.
enum sampleUndoTypes
{
sundo_none, // no changes to sample itself, e.g. loop point update
sundo_update, // silence, amplify, normalize, dc offset - update complete sample section
sundo_delete, // delete part of the sample
sundo_invert, // invert sample phase, apply again to undo
sundo_reverse, // reverse sample, ditto
sundo_unsign, // unsign sample, ditto
sundo_insert, // insert data, delete inserted data to undo
sundo_replace, // replace complete sample (16->8Bit, up/downsample, downmix to mono, pitch shifting / time stretching, trimming, pasting)
};
class CSampleUndo
{
protected:
struct UndoInfo
{
ModSample OldSample;
mpt::charbuf<MAX_SAMPLENAME> oldName;
void *samplePtr = nullptr;
const char *description = nullptr;
SmpLength changeStart = 0, changeEnd = 0;
sampleUndoTypes changeType = sundo_none;
};
using undobuf_t = std::vector<std::vector<UndoInfo>>;
undobuf_t UndoBuffer;
undobuf_t RedoBuffer;
CModDoc &modDoc;
// Sample undo helper functions
void ClearUndo(undobuf_t &buffer, const SAMPLEINDEX smp);
void DeleteStep(undobuf_t &buffer, const SAMPLEINDEX smp, const size_t step);
bool SampleBufferExists(const undobuf_t &buffer, const SAMPLEINDEX smp) const;
void RestrictBufferSize(undobuf_t &buffer, size_t &capacity);
size_t GetBufferCapacity(const undobuf_t &buffer) const;
void RearrangeSamples(undobuf_t &buffer, const std::vector<SAMPLEINDEX> &newIndex);
bool PrepareBuffer(undobuf_t &buffer, const SAMPLEINDEX smp, sampleUndoTypes changeType, const char *description, SmpLength changeStart, SmpLength changeEnd);
bool Undo(undobuf_t &fromBuf, undobuf_t &toBuf, const SAMPLEINDEX smp);
public:
// Sample undo functions
void ClearUndo();
void ClearUndo(const SAMPLEINDEX smp) { ClearUndo(UndoBuffer, smp); ClearUndo(RedoBuffer, smp); }
bool PrepareUndo(const SAMPLEINDEX smp, sampleUndoTypes changeType, const char *description, SmpLength changeStart = 0, SmpLength changeEnd = 0);
bool Undo(const SAMPLEINDEX smp);
bool Redo(const SAMPLEINDEX smp);
bool CanUndo(const SAMPLEINDEX smp) const { return SampleBufferExists(UndoBuffer, smp) && !UndoBuffer[smp - 1].empty(); }
bool CanRedo(const SAMPLEINDEX smp) const { return SampleBufferExists(RedoBuffer, smp) && !RedoBuffer[smp - 1].empty(); }
void RemoveLastUndoStep(const SAMPLEINDEX smp);
const char *GetUndoName(const SAMPLEINDEX smp) const;
const char *GetRedoName(const SAMPLEINDEX smp) const;
void RestrictBufferSize();
void RearrangeSamples(const std::vector<SAMPLEINDEX> &newIndex) { RearrangeSamples(UndoBuffer, newIndex); RearrangeSamples(RedoBuffer, newIndex); }
CSampleUndo(CModDoc &parent) : modDoc(parent) { }
~CSampleUndo()
{
ClearUndo();
};
};
/////////////////////////////////////////////////////////////////////////////////////////
// Instrument Undo
class CInstrumentUndo
{
protected:
struct UndoInfo
{
ModInstrument instr;
const char *description = nullptr;
EnvelopeType editedEnvelope = ENV_MAXTYPES;
};
using undobuf_t = std::vector<std::vector<UndoInfo>>;
undobuf_t UndoBuffer;
undobuf_t RedoBuffer;
CModDoc &modDoc;
// Instrument undo helper functions
void ClearUndo(undobuf_t &buffer, const INSTRUMENTINDEX ins);
void DeleteStep(undobuf_t &buffer, const INSTRUMENTINDEX ins, const size_t step);
bool InstrumentBufferExists(const undobuf_t &buffer, const INSTRUMENTINDEX ins) const;
void RearrangeInstruments(undobuf_t &buffer, const std::vector<INSTRUMENTINDEX> &newIndex);
void RearrangeSamples(undobuf_t &buffer, const INSTRUMENTINDEX ins, std::vector<SAMPLEINDEX> &newIndex);
bool PrepareBuffer(undobuf_t &buffer, const INSTRUMENTINDEX ins, const char *description, EnvelopeType envType);
bool Undo(undobuf_t &fromBuf, undobuf_t &toBuf, const INSTRUMENTINDEX ins);
public:
// Instrument undo functions
void ClearUndo();
void ClearUndo(const INSTRUMENTINDEX ins) { ClearUndo(UndoBuffer, ins); ClearUndo(RedoBuffer, ins); }
bool PrepareUndo(const INSTRUMENTINDEX ins, const char *description, EnvelopeType envType = ENV_MAXTYPES);
bool Undo(const INSTRUMENTINDEX ins);
bool Redo(const INSTRUMENTINDEX ins);
bool CanUndo(const INSTRUMENTINDEX ins) const { return InstrumentBufferExists(UndoBuffer, ins) && !UndoBuffer[ins - 1].empty(); }
bool CanRedo(const INSTRUMENTINDEX ins) const { return InstrumentBufferExists(RedoBuffer, ins) && !RedoBuffer[ins - 1].empty(); }
void RemoveLastUndoStep(const INSTRUMENTINDEX ins);
const char *GetUndoName(const INSTRUMENTINDEX ins) const;
const char *GetRedoName(const INSTRUMENTINDEX ins) const;
void RearrangeInstruments(const std::vector<INSTRUMENTINDEX> &newIndex) { RearrangeInstruments(UndoBuffer, newIndex); RearrangeInstruments(RedoBuffer, newIndex); }
void RearrangeSamples(const INSTRUMENTINDEX ins, std::vector<SAMPLEINDEX> &newIndex) { RearrangeSamples(UndoBuffer, ins, newIndex); RearrangeSamples(RedoBuffer, ins, newIndex); }
CInstrumentUndo(CModDoc &parent) : modDoc(parent) { }
~CInstrumentUndo()
{
ClearUndo();
};
};
OPENMPT_NAMESPACE_END
|