PATH Documentation > WebObjects

Table of Contents

NSUndoManager


Inherits from:
Object
Implements:
NSDisposable
Serializable
Package:
com.webobjects.foundation


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. However, users typically see undo and redo as application features. WebObjects applications can use NSUndoManagers to undo and redo user operations. Typically a session's editing context has an undo manager that provides undo and redo operations on enterprise objects. For more information, see the class specification for EOEditingContext (eocontrol package).


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 first name and the last name of an employee. An application might package both operations as a group, so when the user chooses Undo, both the first and last names are reverted. To undo a single operation, the operation must be packaged alone 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 an undo is performed. Performing an 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. To register the undo operation you specify a selector with a single object argument. When an object changes, the object itself (or another object acting on its behalf) records its attributes prior to the change in the argument object. (This argument is frequently an NSDictionary object, but it can be any object.) Performing the undo then involves resetting the object with these attributes.

To record a simple undo operation, you need only invoke registerUndoWithTarget, 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. If you have multiple arguments, use registerUndoWithTargetAndArguments.

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 a single 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.


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 throws 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.


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 CheckpointNotification 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" (page 335).




Constants


NSUndoManager provides the following constant to specify the priority of closing an undo group compared to other operations that take place after the current event ends. Undo groups are automatically closed at the end of the event. The priority specified by this constant is lower than the priority for an EOEditingContext to flush its changes.


Constant Type Description
UndoCloseGroupingRunLoopOrdering int Specifies the priority for closing the current undo group compared to other operations in the delayed callback queue.



Interfaces Implemented


NSDisposable
dispose


Method Types


Registering undo operations
registerUndoWithTarget
registerUndoWithTargetAndArguments
Checking undo ability
canUndo
canRedo
Performing undo and redo
undo
undoNestedGroup
redo
Limiting the undo stack
setLevelsOfUndo
levelsOfUndo
Creating undo groups
beginUndoGrouping
endUndoGrouping
setGroupsByEvent
groupsByEvent
groupingLevel
Disabling undo
disableUndoRegistration
enableUndoRegistration
isUndoRegistrationEnabled
Checking whether undo or redo is being performed
isUndoing
isRedoing
Clearing undo operations
removeAllActions
removeAllActionsWithTarget


Constructors



NSUndoManager

public NSUndoManager()

Creates an NSUndoManager object.


Instance Methods



beginUndoGrouping

public 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 CheckpointNotification unless a top-level undo is in progress. It posts a DidOpenUndoGroupNotification if a new group was successfully created.



canRedo

public boolean canRedo()

Returns true if the receiver has any actions to redo, false if it doesn't.

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



canUndo

public boolean canUndo()

Returns true if the receiver has any actions to undo, false 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: enableUndoRegistration, registerUndoWithTarget



disableUndoRegistration

public void disableUndoRegistration()

Disables the recording of undo operations, whether by registerUndoWithTarget 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.

dispose

public void dispose()

Conformance to NSDisposable. See the method description of dispose in the interface specification for NSDisposable.

enableUndoRegistration

public 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. Throws an IllegalStateException if invoked while no disableUndoRegistration message is in effect.

endUndoGrouping

public 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. Throws an IllegalStateException if there's no beginUndoGrouping message in effect.

This method posts an CheckpointNotification and an WillCloseUndoGroupNotification just before the group is closed.

See Also: levelsOfUndo



groupingLevel

public 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

public boolean groupsByEvent()

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

See Also: beginUndoGrouping



isRedoing

public boolean isRedoing()

Returns true if the receiver is in the process of performing its redo method, false otherwise.

isUndoRegistrationEnabled

public boolean isUndoRegistrationEnabled()

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

See Also: disableUndoRegistration, enableUndoRegistration



isUndoing

public boolean isUndoing()

Returns true if the receiver is in the process of performing an undo or undoNestedGroup, false otherwise.

levelsOfUndo

public int levelsOfUndo()

Returns the maximum number of top-level undo groups the receiver will hold. If ending the current undo group will result 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



redo

public 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. Throws an IllegalStateException if the method is invoked during an undo operation.

This method posts an CheckpointNotification and WillRedoChangeNotification before it performs the redo operation, and it posts the DidRedoChangeNotification after it performs the redo operation.

See Also: registerUndoWithTarget



registerUndoWithTarget

public void registerUndoWithTarget( Object target, NSSelector aSelector, Object 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. See "Registering Undo Operations" (page 325) in the class description for more information.

Throws an IllegalStateException 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, groupingLevel



registerUndoWithTargetAndArguments

public void registerUndoWithTargetAndArguments( Object target, NSSelector selector, Object[] parameters[])



removeAllActions

public void removeAllActions()

Clears the undo and redo stacks and reenables the receiver.

See Also: enableUndoRegistration, removeAllActionsWithTarget



removeAllActionsWithTarget

public void removeAllActionsWithTarget(Object 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 finalize. <<True?>>

See Also: enableUndoRegistration, removeAllActions



setGroupsByEvent

public void setGroupsByEvent(boolean flag)

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

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

See Also: groupingLevel, groupsByEvent



setLevelsOfUndo

public void setLevelsOfUndo(int levels)

Sets the maximum number of top-level undo groups the receiver will hold to levels. 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



undo

public void undo()

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

This method posts an "CheckpointNotification" (page 336).

See Also: enableUndoRegistration, groupingLevel



undoNestedGroup

public 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. Throws an InternalInconsistencyException if any undo operations have been registered since the last enableUndoRegistration message.

This method posts an "CheckpointNotification" (page 336) and "WillUndoChangeNotification" (page 338) before it performs the undo operation, and it posts the "DidUndoChangeNotification" (page 337) after it performs the undo operation.

See Also: undo




Notifications


CheckpointNotification

public static final String CheckpointNotification

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

notification object
The NSUndoManager
userInfo
null

DidOpenUndoGroupNotification

public static final String DidOpenUndoGroupNotification

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

notification object
The NSUndoManager
userInfo
null

DidRedoChangeNotification

public static final String DidRedoChangeNotification

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

notification object
The NSUndoManager
userInfo
null

DidUndoChangeNotification

public static final String DidUndoChangeNotification

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
null

WillCloseUndoGroupNotification

public static final String WillCloseUndoGroupNotification

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

notification object
The NSUndoManager
userInfo
null

WillRedoChangeNotification

public static final String WillRedoChangeNotification

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

notification object
The NSUndoManager
userInfo
null

WillUndoChangeNotification

public static final String WillUndoChangeNotification

Posted just before 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
null

© 2001 Apple Computer, Inc. (Last Published April 17, 2001)


Table of Contents