Sometimes it is necessary to implement partial undo functionality for a certain modification method of an object, for example, when the complete object state data is large and a modification method affects only a small part of this data. There are no strict rules for determining whether the amount of object state data is big or not and whether the part of the affected data is small or not; implemnetation of partial undo depends on whether the recorded undo data size savings benefit is worth the effort that is needed to implement it.
Below is a sample implementation of a method with partial object
state recording using an example of the setName()
method of the
MyClass
class.
To use partial undo for
the modification method, first call the assertWriteEnabled()
method with
autoUndo
parameter set to false
.
From this point the modification method is responsible for recording undo data by itself because automatic recording is turned off.
Next the method tries to get the undo filer of the object by calling
undoFiler()
. If getting the undo filer is successful, the method starts
saving data. Each record in the undo filer must contain the following
fields:
OdDbUndoFiler
class.
After the current object state is recorded by the undo filer, the method
can apply changes to the object's properties.
void MyClass::setName(const OdString& s)
{
if (m_sName == s)
return; // Nothing to do
assertWriteEnabled(false); // Do not write automatic (complete) undo
OdDbUndoFiler* pFiler = (OdDbUndoFiler*)undoFiler());
if (pFiler)
{ // Undo recording enabled. Write partial undo
pFiler->wrClass(desc()); // Write class identifier
pFiler->wrInt16(OdUInt16(kPU_Rename)); // Write operation code
pFiler->wrString(m_sName); // Write old value
}
m_sName = s; // Set new value
}
If at least one modification method of an object uses partial undo,
override the applyPartialUndo()
method for this object so that restoring the object state is held correctly.
The applyPartialUndo()
method first checks
that the class type specified by the pClass
parameter matches the class type
of the object. If it does not, the method must call applyPartialUndo()
of
the parent class.
If the class types match, the method must use the undo filer specified by
the pFiler
parameter to read the operation code (the class description
was already read from the undo filer to determine which object must restore its state).
When the operation is identified, the method must read the data from the undo filer
and apply it to the object using its appropriate set()
method.
Note: The applyPartialUndo()
method is responsible for
correctly reading data from the undo filer. It must know which types of
fields to scan and must stop after reading what it needs.
void MyClass::applyPartialUndo(OdDbDwgFiler* pFiler, OdRxClass* pClass)
{
if (pClass == desc())
{ // This is my class partial undo
OdInt16 opCode = pFiler->rdInt16();
switch (opCode)
{
case kPU_Rename:
// Do not change data member directly
// setName() will write partial undo for "redo" operation
setName(pFiler->rdString());
break;
case :
break;
...
default:
ODA_ASSERT_ONCE(!L"Unknown partial undo operation code");
}
}
else
{ // It's undo of parent class. Call parent's method
MyClassParent::applyPartialUndo(pFiler, pClass);
}
}
Implementing Undo and Redo Operations
Copyright © 2002 – 2021. Open Design Alliance. All rights reserved.
|