One-Line UndoNano provides an efficient type-safe undo mechanism, bringing unlimited undo/redo to any application with one line of code. This is achieved through the NUndoManager class, an instance of which can be placed within objects that need to manage undo (e.g., a document window, or the application itself). OverviewThe basic model for undo is that methods that can perform an undo-able action should first inform the undo manager how to undo it. Nano captures actions as functors, self-contained objects that represent an arbitrary function call. This approach allows applications to achieve "one-line undo", since methods that perform an action can simply record themselves with the current state in order to revert the action: void CShape::SetColor(const NColor &theColor)
{
RecordUndo(BindSelf(CShape::SetColor, mColor), kColorChangeKey);
mColor = theColor;
}
This model is also used by Cocoa's NSUndoManager, albeit more verbosely: - (void) setColor:(const NSColor *) theColor
{
[[[self undoManager] prepareWithInvocationTarget:self]
setColor:mColor];
[[self undoManager] setActionName:NSLocalizedString(
@kColorChangeKey,
@"Color Change")];
[mColor release];
mColor = [theColor copy];
}
In Nano, an undo action captures an object, a method, and an arbitrary list of parameters. The captured method does not need to be declared as static, and will be invoked dynamically with the specified parameters on the captured object. When the user selects a new color, SetColor will record an action that sets the color to its current value and supplies an (optional) localized string for the Edit menu. When the user performs an undo, this action will be invoked and the shape restored to the previous color. NUndoManagerNUndoManager is the core undo object within Nano. It maintains a pair of stacks to hold undo actions, and moves actions between these stacks as the user performs undo or redo operations. Each stack is allowed to grow indefinitely, or up to some limit. Since NUndoManager knows when an undo or redo is being performed, it can also ensure that recorded actions are added to the appropriate stack. I.e., when an action is undone, it will typically record another action to undo the undo-ing - which is automatically placed on the redo stack. NUndoSourceNUndoSource is inherited from by objects that are the source of undoable actions. It provides access to the appropriate undo manager for the object, allowing these to be stacked together in a hierarchy. For example, a view may wish to use the undo manager of its window (which in turn may use the undo manager of the application). It also provides a simplified interface for recording actions. This allows callers to capture both an action to undo, and a localized string to name that action, in one line of code. Undo GroupsThe undo manager can capture multiple actions as a single group, allowing complex operations to appear as a single action to the user. Undo groups can contain an arbitrary number of actions; each item on the undo stack is in fact a group of actions, and size limits imposed on the stack do not affect the number of actions that can be grouped. Groups can be opened/closed around a sequence of actions, or will be opened/closed automatically around individual actions as required. Interactive UndoOne common problem with action-based undo is how to efficiently represent interactive operations, where a large number of actions may be captured due to user input. For example, dragging the mouse around a color picker wheel may trigger hundreds of calls to SetColor before the mouse is released. Each of these actions should be placed within one group, to ensure they appear as a single operation to the user. Although this is straightforward to arrange, undoing or redoing that operation will still need to invoke hundreds of actions each time it is performed in order to reply the intermediate steps recording during the action. To improve the efficiency of this process, NUndoSource::TrackUndo can be used to capture actions within a mouse tracking sequence into a single group. When the mouse is released, TrackUndo will consolidate the group into a single action, ensuring that the group placed on the undo/redo stacks is a more efficient representation of the operation. |