18.3 Extending UndoManager


Now that we've looked at all of the classes and interfaces in the undo framework, we'll look at a few ideas for extending the functionality it provides.

In this example, we'll extend UndoManager to add a few extra features. The first thing we'll add is the ability to get a list of the edits stored in the manager. This is a simple task of returning the contents of the edits vector inherited from CompoundEdit. We also provide access to an array of significant undoable edits and an array of significant redoable edits. These might be useful in a game like chess, in which we want to provide a list of past moves.

The next major feature we add is support for listeners. At this writing, the current UndoManager does not have any way of notifying you when it receives edits. As we saw in an earlier example, this means that you have to listen to each edit-generating component if you want to update the user interface to reflect new undoable or redoable edits as they occur. In our manager, we simply add the ability to add and remove undoable edit listeners to the undo manager itself. Each time an edit is added, the undo manager fires an UndoableEditEvent to any registered listeners. This way, we can just add the undo manager as a listener to each edit-generating component and then add a single listener to the undo manager to update the UI.

The methods of our new undo manager can be divided into two groups, each supporting one of the two features we're adding. We'll split the source code listing along these lines so we can talk about each set of methods independently. Here's the first half:

// ExtendedUndoManager.java // import javax.swing.event.*; import javax.swing.undo.*; import java.util.Enumeration; import java.util.Vector; // An extension of UndoManager that provides two additional features:  // (1) The ability to add and remove listeners  // (2) The ability to gain more extensive access to the edits being managed public class ExtendedUndoManager extends UndoManager   implements UndoableEditListener {   private ExtendedUndoableEditSupport support =     new ExtendedUndoableEditSupport( );   private Object source; // The source of the last edit   // Return the complete list of edits in an array.   public synchronized UndoableEdit[] getEdits( ) {     UndoableEdit[] array = new UndoableEdit[edits.size( )];     edits.copyInto(array);     return array;   }   // Return all currently significant undoable edits. The first edit is the next one   // to be undone.   public synchronized UndoableEdit[] getUndoableEdits( ) {     int size = edits.size( );     Vector v = new Vector(size);     for (int i=size-1;i>=0;i--) {       UndoableEdit u = (UndoableEdit)edits.elementAt(i);       if (u.canUndo( ) && u.isSignificant( ))         v.addElement(u);     }     UndoableEdit[] array = new UndoableEdit[v.size( )];     v.copyInto(array);     return array;   }   // Return all currently significant redoable edits. The first edit is the next one   // to be redone.   public synchronized UndoableEdit[] getRedoableEdits( ) {     int size = edits.size( );     Vector v = new Vector(size);     for (int i=0; i<size; i++) {       UndoableEdit u = (UndoableEdit)edits.elementAt(i);       if (u.canRedo( ) && u.isSignificant( ))         v.addElement(u);     }     UndoableEdit[] array = new UndoableEdit[v.size( )];     v.copyInto(array);     return array;   }

The first method here is simple. All we do is copy the edits from the edits vector into an array and return it. The other two methods are nearly identical. They are a little more complicated than they ideally should be because we don't have access to the current insertion point into the edits vector (which would split the list right between the undoable and redoable edits). Instead, we just iterate over the elements, building up a list of significant undoable or redoable edits.

Here are the methods we added to support listeners:

  // UndoableEditListener Method Support (ExtendedUndoManager.java, part 2)   //   // Add an edit and notify our listeners.   public synchronized boolean addEdit(UndoableEdit anEdit) {     boolean b = super.addEdit(anEdit);     if (b)       support.postEdit(anEdit); // If the edit was added, notify listeners.     return b;   }   // When an edit is sent to us, call addEdit( ) to notify any of our listeners.   public synchronized void undoableEditHappened(UndoableEditEvent ev) {     UndoableEdit ue = ev.getEdit( );     source = ev.getSource( );     addEdit(ue);   }   // Add a listener to be notified each time an edit is added to this manager.   // This makes it easy to update undo/redo menus as edits are added.   public synchronized void addUndoableEditListener(UndoableEditListener l) {     support.addUndoableEditListener(l);   }   // Remove a listener from this manager.   public synchronized void removeUndoableEditListener(UndoableEditListener l) {     support.removeUndoableEditListener(l);   }   // A simple extension of UndoableEditSupport that lets us specify the event   // source each time we post an edit   class ExtendedUndoableEditSupport extends UndoableEditSupport {     // Post an edit to added listeners.     public synchronized void postEdit(UndoableEdit ue) {       realSource = source; // From our enclosing manager object       super.postEdit(ue);     }   } }

The first method here is a customized implementation of addEdit( ). For the most part, we leave this method to the superclass. The only thing we've added is a call to UndoableEditSupport.postEdit( ). Any time an edit is added to the undo manager, we notify its listeners. The idea is that a single listener, probably responsible for updating an Undo menu, is added to the undo manager.

The next method is the undoableEditHappened( ) method from the UndoableEditListener interface. This is the method called each time any edit-generating component in the application fires an UndoableEdit. In this method, we first store the source of the event (we'll see how we use this shortly) and then call addEdit( ).

The next two methods simply use UndoableEditSupport to manage interested listeners.

Finally, we define a small inner class called ExtendedUndoableEditSupport. This is an extension of UndoableEditSupport that we use to set the correct event source each time the ExtendedUndoManager fires an UndoableEditEvent. Rather than declaring the undo manager as the source of the event, we use the real source of the event that was passed to the undo manager's undoableEditHappened( ) method. Note that realSource, which is a protected field in UndoableEditSupport, becomes the source object in the fired UndoableEditEvent.



Java Swing
Graphic Java 2: Mastering the Jfc, By Geary, 3Rd Edition, Volume 2: Swing
ISBN: 0130796670
EAN: 2147483647
Year: 2001
Pages: 289
Authors: David Geary

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net