PATH  Documentation > Mac OS X > Foundation Reference: Objective-C



Table of Contents

NSUndoManager


Inherits from:
NSObject
Conforms to:
NSObject
(NSObject)
Declared in:
Foundation/NSUndoManager.h




Class Description


NSUndoManager is a general-purpose recorder of operations for undo and redo. You register an undo operation by specifying the object that's changing (or the owner of that object), along with a method to invoke to revert its state, and the arguments for that method. NSUndoManager groups all operations within a single cycle of the run loop, so that performing an undo reverts all changes that occurred during the loop. Also, when performing undo an NSUndoManager saves the operations reverted so that you can redo the undos.

NSUndoManager is implemented as a class of the Foundation framework because executables other than applications might want to revert changes to their states. For example, you might have an interactive command-line tool with undo and redo commands; or there could be Distributed Object implementations that can revert operations "over the wire." However, users typically see undo and redo as application features. The Application Kit implements undo and redo in its NSTextView object and makes it easy to implement it in objects along the responder chain. For more on the role of the Application Kit in undo and redo, see "Undo in Applications" .


Operations and Groups


An undo operation is a method for reverting a change to an object, along with the arguments needed to revert the change (for example, its state before the change). Undo operations are typically collected in undo groups, which represent whole revertible actions, and are stored on a stack. Redo operations and groups are simply undo operations stored on a separate stack (described below). When an NSUndoManager performs undo or redo, it's actually undoing or redoing an entire group of operations. For example, a user could change the type face and the font size of some text. An application might package both attributed-setting operations as a group, so when the user chooses Undo, both type face and font size are reverted. To undo a single operation, it must still be packaged in a group.

NSUndoManager normally creates undo groups automatically during the run loop. The first time it's asked to record an undo operation in the run loop, it creates a new group. Then, at the end of the loop, it closes the group. You can create additional, nested undo groups within these default groups using the beginUndoGrouping and enableUndoRegistration methods. You can also turn off the default grouping behavior using setGroupsByEvent:.


The Undo and Redo Stacks


Undo groups are stored on a stack, with the oldest groups at the bottom and the newest at the top. The undo stack is unlimited by default, but you can restrict it to a maximum number of groups using the setLevelsOfUndo: method. When the stack exceeds the maximum, the oldest undo groups are dropped from the bottom.

Initially, both stacks are empty. Recording undo operations adds to the undo stack, but the redo stack remains empty until undo is performed. Performing undo causes the reverting operations in the latest group to be applied to their objects. Since these operations cause changes to the objects' states, the objects presumably register new operations with the NSUndoManager, this time in the reverse direction from the original operations. Since the NSUndoManager is in the process of performing undo, it records these operations as redo operations on the redo stack. Consecutive undos add to the redo stack. Subsequent redo operations pull the operations off the redo stack, apply them to the objects, and push them back onto the undo stack.

The redo stack's contents last as long as undo and redo are performed successively. However, because applying a new change to an object invalidates the previous changes, as soon as a new undo operation is registered, the redo stack is cleared. This prevents redo from returning objects to an inappropriate prior state. You can check for the ability to undo and redo with the canUndo and canRedo methods.


Registering Undo Operations


To add an undo operation to the undo stack, you must register it with the object that will perform the undo operation. NSUndoManager supports two ways to register undo operations: one based on a simple selector with a single object argument, and one based on a general NSInvocation (which allows any number and type of arguments). In the first type of operation, when an object changes, the object itself (or another object acting on its behalf) records in the object passed as argument the attributes of the object prior to the change. (This argument is frequently an NSDictionary object, but it can be any object.) Performing the undo then involves resetting the object with these attributes. Invocation-based undo is useful for undoing specific state-changing methods, such as a setFont:color: method.

In most applications a single instance of NSUndoManager belongs to an object that contains or manages other objects. This is particularly the case with document-based applications, where each NSDocument object is responsible for all undo and redo operations for a document. An object such as this is often called the NSUndoManager's client. Each client object has its own NSUndoManager. The client claims exclusive right to alter its undoable objects so that it can record undo operations for all changes. In the specific case of documents, this scheme keeps each pair of undo and redo stacks separate so that when an undo is performed, it applies to the focal document in the application (typically the one displayed in the key window). It also relieves the individual objects in a document from having to know the identity of their NSUndoManager or from having to track changes to themselves.

