Files
fs2open.github.com/code/globalincs/undosys.cpp
Goober5000 e90416e199 Several coverity fixes (#6232)
* coverity - using `std::move` instead of copying, and change some parameters from value to reference

* coverity - fix or mark potential copy-paste mistakes

* coverity - various checking of parameter and return values

* coverity - miscellaneous fixes, and change some 'intentional' markers to 'false positive' markers
2024-07-06 12:51:31 +02:00

167 lines
3.3 KiB
C++

/*
* z64555's Undo system, created for the FreeSpace Source Code project
*/
#include "globalincs/pstypes.h"
#include "globalincs/undosys.h"
#include "globalincs/vmallocator.h"
// Pure virtual deconstructors must be defined.
Undo_item_base::~Undo_item_base() = default;
Undo_system::Undo_system()
: max_undos(10) {};
Undo_system::Undo_system(size_t _undos)
: max_undos(_undos) {};
void Undo_system::clamp_stacks() {
while (undo_stack.size() > max_undos) {
// If we're past the stack limit, de-construct and remove until we're in the limit
// If max_undos is held constant (as it should be), the loop isn't necassary.
// But, it also isn't that much different from an if{} when assembled, so there's no harm in using it
delete undo_stack.front();
undo_stack.pop_front();
}
// This while() shouldn't ever be triggered, but this is a safety to prevent any weird edge-cases.
while (redo_stack.size() > max_undos) {
delete redo_stack.front();
redo_stack.pop_front();
}
}
void Undo_system::clear() {
clear_redo();
for (auto &element : undo_stack) {
delete element;
}
undo_stack.clear();
}
void Undo_system::clear_redo() {
for (auto &element : redo_stack) {
delete element;
}
redo_stack.clear();
}
size_t Undo_system::save_stack(Undo_stack& stack) {
if (stack.size() == 0) {
return 0;
}
clear_redo();
// Copy the stack onto the heap, and push it into our undo_stack as a single item
Undo_item_base *new_item = new Undo_stack(stack);
Assert(new_item != nullptr);
undo_stack.push_back(new_item);
// Input stack is told to untrack the items, so that there's only one deletion handler for them
stack.untrack();
clamp_stacks();
return undo_stack.size();
}
std::pair<const void*, const void*> Undo_system::redo() {
std::pair<const void*, const void*> retval;
if (redo_stack.empty()) {
// Nothing to redo
return {nullptr, nullptr};
}
auto &item = redo_stack.back();
retval = item->restore();
undo_stack.push_back(item);
redo_stack.pop_back();
return retval;
};
std::pair<const void*, const void*> Undo_system::undo() {
std::pair<const void*, const void*> retval;
if (undo_stack.empty()) {
// Nothing to undo
return {nullptr, nullptr};
}
auto &item = undo_stack.back();
retval = item->restore();
redo_stack.push_back(item);
undo_stack.pop_back();
return retval;
};
bool Undo_system::empty() {
return undo_stack.empty();
}
bool Undo_system::empty_redo() {
return redo_stack.empty();
}
size_t Undo_system::size() {
return undo_stack.size();
}
size_t Undo_system::size_redo() {
return redo_stack.size();
}
Undo_stack::Undo_stack()
: reverse(false) {
}
Undo_stack::Undo_stack(size_t size)
: reverse(false) {
stack.reserve(size);
}
Undo_stack::~Undo_stack() {
clear();
}
void Undo_stack::reserve(size_t size) {
stack.reserve(size);
}
std::pair<const void*, const void*> Undo_stack::restore() {
std::pair<const void*, const void*> retval;
if (reverse) {
for (auto it = stack.rbegin(); it != stack.rend(); ++it) {
retval = (*it)->restore();
}
} else {
for (auto &element : stack) {
retval = element->restore();
}
}
reverse = !reverse;
return retval;
}
size_t Undo_stack::size() {
return stack.size();
}
void Undo_stack::untrack() {
stack.clear();
}
void Undo_stack::clear() {
for (auto &element : stack) {
delete element;
}
stack.clear();
}