18.1 The Swing Undo Facility


The javax.swing.undo package contains two interfaces and seven classes (two of which are exception classes). These, along with a listener interface and event class from the javax.swing.event package, comprise the undo facility shown in Figure 18-1.

Figure 18-1. The Swing undo facility
figs/swng2.1801.gif

Here is a brief overview of each class:

UndoableEdit

The base interface for just about everything else in the undo package. It serves as an abstraction for anything in an application that can be undone.

AbstractUndoableEdit

The default implementation of UndoableEdit provides a starting point for building new UndoableEdit classes. It provides a simple set of rules for determining whether undo and redo requests are allowable (based on whether the edit has already been undone, redone, or killed). Despite its name, it is not an abstract class. This is really just a technicality since the default implementation is not at all useful as it is, so you'd never want to instantiate one.

CompoundEdit

This extension of AbstractUndoableEdit allows multiple edits to be grouped together into a single edit. Those familiar with the classic Design Patterns by Erich Gamma et al. (Addison-Wesley) will recognize this construct as a basic implementation of the Composite pattern.

UndoableEditEvent

This event class can be used to notify listeners that an undoable edit has been made.

UndoableEditListener

The listener interface to which UndoableEditEvents are sent. It contains a single method called undoableEditHappened( ).

UndoManager

An extension of CompoundEdit that can manage a list of edits to be undone or redone in sequence. UndoManager implements UndoableEditListener, so it can be added to many components that generate UndoableEditEvents, allowing it to manage edits from multiple sources in a single undo list.

StateEdit

An extension of AbstractUndoableEdit that can be used for edits that are undone or redone by changing a set of property values representing the state of the application (or some aspect of it) before and after the edit.

StateEditable

This interface must be implemented by objects wishing to use StateEdit. The StateEdit constructor accepts a StateEditable and calls its two methods, storeState( ) and restoreState( ), to manage the editable's state.

UndoableEditSupport

This class provides support facilities for classes that need to support undo. It simplifies the processes of managing listeners, firing events, and grouping multiple edits.

CannotUndoException and CannotRedoException

These exception classes extend RuntimeException and are thrown when an undo or redo attempt is made while the edit is not in the correct state (e.g., calling redo( ) on an edit that has not yet been undone).

Now that we have a basic understanding of what we have to work with, let's look at the details of each of these interfaces and classes.

18.1.1 The UndoableEdit Interface

The UndoableEdit interface defines a set of operations that can be performed on any object that needs to provide undo and redo functionality. Typically, classes that implement this interface are fairly small. Instances of these classes represent single, undoable changes ("edits") made to the state of the application or some component of the application.

UndoableEdits can be thought of as being in one of three states, as shown in the state diagram in Figure 18-2. Since UndoableEdit is just an interface, it can't really enforce this state model. However, this is the intended state model, and the AbstractUndoableEdit class described in the next section does enforce it. Alternate implementations of this interface should not deviate from this model.

Figure 18-2. UndoableEdit state chart
figs/swng2.1802.gif

When initially created, the edit represents some change that has just been done and is now undoable.[1] Once the edit is undone, it becomes redoable. Having been undone, it may be redone, causing it to become undoable again. This sequence can be repeated indefinitely. If, for whatever reason, an edit can no longer be used, it can be "killed," taking it to the dead state. Once killed, the edit can no longer be undone or redone. Dead is dead.

[1] Throughout this chapter, we'll use italics when we refer to the "state" of an edit. These states do not usually map directly to any field or method provided by the undo classes; they simply provide an easy way to talk about the state of an edit.

The die( ) method provides a mechanism for edits to explicitly release any resources they may be holding, rather than waiting until the edits are garbage-collected.

18.1.1.1 Properties

Table 18-1 shows the properties defined by UndoableEdit.

Table 18-1. UndoableEdit properties

Property

Data type

get

is

set

Default value

presentationName

String

·

     

redoPresentationName

String

·

     

significant

boolean

 

·

   

undoPresentationName

String

·

     

UndoableEdits are typically displayed to a user, allowing the user to decide to undo or redo some action that was performed. In support of this, UndoableEdit provides three properties that name an edit: presentationName , undoPresentationName, and redoPresentationName. These properties might have values such as delete, undo delete, and redo delete, respectively. The last two names are typically just variations on the first.

The significant property may be used to distinguish edits of different levels of importance. An insignificant edit, for example, might not be displayed to the user, instead being performed as a side effect of some other edit.

To understand the idea of an insignificant edit, think of a computer chess game. The process of making a move might consist of clicking on a piece, causing the square the piece is sitting on to change color, then clicking on the destination square, completing the move. You might implement the game's undo capability by having an edit responsible for changing the color of the square of the selected piece and another edit representing the move itself. The first edit is not the type of edit you'd want to display to the user. Instead, you'd track this as an insignificant edit.

18.1.1.2 Edit-merging methods

UndoableEdit provides two methods for merging the changes described by two edits into a single edit. Implementations of these methods are under no obligation to support this merging feature. Such implementations simply return false, indicating that no merging of the edits was done.

The addEdit( ) and replaceEdit( ) methods are similar. addEdit( ) asks the edit it is invoked upon to absorb an input edit, and replaceEdit( ) asks the edit if it would like to replace the input edit. The idea is that addEdit( ) is called on an existing edit, passing in a new one, while replaceEdit( ) is called on a new edit, passing in an existing one.

Absorbing edits can be useful in a variety of scenarios. One use of this feature might be to group together a series of "delete-character" edits created as a user holds down the Delete or Backspace key. By merging these into a single edit, the user would be able to undo the series of deletions with a single undo operation, rather than having to undo each individual character.

For another example, consider our chess game again. If a user clicked on three different pieces before deciding which one to move, each subsequent piece selection edit would probably want to replace the previous one so that the temporary selections don't become part of the undoable history.

public abstract boolean addEdit(UndoableEdit anEdit)

This method asks the edit to absorb anEdit. If the edit is able to do so, it returns true. If not, it returns false. If anEdit is absorbed, it is no longer undoable or redoable by itself (i.e., canUndo( ) and canRedo( ) should return false, and undo( ) and redo( ) should throw exceptions). If anEdit is no longer needed, die( ) should be called on it.

public abstract boolean replaceEdit(UndoableEdit anEdit)

This method asks this edit to replace anEdit. If the edit is able to do so, it returns true. If not, it returns false. If anEdit is replaced, it should no longer be undoable or redoable by itself (i.e., canUndo( ) and canRedo( ) should return false, and undo( ) and redo( ) should throw exceptions). If anEdit is no longer needed, die( ) should be called on it.

For more information on these methods, see the discussion of CompoundEdit.

18.1.1.3 Other methods
public abstract boolean canRedo( )

This method returns true if the edit currently can be redone, implying that a subsequent redo( ) call should not throw a CannotRedoException.

public abstract boolean canUndo( )

This method returns true if the edit currently can be undone, implying that a subsequent undo( ) call should not throw a CannotUndoException.

public abstract void die( )

This method is called to indicate that the edit can no longer be undone or redone. Any state being held by the edit can be released, and subsequent calls to undo( ) or redo( ) should throw exceptions.

public abstract void redo( ) throws CannotRedoException

This method is called to redo an edit that has previously been undone. If the edit cannot be redone (perhaps because it has not been undone yet), a CannotRedoException should be thrown.

public abstract void undo( ) throws CannotUndoException

Called to undo an edit. If the edit cannot be undone (perhaps because it has already been undone), a CannotUndoException should be thrown.

18.1.2 The AbstractUndoableEdit Class

This implementation of UndoableEdit provides useful default behavior for the methods defined in the interface. It enforces the state model described in the previous section using two internal boolean properties, alive and done. A new AbstractUndoableEdit is both alive and done. A call to die( ) makes it no longer alive. A call to undo( ) makes it no longer done, while redo( ) makes it done again. In order for an edit to be undone, it must be both alive and done. To be redone, it must be alive but not done.

