Common edit design pattern.

Messaging is an important aspect of application development, especially in a UI-driven application.

While working on the undo architecture of an application I’m putting together, I ran into an interesting problem, and a general-purpose architecture which could handle this popped into my mind, that I thought I’d jot down for future reference. Feel free to borrow the idea if it seems useful, and pardon me if the idea is blindingly obvious.

A typical editing architecture essentially can be represented as a state S which represents the current state of the document, and a set of messages fi which transforms the state Si to a new state Si+1. Each message f represents an editing change to the document S.

Now a typical undo architecture handles undoing messages by storing the creation of messages f’ which is the inverse of f. When an edit operation f is generated and document S is transformed, an inverse operation f’ is created (in practice, as part of the code implementing the change message f) and added to a stack of undo operations, which can then be used to undo the operation on S. In theory as long as each operation has an inverse, we can maintain a perfect undo stack. In practice, of course, user considerations muck up this perfect world: for a text editor, for example, individual keystrokes f(key) may be coalesced into a single insert operation for purposes of an undo operation. (In that case, the easiest way to maintain this undo state is to allow the code creating the f’ function to peek at the top of the undo stack, to determine if the existing top of the undo stack can simply be extended.)

A second thing that one can do with messages f to a state S is that you can then use those messages to determine if S has changed, in order to update the user interface element displaying the document S. For example, if you have two text editors opened to the same document, each keystroke f(key) can trigger a message from S (call it ui) which triggers a user interface update.

Now I was working on an application which had multiple user interface elements editing the same object, and I was generating messages ui which was being used to update my user interface elements. The bug I had was that, when I would edit a state, the user interface would be updated–because my UI handling code would do:


And of course on undo, the undo code would do the equivalent of:


It strikes me that there are two solutions to this problem, though unfortunately most UI frameworks I’ve encountered makes the second solution a bit harder to implement in a natural way.

The first solution is to always use the state changed messages ui in order to drive the user interface refresh cycle. Sometimes this may not be the easiest thing to implement, however: some basic controls (such as check boxes and radio boxes) update their state automatically and really, updating their state again on receiving a state changed message is redundant.

The second solution is, on creation of an edit change request, to automatically note in that request the source of the change: thus, one of the parameters of a change request f is the originating object that sent the change. (For an undo change, the requester would be set to a value indicating that the message came from the undo manager.) The state changed messages would then pass the source of the change request on in the ui messages, so that a user interface element receiving the message can then choose to ignore the message if needed.

I personally like the second solution better than the first; the added flexibility allows my user interface control code to decide how to handle a state change. Unfortunately the undo manager in both the MacOS X Cocoa Framework and the Java Swing Undo manager would require a bit of extra bookkeeping in order to properly set the source of a state change. However, I think the extra work may be worth it on a future project.

Leave a Reply

Please log in using one of these methods to post your comment: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s