feat: Implement better and more complete undo/redo stack (#1433)

This PR aims to implement a more complete undo/redo stack that, unlike
the old one, also supports undoing insertions, deletions and resize
operations
This commit is contained in:
Nik
2023-11-25 12:43:48 +01:00
committed by GitHub
parent e5f36ca08d
commit 7e660450ed
36 changed files with 904 additions and 325 deletions

View File

@@ -5,7 +5,6 @@
#include <cmath>
#include <cstring>
#include <map>
#include <optional>
#include <hex/helpers/magic.hpp>
@@ -20,9 +19,8 @@ namespace hex::prv {
}
Provider::Provider() : m_id(s_idCounter++) {
this->m_patches.emplace_back();
this->m_currPatches = this->m_patches.begin();
Provider::Provider() : m_undoRedoStack(this), m_id(s_idCounter++) {
}
Provider::~Provider() {
@@ -39,7 +37,7 @@ namespace hex::prv {
}
void Provider::write(u64 offset, const void *buffer, size_t size) {
this->writeRaw(offset - this->getBaseAddress(), buffer, size);
EventManager::post<EventProviderDataModified>(this, offset, size, static_cast<const u8*>(buffer));
this->markDirty();
}
@@ -68,53 +66,29 @@ namespace hex::prv {
file.writeBuffer(buffer.data(), bufferSize);
}
for (auto &[patchAddress, patch] : getPatches()) {
file.seek(patchAddress - this->getBaseAddress());
file.writeBuffer(&patch, 1);
}
EventManager::post<EventProviderSaved>(this);
}
}
void Provider::resize(size_t newSize) {
hex::unused(newSize);
i64 difference = newSize - this->getActualSize();
if (difference > 0)
EventManager::post<EventProviderDataInserted>(this, this->getActualSize(), difference);
else if (difference < 0)
EventManager::post<EventProviderDataRemoved>(this, this->getActualSize(), -difference);
this->markDirty();
}
void Provider::insert(u64 offset, size_t size) {
auto &patches = getPatches();
std::vector<std::pair<u64, u8>> patchesToMove;
for (auto &[address, value] : patches) {
if (address > offset)
patchesToMove.emplace_back(address, value);
}
for (const auto &[address, value] : patchesToMove)
patches.erase(address);
for (const auto &[address, value] : patchesToMove)
patches.insert({ address + size, value });
EventManager::post<EventProviderDataInserted>(this, offset, size);
this->markDirty();
}
void Provider::remove(u64 offset, size_t size) {
auto &patches = getPatches();
std::vector<std::pair<u64, u8>> patchesToMove;
for (auto &[address, value] : patches) {
if (address > offset)
patchesToMove.emplace_back(address, value);
}
for (const auto &[address, value] : patchesToMove)
patches.erase(address);
for (const auto &[address, value] : patchesToMove)
patches.insert({ address - size, value });
EventManager::post<EventProviderDataRemoved>(this, offset, size);
this->markDirty();
}
@@ -131,38 +105,6 @@ namespace hex::prv {
}
}
std::map<u64, u8> &Provider::getPatches() {
return *this->m_currPatches;
}
const std::map<u64, u8> &Provider::getPatches() const {
return *this->m_currPatches;
}
void Provider::applyPatches() {
if (!this->isWritable())
return;
this->m_patches.emplace_back();
for (auto &[patchAddress, patch] : getPatches()) {
u8 value = 0x00;
this->readRaw(patchAddress - this->getBaseAddress(), &value, 1);
this->m_patches.back().insert({ patchAddress, value });
}
for (auto &[patchAddress, patch] : getPatches()) {
this->writeRaw(patchAddress - this->getBaseAddress(), &patch, 1);
}
this->markDirty();
this->m_patches.emplace_back();
this->m_currPatches = std::prev(this->m_patches.end());
}
Overlay *Provider::newOverlay() {
return this->m_overlays.emplace_back(std::make_unique<Overlay>()).get();
}
@@ -235,54 +177,20 @@ namespace hex::prv {
return { };
}
void Provider::addPatch(u64 offset, const void *buffer, size_t size, bool createUndo) {
if (createUndo) {
// Delete all patches after the current one if a modification is made while
// the current patch list is not at the end of the undo stack
if (std::next(this->m_currPatches) != this->m_patches.end())
this->m_patches.erase(std::next(this->m_currPatches), this->m_patches.end());
createUndoPoint();
}
for (u64 i = 0; i < size; i++) {
u8 patch = static_cast<const u8 *>(buffer)[i];
u8 originalValue = 0x00;
this->readRaw((offset + i) - this->getBaseAddress(), &originalValue, sizeof(u8));
if (patch == originalValue)
getPatches().erase(offset + i);
else
getPatches()[offset + i] = patch;
EventManager::post<EventPatchCreated>(offset, originalValue, patch);
}
this->markDirty();
}
void Provider::createUndoPoint() {
this->m_patches.push_back(getPatches());
this->m_currPatches = std::prev(this->m_patches.end());
}
void Provider::undo() {
if (canUndo())
--this->m_currPatches;
this->m_undoRedoStack.undo();
}
void Provider::redo() {
if (canRedo())
++this->m_currPatches;
this->m_undoRedoStack.redo();
}
bool Provider::canUndo() const {
return this->m_currPatches != this->m_patches.begin();
return this->m_undoRedoStack.canUndo();
}
bool Provider::canRedo() const {
return std::next(this->m_currPatches) != this->m_patches.end();
return this->m_undoRedoStack.canRedo();
}
bool Provider::hasFilePicker() const {
@@ -341,14 +249,6 @@ namespace hex::prv {
}
}
for (const auto &[patchAddress, value] : this->m_patches.back()) {
if (!nextRegionAddress.has_value() || patchAddress < nextRegionAddress)
nextRegionAddress = patchAddress;
if (address == patchAddress)
insideValidRegion = true;
}
if (!nextRegionAddress.has_value())
return { Region::Invalid(), false };
else