Subclasses of AbstractUndoableEdit should take advantage of this fundamental state support by calling super.undo( ) and super.redo( ) in the first line of their undo( ) and redo( ) methods, respectively. This frees the subclass from having to worry about enforcing the edit's state model.

18.1.2.1 Properties

Table 18-2 shows the default values AbstractUndoableEdit specifies for the properties defined in UndoableEdit.

Table 18-2. AbstractUndoableEdit properties

Property

Data type

get

is

set

Default value

presentationNameo

String

·

   

""

redoPresentationNameo

String

·

   

"Redo"*

significanto

boolean

 

·

 

true

undoPresentationNameo

String

·

   

"Undo"*

ooverridden

*As of SDK 1.3.1, these values are UI resources retrieved from the UIManager class.

The presentationName property is empty by default. RedoPresentationName and undoPresentationName are formed by appending presentationName to RedoName and UndoName (protected constants, see below), respectively. By default, all AbstractUndoableEdits are significant.

There is no way to change the values of these properties once the object is created. Concrete edit classes need to provide some way (possibly just returning a constant value from the property accessor) to define the presentation name.

18.1.2.2 Constants

Table 18-3 shows the two protected constants that AbstractUndoableEdit defines. These constants are used by default when forming the redoPresentationName and undoPresentationName properties. In the 1.2.x and 1.3.0 releases, their values were hardcoded English strings. Starting with 1.3.1, these values are retrieved using the UIManager.getString( ) method with the keys "AbstractUndoableEdit.redoText" and "AbstractUndoableEdit.undoText", respectively.

Table 18-3. AbstractUndoableEdit constants

Constant

Type

Description

RedoNamed

String

String ("Redo") prepended to the presentation name to form the undo presentation name

UndoNamed

String

String ("Undo") prepended to the presentation name to form the undo presentation name

ddeprecated as of 1.3.1

18.1.2.3 Constructor
public AbstractUndoableEdit( )

Create a new edit. The edit is initially alive and done.

18.1.2.4 UndoableEdit methods

The following methods provide a simple default implementation of the UndoableEdit interface:

public boolean addEdit(UndoableEdit anEdit)

Always return false. Merging edits is not directly supported by this class.

public boolean canRedo( )

Return true if the edit is alive (die( ) has not been called) and not done (it has been undone).

public boolean canUndo( )

Return true if the edit is alive (die( ) has not been called) and done (it has not already been undone, or it has been undone and redone).

public void die( )

Set a flag indicating that the edit is no longer alive.

public void redo( ) throws CannotRedoException

Call canRedo( ) and throw an exception if it returns false. Otherwise, it sets a flag to indicate that the edit is done.

public boolean replaceEdit(UndoableEdit anEdit)

Always return false. Merging of edits is not directly supported by this class.

public void undo( ) throws CannotUndoException

Call canUndo( ) and throw an exception if it returns false. Otherwise, it sets a flag to indicate that the edit is no longer done.

18.1.2.5 Creating a toggle edit

In this example, we'll create a simple extension of AbstractUndoableEdit called UndoableToggleEdit .[2]

[2] We'll use this class throughout the examples in this chapter, so it's probably a good idea to make sure you understand its purpose.

This edit provides the ability to undo clicking on a JToggleButton (or one of its subclasses, JRadioButton or JCheckBox). A program using this new edit creates a new UndoableToggleEdit each time the toggle button is clicked. If undo( ) is called on the edit, it changes the state of the button back to its previous state. A redo( ) call sets the button back to the state it was in when it was passed into the UndoableToggleEdit constructor. Here's the source code for this new edit class:

