230 lines
8.7 KiB
C++
230 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
|