However, an object that is changed can have its own NSUndoManager and perform its own undo and redo operations. For example, you could have a custom view that displays images dragged into it; with each successful drag operation, it registers a new undo group. If the view is then selected (that is, made first responder) and the Undo command applied, the previously displayed image would be redisplayed.


Simple Undo


To record a simple undo operation, you need only invoke registerUndoWithTarget:selector:object:, giving the object to be sent the undo operation selector, the selector to invoke, and an argument to pass with that message. The target object is usually not the actual object whose state is changing; instead, it's the client object, a document or container that holds many undoable objects. The argument is an object that captures the state of the object before the change is made. Here is a method from the Draw example application (DrawDocument.m):

- (void)setGridVisible:(NSNumber *)flag {
    BOOL flagValue = [flag boolValue];
    if (gvFlags.showGrid != flagValue) {
        NSNumber *currentValue = [NSNumber numberWithBool:gvFlags.showGrid];
        gvFlags.showGrid = flagValue;
        if (flagValue)
            [graphicView resetGUP];
        [graphicView cache:[graphicView bounds]];
        [undoManager registerUndoWithTarget:self selector:@selector(setGridVisible:) object: currentValue];
        [undoManager setActionName:GRID_OP];
    } 

If the user chooses Undo, setGridVisible: is invoked with the previous value:


Invocation-Based Undo


For other changes involving specific methods or arguments that aren't objects, you can use invocation-based undo, which records an actual message to revert the target object's state. As with simple undo, you record a message that reverts the object to its state before the change. However, in this case you do so by sending the message directly to the NSUndoManager, after preparing it with a special message to note the target:

[[myUndoManager prepareWithInvocationTarget:drawObject]
    setFont:[drawObject font] color:[drawObject color]];
[drawObject setFont:newFont color:newColor];

The prepareWithInvocationTarget: method records the argument as the target of the undo operation about to be established. Following this, you send the message that will revert the target's state-in this case, setFont:color:. Because NSUndoManager doesn't respond to this method, forwardInvocation: is invoked, which NSUndoManager implements to record the NSInvocation containing the target, selector, and all arguments. Performing undo thus results in drawObject being sent a setFont:color: message with the old values.


Performing Undo and Redo


Performing undo and redo is usually as simple as sending undo and redo messages to the NSUndoManager. undo closes the last open undo group and then applies all the undo operations in that group (recording any undo operations as redo operations instead). redo likewise applies all the redo operations on the top redo group.

undo is intended for undoing top-level groups, and shouldn't be used for nested undo groups. If any unclosed, nested undo groups are on the stack when undo is invoked, it raises an exception. To undo nested groups, you must explicitly close the group with an enableUndoRegistration message, then use undoNestedGroup to undo it. Note also that if you turn off automatic grouping by event with setGroupsByEvent:, you must explicitly close the current undo group with enableUndoRegistration before invoking either undo method.


Cleaning the Undo Stack


NSUndoManager doesn't retain the targets of undo operations, for several reasons. Foremost is that the client-the object performing undo operations-typically owns the NSUndoManager; thus for the NSUndoManager to retain the target would create cycles. The NSUndoManager does contain references to the targets of undo operations, however, which it uses to send undo messages when undo is performed. If a target object has been deallocated, and an undo message is sent to it, errors will result.

To remedy this, the client must take care to clear undo operations for targets that are being deallocated. This typically occurs in one of three ways:

In a more general sense, it sometimes makes sense to clear all undo and redo operations. Some applications might want to do this when saving a document, for example. To this end, NSUndoManager defines the removeAllActions method, which clears both stacks.


Setting Action Names


NSUndoManager provides methods for qualifying the Undo and Redo command titles in the Edit menu. Take, as an example, a graphics application that allows users to add circles, fill them with a color, and delete them. With the methods defined here, the application could, upon successive undos, make the titles "Undo Delete," "Undo Fill," and "Undo Add Circle."

These extensions automatically localize Undo and Redo command titles. They also provide two methods you can override to customize how these titles are localized: undoMenuTitleForUndoActionName: and redoMenuTitleForUndoActionName:.


Undo Notifications


An NSUndoManager regularly posts checkpoint notifications to synchronize the inclusion of undo operations in undo groups. Objects sometimes delay performing changes, for various reasons. This means they may also delay registering undo operations for those changes. Because NSUndoManager collects individual operations into groups, it must be sure to synchronize its client with the creation of these groups so that operations are entered into the proper undo groups. To this end, whenever an NSUndoManager opens or closes a new undo group (except when it opens a top-level group), it posts an "NSUndoManagerCheckpointNotification" so observers can apply their pending undo operations to the group in effect. The NSUndoManager's client should register itself as an observer for this notification and record undo operations for all pending changes upon receiving it.

NSUndoManager also posts a number of other notifications at specific intervals: when a group is created, when a group is closed, and just before and just after both undo and redo operations. For more on notifications, see "Notifications" .


Undo in Applications


The Application Kit supplements the behavior of NSUndoManager in several ways. It offers default undo and redo behavior in text. It includes APIs for managing the action names that appear with "Undo" and "Redo" in an application's menu. And it establishes a framework for the distribution and selection of NSUndoManagers in an application.


Undo and the Responder Chain


As stated earlier, an application can have one or more clients: objects that register and perform undo operations in their local contexts. Each of these objects has its own NSUndoManager and the associated undo and redo stacks. One example of this scenario involves custom views, each a client of an NSUndoManager. For example, you could have a window with two custom views; each view can display text in changeable attributes (such as font, color, and size) and users can undo (or redo) each change to any attribute in either of the views. NSResponder and NSWindow define methods to help you control the context of undo operations within the view hierarchy.

NSResponder declares the undoManager method for most objects that inherit from it (namely, windows and views). When the first responder of an application receives an undo or redo message, NSResponder goes up the responder chain looking for a next responder that returns an NSUndoManager object from undoManager . Any returned NSUndoManager object is used for the undo or redo operation. If the undoManager message wends its way up the responder chain to the window, the NSWindow object queries its delegate with windowWillReturnUndoManager: to see if the delegate has an NSUndoManager. If the delegate does not implement this method, the NSWindow creates an NSUndoManager for the window and all its views.

Document-based applications often make their NSDocument objects the delegates of their windows and have them respond to the windowWillReturnUndoManager: message by returning the NSUndoManager used for the document. These applications can also make each NSWindowController the delegate of its window-the window controller implements windowWillReturnUndoManager: to get the NSUndoManager from its document and return it, for example:

return [[self document] undoManager];


NSTextView


NSTextViews provide undo and redo behavior by default. For your application to take advantage of this feature, however, it must send setAllowsUndo: with an argument of YES to the text view. If you want a text view to have its own NSUndoManager (and not the window's), have the text view's delegate implement undoManagerForTextView:, to return the NSUndoManager.

The default undo and redo behavior applies to text fields and text in cells as long as the field or cell is the first responder (that is, the focus of keyboard actions). Once the insertion point leaves the field or cell, prior operations cannot be undone.




Constants


NSUndoManager provides the following constant as a convenience; you can use it to compare to values returned by some NSUndoManager methods:


Constant Type Description
NSUndoCloseGroupingRunLoopOrdering int Used with NSRunLoop's performSelector:target:argument:order:modes:



Method Types


Registering undo operations
- registerUndoWithTarget:selector:object:
- prepareWithInvocationTarget:
- forwardInvocation:
Checking undo ability
- canUndo
- canRedo
Performing undo and redo
- undo
- undoNestedGroup
- redo
Limiting the undo stack
- setLevelsOfUndo:
- levelsOfUndo
Creating undo groups
- beginUndoGrouping
- endUndoGrouping
- enableUndoRegistration
- groupsByEvent
- setGroupsByEvent:
- groupingLevel
Disabling undo
- disableUndoRegistration
- enableUndoRegistration
- isUndoRegistrationEnabled
Checking whether undo or redo is being performed
- isUndoing
- isRedoing
Clearing undo operations
- removeAllActions
- removeAllActionsWithTarget:
Setting and getting the action name
- setActionName:
- redoActionName
- undoActionName
Getting and localizing menu item title
- redoMenuItemTitle
- undoMenuItemTitle
- redoMenuTitleForUndoActionName:
- undoMenuTitleForUndoActionName:
Working with run loops
- runLoopModes
- setRunLoopModes:


Instance Methods



beginUndoGrouping

- (void)beginUndoGrouping

Marks the beginning of an undo group. All individual undo operations before a subsequent endUndoGrouping message are grouped together and reversed by a later undo message. By default undo groups are begun automatically at the start of the event loop, but you can begin your own undo groups with this method, and nest them within other groups.

This method posts an "NSUndoManagerCheckpointNotification" unless a top-level undo is in progress. It posts a "NSUndoManagerDidOpenUndoGroupNotification" if a new group was successfully created.



canRedo

- (BOOL)canRedo

Returns YES if the receiver has any actions to redo, NO if it doesn't.

Because any undo operation registered clears the redo stack, this method posts an "NSUndoManagerCheckpointNotification" to allow clients to apply their pending operations before testing the redo stack.

See Also: - canUndo, - redo



canUndo

- (BOOL)canUndo

Returns YES if the receiver has any actions to undo, NO if it doesn't. This does not mean you can safely invoke undo or undoNestedGroup-you may have to close open undo groups first.

See Also: - canRedo, - enableUndoRegistration, - registerUndoWithTarget:selector:object:



disableUndoRegistration

- (void)disableUndoRegistration

Disables the recording of undo operations, whether by registerUndoWithTarget:selector:object: or by invocation-based undo. This method can be invoked multiple times by multiple clients. enableUndoRegistration must be invoked an equal number of times to re-enable undo registration.

enableUndoRegistration

- (void)enableUndoRegistration

Enables the recording of undo operations. Because undo registration is enabled by default, it is often used to balance a prior disableUndoRegistration message. Undo registration isn't actually re-enabled until an enable message balances the last disable message in effect. Raises an NSInternalInconsistencyException if invoked while no disableUndoRegistration message is in effect.

endUndoGrouping

- (void)endUndoGrouping

Marks the end of an undo group. All individual undo operations back to the matching beginUndoGrouping message are grouped together and reversed by a later undo or undoNestedGroup message. Undo groups can be nested, thus providing functionality similar to nested transactions. Raises an NSInternalInconsistencyException if there's no beginUndoGrouping message in effect.

This method posts an "NSUndoManagerCheckpointNotification" and an "NSUndoManagerWillCloseUndoGroupNotification" just before the group is closed.

See Also: - levelsOfUndo



forwardInvocation:

- (void)forwardInvocation:(NSInvocation *)anInvocation

Overrides NSObject's implementation to record anInvocation as an undo operation. Also clears the redo stack. You can override this method if you want different or supplementary invocation-based behavior. See "Invocation-Based Undo" in the class description for more information.

Raises an NSInternalInconsistencyException if prepareWithInvocationTarget: wasn't invoked before this method. This method then clears the prepared invocation target. Also raises an NSInternalInconsistencyException if invoked when no undo group has been established using beginUndoGrouping. Undo groups are normally set by default, so you should rarely need to begin a top-level undo group explicitly.

See Also: - undoNestedGroup, - registerUndoWithTarget:selector:object:, - groupingLevel



groupingLevel

- (int)groupingLevel

Returns the number of nested undo groups (or redo groups, if Redo was last invoked) in the current event loop. If zero is returned, there is no open undo or redo group.

See Also: - levelsOfUndo, - setLevelsOfUndo:



groupsByEvent

- (BOOL)groupsByEvent

Returns YES if the receiver automatically creates undo groups around each pass of the run loop, NO if it doesn't. The default is YES.

See Also: - beginUndoGrouping, - setGroupsByEvent:



isRedoing

- (BOOL)isRedoing

Returns YES if the receiver is in the process of performing its redo method, NO otherwise.

See Also: - isUndoing



isUndoRegistrationEnabled

- (BOOL)isUndoRegisgrationEnabled

Returns whether the recording of undo operations is enabled. Undo registration is enabled by default.

See Also: - disableUndoRegistration, - enableUndoRegistration



isUndoing

- (BOOL)isUndoing

Returns YES if the receiver is in the process of performing its undo or undoNestedGroup method, NO otherwise.

See Also: - isRedoing



levelsOfUndo

- (unsigned)levelsOfUndo

Returns the maximum number of top-level undo groups the receiver will hold. When ending an undo group results in the number of groups exceeding this limit, the oldest groups are dropped from the stack. A limit of zero indicates no limit, so old undo groups are never dropped. The default is zero.

See Also: - enableUndoRegistration, - setLevelsOfUndo:



prepareWithInvocationTarget:

- (id)prepareWithInvocationTarget:(id)target

Prepares the receiver for invocation-based undo with target as the subject of the next undo operation and returns self. See " "Invocation-Based Undo" " in the class description for more information.

See Also: - forwardInvocation:



redo

- (void)redo

Performs the operations in the last group on the redo stack, if there are any, recording them on the undo stack as a single group. Raises an NSInternalInconsistencyException if the method is invoked during an undo operation.

This method posts an "NSUndoManagerCheckpointNotification" and "NSUndoManagerWillRedoChangeNotification" before it performs the redo operation, and it posts the "NSUndoManagerDidRedoChangeNotification" after it performs the redo operation.

See Also: - registerUndoWithTarget:selector:object:



redoActionName

- (NSString *)redoActionName

Returns the name identifying the redo action. For example, if the menu title is "Redo Delete," the string returned is "Delete." Returns an empty string if no action name has been assigned or nil if there is nothing to redo.

See Also: - setActionName:, - undoActionName



redoMenuItemTitle

- (NSString *)redoMenuItemTitle

Returns the complete title of the Redo menu command, for example, "Redo Paste." Returns "Redo" if no action name has been assigned or nil if there is nothing to redo.

See Also: - undoMenuItemTitle



redoMenuTitleForUndoActionName:

- (NSString *)redoMenuTitleForUndoActionName:(NSString *)actionName

Returns the complete, localized title of the Redo menu command for the action identified by actionName. Override this method if you want to customize the localization behavior. This method is invoked by redoMenuItemTitle.

See Also: - undoMenuTitleForUndoActionName:



registerUndoWithTarget:selector:object:

- (void)registerUndoWithTarget:(id)target selector:(SEL)aSelector object:(id)anObject

Records a single undo operation for target, so that when undo is performed it's sent aSelector with anObject as the sole argument. Also clears the redo stack. Doesn't retain target. See "Simple Undo" in the class description for more information.

Raises an NSInternalInconsistencyException if invoked when no undo group has been established using beginUndoGrouping. Undo groups are normally set by default, so you should rarely need to begin a top-level undo group explicitly.

See Also: - undoNestedGroup, - forwardInvocation:, - groupingLevel



removeAllActions

- (void)removeAllActions

Clears the undo and redo stacks and reenables the receiver.

See Also: - enableUndoRegistration, - removeAllActionsWithTarget:



removeAllActionsWithTarget:

- (void)removeAllActionsWithTarget:(id)target

Clears the undo and redo stacks of all operations involving target as the recipient of the undo message. Doesn't re-enable the receiver if it's disabled. An object that shares an NSUndoManager with other clients should invoke this message in its implementation of dealloc.

See Also: - enableUndoRegistration, - removeAllActions



runLoopModes

- (NSArray *)runLoopModes

Returns the modes governing the types of input handled during a cycle of the run loop. By default, the sole run-loop mode is NSDefaultRunLoopMode (which excludes data from NSConnections).

See Also: - setRunLoopModes:, - performSelector:target:argument:order:modes: (NSRunLoop)



setActionName:

- (void)setActionName:(NSString *)actionName

Sets the name of the action associated with the Undo or Redo command to actionName. If actionName is an empty string, the action name currently associated with the menu command is removed. There is no effect if actionName is nil.

See Also: - redoActionName, - undoActionName



setGroupsByEvent:

- (void)setGroupsByEvent:(BOOL)flag

Sets whether the receiver automatically groups undo operations during the run loop. If flag is YES, the receiver creates undo groups around each pass through the run loop; if flag is NO it doesn't. The default is YES.

If you turn automatic grouping off, you must close groups explicitly before invoking either undo or undoNestedGroup.

See Also: - groupingLevel, - groupsByEvent



setLevelsOfUndo:

- (void)setLevelsOfUndo:(unsigned)anInt

Sets the maximum number of top-level undo groups the receiver will hold to anInt. When ending an undo group results in the number of groups exceeding this limit, the oldest groups are dropped from the stack. A limit of zero indicates no limit, so that old undo groups are never dropped. The default is zero.

If invoked with a limit below the prior limit, old undo groups are immediately dropped.

See Also: - enableUndoRegistration, - levelsOfUndo



setRunLoopModes:

- (void)setRunLoopModes:(NSArray *)modes

Sets the modes that determine the types of input handled during a cycle of the run loop. By default, the sole run-loop mode is NSDefaultRunLoopMode (which excludes data from NSConnections). With this method, you could limit the input to data received during a mouse-tracking session by setting the mode to NSEventTrackingRunLoopMode, or you could limit it to data received from a modal panel with NSModalPanelRunLoopMode.

See Also: - runLoopModes, - performSelector:target:argument:order:modes: (NSRunLoop)



undo

- (void)undo

Closes the top-level undo group if necessary and invokes undoNestedGroup. It also invokes endUndoGrouping if the nesting level is 1. Raises an NSInternalInconsistencyException if more than one undo group is open (that is, if the last group isn't at the top level).

This method posts an "NSUndoManagerCheckpointNotification" .

See Also: - enableUndoRegistration, - groupingLevel



undoActionName

- (NSString *)undoActionName

Returns the name identifying the undo action. For example, if the menu title is "Undo Delete," the string returned is "Delete." Returns an empty string if no action name has been assigned or nil if there is nothing to undo.

See Also: - redoActionName, - setActionName:



undoMenuItemTitle

- (NSString *)undoMenuItemTitle

Returns the complete title of the Undo menu command, for example, "Undo Paste." Returns "Undo" if no action name has been assigned or nil if there is nothing to undo.

See Also: - redoMenuItemTitle



undoMenuTitleForUndoActionName:

- (NSString *)undoMenuTitleForUndoActionName:(NSString *)actionName

Returns the complete, localized title of the Undo menu command for the action identified by actionName. Override this method if you want to customize the localization behavior. This method is invoked by undoMenuItemTitle.

See Also: - redoMenuTitleForUndoActionName:



undoNestedGroup

- (void)undoNestedGroup

Performs the undo operations in the last undo group (whether top-level or nested), recording the operations on the redo stack as a single group. Raises an NSInternalInconsistencyException if any undo operations have been registered since the last enableUndoRegistration message.

This method posts an "NSUndoManagerCheckpointNotification" and "NSUndoManagerWillUndoChangeNotification" before it performs the undo operation, and it posts the "NSUndoManagerDidUndoChangeNotification" after it performs the undo operation.

See Also: - undo




Notifications


NSUndoManagerCheckpointNotification

Posted whenever an NSUndoManager opens or closes an undo group (except when it opens a top-level group), and when checking the redo stack in canRedo. The notification contains:

notification object
The NSUndoManager
userInfo
nil

NSUndoManagerDidOpenUndoGroupNotification

Posted whenever an NSUndoManager opens an undo group, which occurs in the implementation of the beginUndoGrouping method. The notification contains:

notification object
The NSUndoManager
userInfo
nil

NSUndoManagerDidRedoChangeNotification

Posted just after an NSUndoManager performs a redo operation ( redo). The notification contains:

notification object
The NSUndoManager
userInfo
nil

NSUndoManagerDidUndoChangeNotification

Posted just after an NSUndoManager performs an undo operation. If you invoke undo or undoNestedGroup, this notification will be posted. The notification contains:

notification object
The NSUndoManager
userInfo
nil

NSUndoManagerWillCloseUndoGroupNotification

Posted whenever an NSUndoManager closes an undo group, which occurs in the implementation of the endUndoGrouping method. The notification contains:

notification object
The NSUndoManager
userInfo
nil

NSUndoManagerWillRedoChangeNotification

Posted just before an NSUndoManager performs a redo operation ( redo). The notification contains:

notification object
The NSUndoManager
userInfo
nil

NSUndoManagerWillUndoChangeNotification

Posted just after an NSUndoManager performs an undo operation. If you invoke undo or undoNestedGroup, this notification will be posted. The notification contains:

notification object
The NSUndoManager
userInfo
nil


Table of Contents