// UndoableToggleEdit.java // import javax.swing.*; import javax.swing.undo.*; // An UndoableEdit used to undo the clicking of a JToggleButton public class UndoableToggleEdit extends AbstractUndoableEdit {   private JToggleButton button;   private boolean selected;   // Create a new edit for a JToggleButton that has just been toggled.   public UndoableToggleEdit(JToggleButton button) {     this.button = button;     selected = button.isSelected( );   }   // Return a reasonable name for this edit.   public String getPresentationName( ) {     return "Toggle " + button.getText( ) + " " +     (selected ? "on" : "off");   }   // Redo by setting the button state as it was initially.   public void redo( ) throws CannotRedoException {     super.redo( );     button.setSelected(selected);   }   // Undo by setting the button state to the opposite value.   public void undo( ) throws CannotUndoException {     super.undo( );     button.setSelected(!selected);   } }

We inherit most of our behavior from AbstractUndoableEdit. The most important thing to learn from this class is that the edit keeps track of enough information to undo or redo an operation. In our case, this is done by holding a reference to the toggle button the edit applies to, as well as by keeping a boolean to hold the value of the toggle. For more complex undo capabilities, your edit classes probably need more information than this.

Another important thing to notice is that both undo( ) and redo( ) call their super implementations to ensure that the edit is in the appropriate state.

Next, let's look at a small application that shows how we might use this new edit class. In this (admittedly worthless) application, we create three toggle buttons that we place in the center of a frame. Below these toggle buttons, we add two JButtons one for undo and one for redo. Each time one of the toggle buttons is pressed (see the actionPerformed( ) method in the SimpleListener inner class), we create a new UndoableToggleEdit (discarding any previous edit). At this time, we also update the labels on our undo and redo buttons using the names defined by the new edit.

If the undo button is clicked, we call undo( ) on the edit, which changes the state of the last toggle button we clicked. Clicking the redo button switches it back again by calling redo( ) on the edit.

When we initially create the edit or when we perform an undo or redo (note the finally blocks in the two anonymous listener classes), we enable or disable the undo and redo buttons based on the edit's response to canUndo( ) and canRedo( ).

For now, we can only undo the most recent edit. Later in the chapter, we'll present a similar example that supports multiple undo operations. Here's the source code for this sample application:

// UndoableToggleApp.java // import javax.swing.*; import javax.swing.event.*; import javax.swing.undo.*; import java.awt.*; import java.awt.event.*; // A sample app showing the use of UndoableToggleEdit public class UndoableToggleApp extends JFrame {   private UndoableEdit edit;   private JButton undoButton;   private JButton redoButton;   // Create the main frame and everything in it.   public UndoableToggleApp( ) {     // Create some toggle buttons (and subclasses).     JToggleButton tog = new JToggleButton("ToggleButton");     JCheckBox cb = new JCheckBox("CheckBox");     JRadioButton radio = new JRadioButton("RadioButton");     // Add our listener to each toggle button.     SimpleListener sl = new SimpleListener( );     tog.addActionListener(sl);     cb.addActionListener(sl);     radio.addActionListener(sl);     // Lay out the buttons.     Box buttonBox = new Box(BoxLayout.Y_AXIS);     buttonBox.add(tog);     buttonBox.add(cb);     buttonBox.add(radio);     // Create undo and redo buttons (initially disabled).     undoButton = new JButton("Undo");     redoButton = new JButton("Redo");     undoButton.setEnabled(false);     redoButton.setEnabled(false);     // Add a listener to the undo button. It attempts to call undo( ) on the     // current edit, then enables/disables the undo/redo buttons as appropriate.     undoButton.addActionListener(new ActionListener( ) {       public void actionPerformed(ActionEvent ev) {         try {           edit.undo( );         } catch (CannotUndoException ex) { ex.printStackTrace( ); }         finally {           undoButton.setEnabled(edit.canUndo( ));           redoButton.setEnabled(edit.canRedo( ));         }       }     });     // Add a redo listener, which is just like the undo listener,     // but for redo this time.     redoButton.addActionListener(new ActionListener( ) {       public void actionPerformed(ActionEvent ev) {         try {           edit.redo( );         } catch (CannotRedoException ex) { ex.printStackTrace( ); }         finally {           undoButton.setEnabled(edit.canUndo( ));           redoButton.setEnabled(edit.canRedo( ));         }       }     });     // Lay out the undo/redo buttons.     Box undoRedoBox = new Box(BoxLayout.X_AXIS);     undoRedoBox.add(Box.createGlue( ));     undoRedoBox.add(undoButton);     undoRedoBox.add(Box.createHorizontalStrut(2));     undoRedoBox.add(redoButton);     undoRedoBox.add(Box.createGlue( ));     // Lay out the main frame     Container content = getContentPane( );     content.setLayout(new BorderLayout( ));     content.add(buttonBox, BorderLayout.CENTER);     content.add(undoRedoBox, BorderLayout.SOUTH);     setSize(400, 150);   }   public class SimpleListener implements ActionListener {     // When a toggle button is clicked, we create a new UndoableToggleEdit (which     // replaces any previous edit). We then get the edit's undo/redo names and set     // the undo/redo button labels. Finally, we enable/disable these buttons by     // asking the edit what we are allowed to do.     public void actionPerformed(ActionEvent ev) {       JToggleButton tb = (JToggleButton)ev.getSource( );       edit = new UndoableToggleEdit(tb);       undoButton.setText(edit.getUndoPresentationName( ));       redoButton.setText(edit.getRedoPresentationName( ));       undoButton.getParent( ).validate( );       undoButton.setEnabled(edit.canUndo( ));       redoButton.setEnabled(edit.canRedo( ));     }   }   // Main program just creates the frame and displays it   public static void main(String[] args) {     JFrame f = new UndoableToggleApp( );     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);     f.setVisible(true);   } }

