📄 undo.h
字号:
// --------- undo.h
#ifndef UNDO_H
#define UNDO_H
#include <deque>
#include <stack>
namespace DDJCProgrammingColumnUndo {
//------------------------------------------------------------------------------
// UndoNode<D>: base class for all undo/redo actions
template <class D> class UndoData;
template <class D>
class UndoNode
{
bool terminator; // undo action stream terminator
protected:
explicit UndoNode(bool term) : terminator(term) { }
public:
virtual ~UndoNode() { }
virtual void Undo(D& doc) = 0;
virtual void Redo(D& doc) = 0;
virtual void SetUndoContext(D& doc) = 0;
bool& Terminator()
{ return terminator; }
void* operator new(size_t sz, UndoData<D>* data);
};
//------------------------------------------------------------------------------
// UndoItem: base template class for all undo/redo actions
// D = document class
// T = datum unit of undo action (string, char, etc.)
// P = document position information
// C = document context information
// class D must provide these public functions:
// void SetUndoContext(C);
// C GetUndoContext() const;
// void Delete(P); // deletes datum at P
// void Insert(P, T); // inserts datum T into document just ahead of P
// void Replace(P, T); // replaces datum at P with T
// T GetDatum(P); // returns datum at P
// classes T and C must support operator=
// classes T, P, and C must have public default constructors
//
template <class D, class T, class P, class C>
class UndoItem : public UndoNode<D>
{
C context; // document view context (cursor, e.g.) at time of action
P position; // document position of undoable action
T datum; // data value
protected:
UndoItem(D& doc, P pos, bool term) : UndoNode<D>(term), position(pos)
{ context = doc.GetUndoContext(); }
void SetUndoContext(D& doc)
{
C ctx = context;
context = doc.GetUndoContext();
doc.SetUndoContext(ctx);
}
P Position() const
{ return position; }
T Datum() const
{ return datum; }
void SetDatum(T d)
{ datum = d; }
};
//------------------------------------------------------------------------------
// base class for undoing insertion actions
// instantiate derived class and add to UndoData stack before performing action
//
template <class D, class T, class P, class C>
class UndoInsertNode : public UndoItem<D,T,P,C>
{
public:
UndoInsertNode(D& doc, P pos, bool term) : UndoItem<D,T,P,C>(doc, pos, term)
{ }
void Undo(D& doc)
{
// --- save datum for redo
SetDatum(doc.GetDatum(Position()));
// ---- undo the insertion
doc.Delete(Position());
}
void Redo(D& doc)
{
doc.Insert(Position(), Datum());
}
};
//------------------------------------------------------------------------------
// base class for undoing deletion actions
// instantiate derived class and add to UndoData stack before performing action
//
template <class D, class T, class P, class C>
class UndoDeleteNode : public UndoItem<D,T,P,C>
{
public:
UndoDeleteNode(D& doc, P pos, bool term) : UndoItem<D,T,P,C>(doc, pos, term)
{
// --- save datum for undo/redo
T dat = doc.GetDatum(Position());
SetDatum(dat);
}
void Undo(D& doc)
{
doc.Insert(Position(), Datum());
}
void Redo(D& doc)
{
doc.Delete(Position());
}
};
//------------------------------------------------------------------------------
// base class for undoing replacement actions
// instantiate derived class and add to UndoData stack before performing action
//
template <class D, class T, class P, class C>
class UndoReplaceNode : public UndoItem<D,T,P,C>
{
public:
UndoReplaceNode(D& doc, P pos, bool term) : UndoItem<D,T,P,C>(doc, pos, term)
{
// --- save datum for undo/redo
SetDatum(doc.GetDatum(Position()));
}
void Undo(D& doc)
{
T temp = doc.GetDatum(Position());
doc.Replace(Position(), Datum());
SetDatum(temp);
}
void Redo(D& doc)
{ Undo(doc); }
};
//------------------------------------------------------------------------------
// base class for storing undo actions
// application derives from this class
//
template <class D>
class UndoData
{
D& doc;
bool discardedundos;
bool undoenabled;
std::deque<UndoNode<D>*> undodeque;
std::stack<UndoNode<D>*> redostack;
int maxundos;
bool unredoing;
void DeleteAllRedoActions();
void DeleteAllUndoActions();
public:
UndoData(D& doc, int max);
virtual ~UndoData();
void AddUndoNode(UndoNode<D>*undonode); // adds an action that can be undone
void UndoLastAction(); // undoes the most recent action
void RedoLastUndo(); // redoes the most recent undo
void EnableUndo()
{ undoenabled = true; }
void DisableUndo()
{ undoenabled = false; }
bool IsUndoEnabled() const
{ return undoenabled && !unredoing; }
bool IsUndoDataStored() const
{ return !undodeque.empty(); }
bool WasUndoDataDiscarded() const
{ return discardedundos; }
bool IsRedoDataStored() const
{ return !redostack.empty(); }
};
template <class D>
UndoData<D>::UndoData(D& doc, int max) : doc(doc),
unredoing(false), discardedundos(false), undoenabled(true), maxundos(max)
{
}
template <class D>
UndoData<D>::~UndoData()
{
DeleteAllUndoActions();
DeleteAllRedoActions();
}
template <class D>
void UndoData<D>::DeleteAllUndoActions()
{
while (!undodeque.empty()) {
delete undodeque.back();
undodeque.pop_back();
}
}
template <class D>
void UndoData<D>::DeleteAllRedoActions()
{
while (!redostack.empty()) {
delete redostack.top();
redostack.pop();
}
}
template <class D>
void UndoData<D>::AddUndoNode(UndoNode<D>* undonode)
{
if (undonode != 0) {
// --- clean up the undos saved for possible redos
DeleteAllRedoActions();
// --- prevent the undo deque from growing too large
if (undodeque.size() >= maxundos) {
while (!undodeque.empty() && (undodeque.size() >= maxundos ||
undodeque.front()->Terminator() == false)) {
delete undodeque.front();
undodeque.pop_front();
}
if (!undodeque.empty())
undodeque.front()->Terminator() = true;
else
undonode->Terminator() = true;
discardedundos = true;
}
undodeque.push_back(undonode);
}
}
template <class D>
void UndoData<D>::UndoLastAction()
{
if (!undodeque.empty()) {
bool term = false;
unredoing = true;
UndoNode<D>* undonode = 0;
while (!term) {
assert(!undodeque.empty());
undonode = undodeque.back();
undonode->Undo(doc);
redostack.push(undonode);
undodeque.pop_back();
term = undonode->Terminator();
}
undonode->SetUndoContext(doc);
unredoing = false;
}
}
template <class D>
void UndoData<D>::RedoLastUndo()
{
if (!redostack.empty()) {
bool term = false;
unredoing = true;
UndoNode<D>* undonode = 0;
while (!term) {
undonode = redostack.top();
undonode->Redo(doc);
undodeque.push_back(undonode);
redostack.pop();
// stop if the stack is empty or if the top, unpopped node
// (which is the terminating node of the next sequence) has a terminator
term = redostack.empty() ? true : redostack.top()->Terminator();
}
assert(undonode != 0);
undonode->SetUndoContext(doc);
unredoing = false;
}
}
// ---- operator placement new supports undo enable/disable
template <class D>
void* UndoNode<D>::operator new(size_t sz, UndoData<D>* data)
{
void* p = 0;
if (data->IsUndoEnabled())
p = ::operator new (sz);
return p;
}
} // namespace DDJCProgrammingColumnUndo
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -