winamp/Src/external_dependencies/openmpt-trunk/mptrack/Undo.h
2024-09-24 14:54:57 +02:00

229 lines
8.7 KiB
C++

/*
* 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