Figure 18-3 shows what this application looks like after we've played with it for a while. We just got through toggling the radio button and then clicking on the undo button.

Figure 18-3. Sample UndoableToggleApp display
figs/swng2.1803.gif

18.1.3 The CompoundEdit Class

CompoundEdit is a subclass of AbstractUndoableEdit that supports the aggregation of multiple edits into a single composite edit. After a CompoundEdit is created, UndoableEdits can be added by calling addEdit( ). Once all edits have been added, a new method, end( ), must be called on the CompoundEdit to indicate that the creation of the edit is complete (after this point, addEdit( ) just returns false). Only after end( ) has been called can the edit be undone. CompoundEdit implements undo( ) and redo( ) by calling the appropriate method on each of the edits added to it, allowing all of them to be executed at once.

Figure 18-4 shows a state chart very similar to the one we saw for UndoableEdit. The key difference is that a CompoundEdit is initially inProgress and does not allow either undo( ) or redo( ). The end( ) method must be called after adding the edits to the CompoundEdit to enable undo.

Figure 18-4. CompoundEdit state chart
figs/swng2.1804.gif
18.1.3.1 Properties

CompoundEdit defines the properties shown in Table 18-4. inProgress is the only new property here. It indicates whether edits may be added to the CompoundEdit. Initially true, this property is set to false when end( ) is called. It never changes back to true after that.

Table 18-4. CompoundEdit properties

Property

Data type

get

(get)

is

set

(set)

Default value

inProgress

boolean

 

·

 

true

presentationNameo

String

·

   

""

redoPresentationNameo

String

·

   

"Redo"

significanto

boolean

 

·

 

false

undoPresentationNameo

String

·

   

"Undo"

ooverridden

See also properties from the AbstractUndoableEdit class (Table 18-2).

The values of presentationName , redoPresentationName, and undoPresentationName are initially the same as the values defined in AbstractUndoableEdit. However, once edits are added, these values are set to the values of the corresponding properties of the last edit added.

The significant property is initially false. Once edits are added, this value is determined by checking the significance of the child edits. If any of them are set to true, the CompoundEdit is considered to be significant.

18.1.3.2 Protected field
protected Vector edits

This is where the CompoundEdit stores its edits. New edits are added to the end of the vector.

18.1.3.3 Constructor
public CompoundEdit( )

This constructor creates a new edit with no children that is initially inProgress. The undo( ) and redo( ) methods throw exceptions until end( ) is called.

18.1.3.4 UndoableEdit methods

The following methods override the implementations of the UndoableEdit methods defined in AbstractUndoableEdit:

public boolean addEdit(UndoableEdit anEdit)

This method returns false if the edit is not currently inProgress. Otherwise, the first time the method is called, the input edit is added as the first element of a list of child edits. Subsequent calls are given the opportunity to merge with the last edit in the list. This is done by calling addEdit(anEdit) on the last edit in the list. If this returns false (indicating that the last edit did not absorb the new edit), anEdit.replaceEdit(lastEdit) is called to see if the new edit can replace the last edit. If this returns true (indicating that the new edit can replace the last edit), the last edit is removed from the list, and the new edit is added in its place. If not, the last edit is left in the list, and the new edit is added to the end of the list.

Figure 18-5 shows a sequence diagram for this method. In this example, the last edit does not absorb the new edit, but the new edit does replace the previous last edit. We show the last two operations in italics to indicate that these are not actual method calls on the compound edit.

Figure 18-5. CompoundEdit.addEdit( ) sequence diagram
figs/swng2.1805.gif
public boolean canRedo( )

Return true if the edit is not inProgress and super.canRedo( ) returns true.

public boolean canUndo( )

Return true if the edit is not inProgress and super.canUndo( ) returns true.

public void die( )

Call die( ) on each of the child edits in reverse order. It then calls super.die( ).

public void redo( ) throws CannotRedoException

Call super.redo( ) to make sure redo is allowed. It then calls redo( ) on each of its children in the order they were added.

public void undo( ) throws CannotUndoException

Call super.undo( ) to make sure undo is allowed. It then calls undo( ) on each of its children in the reverse of the order they were added.

18.1.3.5 Other methods
public void end( )

Indicate that no more edits will be added. After this method is called, the edit is no longer inProgress.

protected UndoableEdit lastEdit( )

Return the last edit added, or null if no edits have been added yet.

18.1.4 Using Compound Edits

The following example is a modification of the UndoableToggleApp from the previous section. This version uses CompoundEdit to allow multiple button toggles to be undone at once. Each time one of the toggle buttons is clicked, a new UndoableToggleEdit is created and added to a CompoundEdit. Once you've toggled as many buttons as you want, you can click the end button, which causes end( ) to be called on the CompoundEdit and enables the undo button. Clicking on this button causes undo( ) to be called on the CompoundEdit, which in turn calls undo( ) on each of the UndoableToggleEdits that were added to it. Clicking on one of the toggle buttons again causes the CompoundEdit to be replaced with a new one, to which new edits will be added until the end button is pressed again.

Here's the source code for this example. Much of it is unchanged from the UndoableToggleApp example, so we've highlighted the significant changes.

// UndoableToggleApp2.java // import javax.swing.*; import javax.swing.event.*; import javax.swing.undo.*; import java.awt.*; import java.awt.event.*; // A sample app showing the use of UndoableToggleEdit and CompoundEdit public class UndoableToggleApp2 extends JFrame {   private CompoundEdit edit;   private JButton undoButton;   private JButton redoButton;   private JButton endButton;   // Create the main frame and everything in it.   public UndoableToggleApp2( ) {     // Create some toggle buttons (and subclasses).     JToggleButton tog = new JToggleButton("ToggleButton");     JCheckBox cb = new JCheckBox("CompoundEdit ExampleCheckBox");     JRadioButton radio = new JRadioButton("RadioButton");     // Add our listener to each toggle button.     SimpleListener sl = new SimpleListener( );     tog.addActionListener(sl);     cb.addActionListener(sl);     radio.addActionListener(sl);     // Lay out the buttons.     Box buttonBox = new Box(BoxLayout.Y_AXIS);     buttonBox.add(tog);     buttonBox.add(cb);     buttonBox.add(radio);     // Create undo and redo buttons (initially disabled).     undoButton = new JButton("Undo");     redoButton = new JButton("Redo");     endButton = new JButton("End");     undoButton.setEnabled(false);     redoButton.setEnabled(false);     endButton.setEnabled(false);     // Add a listener to the undo button. It attempts to call undo( ) on the      // current edit, then enables/disables the undo/redo buttons as appropriate.     undoButton.addActionListener(new ActionListener( ) {       public void actionPerformed(ActionEvent ev) {         try {           edit.undo( );         } catch (CannotUndoException ex) { ex.printStackTrace( ); }         finally {           undoButton.setEnabled(edit.canUndo( ));           redoButton.setEnabled(edit.canRedo( ));         }       }     });     // Add a redo listener, which is just like the undo listener, but for redo this     // time.     redoButton.addActionListener(new ActionListener( ) {       public void actionPerformed(ActionEvent ev) {         try {           edit.redo( );         } catch (CannotRedoException ex) { ex.printStackTrace( ); }         finally {           undoButton.setEnabled(edit.canUndo( ));           redoButton.setEnabled(edit.canRedo( ));         }       }     });     // Add an end listener. This listener will call end( ) on the CompoundEdit and     // update the undo/redo buttons.     endButton.addActionListener(new ActionListener( ) {       public void actionPerformed(ActionEvent ev) {         edit.end( );         endButton.setEnabled(false);         undoButton.setEnabled(edit.canUndo( ));         redoButton.setEnabled(edit.canRedo( ));       }     });     // Lay out the undo/redo/end buttons.     Box undoRedoEndBox = new Box(BoxLayout.X_AXIS);     undoRedoEndBox.add(Box.createGlue( ));     undoRedoEndBox.add(undoButton);     undoRedoEndBox.add(Box.createHorizontalStrut(2));     undoRedoEndBox.add(redoButton);     undoRedoEndBox.add(Box.createHorizontalStrut(2));     undoRedoEndBox.add(endButton);     undoRedoEndBox.add(Box.createGlue( ));     // Lay out the main frame.     Container content = getContentPane( );     content.setLayout(new BorderLayout( ));     content.add(buttonBox, BorderLayout.CENTER);     content.add(undoRedoEndBox, BorderLayout.SOUTH);     setSize(400, 150);   }   public class SimpleListener implements ActionListener {     public void actionPerformed(ActionEvent ev) {       if (edit == null || edit.isInProgress( ) == false)         edit = new CompoundEdit( );       JToggleButton tb = (JToggleButton)ev.getSource( );       UndoableEdit togEdit = new UndoableToggleEdit(tb);       edit.addEdit(togEdit);       endButton.setEnabled(true);       undoButton.setEnabled(edit.canUndo( ));       redoButton.setEnabled(edit.canRedo( ));     }   }   // Main program just creates the frame and displays it.   public static void main(String[] args) {     JFrame f = new UndoableToggleApp2( );     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);     f.setVisible(true);   } } 

18.1.5 The UndoableEditEvent Class

UndoableEditEvent is an event class (it extends java.util.EventObject) defined in the javax.swing.event package. It is used by components that support undo to notify interested listeners (implementing UndoableEditListener) that an UndoableEdit has been performed.

A little later in the chapter, we'll see an example that uses the UndoableEditEvent class and the UndoableEditListener interface.

18.1.5.1 Property

UndoableEditEvent defines the property shown in Table 18-5. The edit property contains the UndoableEdit that was generated, causing this event to be fired.

Table 18-5. UndoableEditEvent property

Property

Data type

get

is

set

Default value

edit

UndoableEdit

·

     

See also the java.util.EventObject class.

18.1.5.2 Constructor
public UndoableEditEvent(Object source, UndoableEdit edit)

Create a new event with the specified event source and UndoableEdit.

18.1.6 The UndoableEditListener Interface

Classes that generate UndoableEditEvents fire these events to UndoableEditListeners. This is a simple interface (like UndoableEditEvent, it can be found in the javax.swing.event package), defining a single method:

public abstract void undoableEditHappened(UndoableEditEvent e)

Called when an undoable operation is performed on an object that supports undo. The event e can be used to obtain the new UndoableEdit.



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