Java/Swing JFC/Undo Redo

Материал из Java эксперт
Перейти к: навигация, поиск

Adding Undo and Redo to a Text Component

  
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;
public class Main{
public static void main(String[] argv) throws Exception{
    JTextComponent textcomp = new JTextArea();
    final UndoManager undo = new UndoManager();
    Document doc = textcomp.getDocument();
    JFrame f = new JFrame();
    f.add(new JScrollPane(textcomp));
    f.setSize(330,300);
    f.setVisible(true);
    
    doc.addUndoableEditListener(new UndoableEditListener() {
        public void undoableEditHappened(UndoableEditEvent evt) {
            undo.addEdit(evt.getEdit());
        }
    });
    
    textcomp.getActionMap().put("Undo",
        new AbstractAction("Undo") {
            public void actionPerformed(ActionEvent evt) {
                try {
                    if (undo.canUndo()) {
                        undo.undo();
                    }
                } catch (CannotUndoException e) {
                }
            }
       });
    
    textcomp.getInputMap().put(KeyStroke.getKeyStroke("control Z"), "Undo");
    
    textcomp.getActionMap().put("Redo",
        new AbstractAction("Redo") {
            public void actionPerformed(ActionEvent evt) {
                try {
                    if (undo.canRedo()) {
                        undo.redo();
                    }
                } catch (CannotRedoException e) {
                }
            }
        });
    
    textcomp.getInputMap().put(KeyStroke.getKeyStroke("control Y"), "Redo");
}}





Add Undo and Redo to a text component

  
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;
public class Main {
  public static void main(String[] argv) throws Exception {
    JTextComponent textcomp = new JTextArea();
    final UndoManager undo = new UndoManager();
    Document doc = textcomp.getDocument();
    doc.addUndoableEditListener(new UndoableEditListener() {
      public void undoableEditHappened(UndoableEditEvent evt) {
        undo.addEdit(evt.getEdit());
      }
    });
    textcomp.getActionMap().put("Undo", new AbstractAction("Undo") {
      public void actionPerformed(ActionEvent evt) {
        try {
          if (undo.canUndo()) {
            undo.undo();
          }
        } catch (CannotUndoException e) {
        }
      }
    });
    textcomp.getInputMap().put(KeyStroke.getKeyStroke("control Z"), "Undo");
    textcomp.getActionMap().put("Redo", new AbstractAction("Redo") {
      public void actionPerformed(ActionEvent evt) {
        try {
          if (undo.canRedo()) {
            undo.redo();
          }
        } catch (CannotRedoException e) {
        }
      }
    });
    textcomp.getInputMap().put(KeyStroke.getKeyStroke("control Y"), "Redo");
  }
}





Add undo support to the StyleFrame example

 
/*
Java Swing, 2nd Edition
By Marc Loy, Robert Eckstein, Dave Wood, James Elliott, Brian Cole
ISBN: 0-596-00408-7
Publisher: O"Reilly 
*/
// UndoStyleFrame.java
//Add undo support to the StyleFrame example. This example only
//retains the most recent edit, to keep things simple.
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.SwingConstants;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.undo.UndoableEdit;
public class UndoStyleFrame extends StyleFrame {
  protected UndoAct undoAction = new UndoAct(); // an Action for undo
  protected RedoAct redoAction = new RedoAct(); // an Action for redo
  public UndoStyleFrame() {
    super();
    setTitle("UndoStyleFrame");
    // register the Actions as undo listeners (we inherited textPane)
    textPane.getDocument().addUndoableEditListener(undoAction);
    textPane.getDocument().addUndoableEditListener(redoAction);
    // create menu for undo/redo
    JMenu editMenu = new JMenu("Edit");
    editMenu.add(new JMenuItem(undoAction));
    editMenu.add(new JMenuItem(redoAction));
    menuBar.add(editMenu); // we inherited menuBar from superclass
    // create buttons for undo/redo
    JPanel buttonPanel = new JPanel();
    buttonPanel.add(new JButton(undoAction));
    buttonPanel.add(new JButton(redoAction));
    getContentPane().add(buttonPanel, java.awt.BorderLayout.SOUTH);
  }
  // begin inner classes ------------
  public class UndoAct extends AbstractAction implements UndoableEditListener {
    private UndoableEdit edit;
    public UndoAct() {
      super("Undo");
      setEnabled(false);
    }
    public void updateEnabled() {
      setEnabled(edit.canUndo());
    }
    public void undoableEditHappened(UndoableEditEvent event) {
      edit = event.getEdit();
      putValue(NAME, edit.getUndoPresentationName());
      updateEnabled();
    }
    public void actionPerformed(ActionEvent ae) {
      edit.undo();
      updateEnabled(); // disable undo
      redoAction.updateEnabled(); // enable redo
    }
  }
  public class RedoAct extends AbstractAction implements UndoableEditListener {
    private UndoableEdit edit;
    public RedoAct() {
      super("Redo");
      setEnabled(false);
    }
    public void updateEnabled() {
      setEnabled(edit.canRedo());
    }
    public void undoableEditHappened(UndoableEditEvent event) {
      edit = event.getEdit();
      putValue(NAME, edit.getRedoPresentationName());
      updateEnabled();
    }
    public void actionPerformed(ActionEvent ae) {
      edit.redo();
      updateEnabled(); // disable redo
      undoAction.updateEnabled(); // enable undo
    }
  }
  // end inner classes ------------
  public static void main(String[] args) {
    JFrame frame = new UndoStyleFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(400, 300);
    frame.setVisible(true);
  }
}
//StyleFrame.java
//A JTextPane with a menu for Style manipulation.
class StyleFrame extends JFrame implements ActionListener {
  protected StyleBox styleBox;
  protected JTextPane textPane;
  protected JMenuBar menuBar;
  protected JMenu applyStyleMenu, modifyStyleMenu;
  protected JMenuItem createItem;
  public StyleFrame() {
    super("StyleFrame");
    styleBox = new StyleBox();
    textPane = new JTextPane();
    getContentPane().add(new JScrollPane(textPane), BorderLayout.CENTER);
    // set up menu
    menuBar = new JMenuBar();
    JMenu styleMenu = new JMenu("Style");
    menuBar.add(styleMenu);
    setJMenuBar(menuBar);
    applyStyleMenu = new JMenu("Set Logical Style");
    applyStyleMenu
        .setToolTipText("set the Logical Style for the paragraph at caret location");
    styleMenu.add(applyStyleMenu);
    modifyStyleMenu = new JMenu("Modify Style");
    modifyStyleMenu
        .setToolTipText("redefine a named Style (will affect paragraphs using that style)");
    styleMenu.add(modifyStyleMenu);
    createItem = new JMenuItem("Create New Style");
    createItem
        .setToolTipText("define a new Style (which can then be applied to paragraphs)");
    createItem.addActionListener(this);
    styleMenu.add(createItem);
    // add the default style to applyStyleMenu and modifyStyleMenu
    createMenuItems(StyleContext.DEFAULT_STYLE);
  }
  protected void createMenuItems(String styleName) {
    // add "styleName" to applyStyleMenu and modifyStyleMenu
    JMenuItem applyItem = new JMenuItem(styleName);
    applyItem.addActionListener(this);
    applyStyleMenu.add(applyItem);
    JMenuItem modifyItem = new JMenuItem(styleName);
    modifyItem.addActionListener(this);
    modifyStyleMenu.add(modifyItem);
  }
  public void actionPerformed(ActionEvent e) {
    // determine which menuItem was invoked and process it
    JMenuItem source = (JMenuItem) e.getSource();
    if (applyStyleMenu.isMenuComponent(source)) {
      // apply an existing style to the paragraph at the caret position
      String styleName = source.getActionCommand();
      Style style = textPane.getStyle(styleName);
      textPane.setLogicalStyle(style);
    }
    if (source == createItem) {
      // define a new Style and add it to the menus
      styleBox.clear();
      int response = JOptionPane.showConfirmDialog(this, styleBox,
          "Style Editor", JOptionPane.OK_CANCEL_OPTION,
          JOptionPane.PLAIN_MESSAGE);
      if (response == JOptionPane.OK_OPTION
          && styleBox.getStyleName().length() > 0) {
        String styleName = styleBox.getStyleName();
        Style style = textPane.addStyle(styleName, null);
        styleBox.fillStyle(style);
        createMenuItems(styleName); // add new Style to the menus
      }
    }
    if (modifyStyleMenu.isMenuComponent(source)) {
      // redefine a Style (will automatically redraw paragraphs using
      // Style)
      String styleName = source.getActionCommand();
      Style style = textPane.getStyle(styleName);
      styleBox.loadFromStyle(style);
      int response = JOptionPane.showConfirmDialog(this, styleBox,
          "Style Editor", JOptionPane.OK_CANCEL_OPTION,
          JOptionPane.PLAIN_MESSAGE);
      if (response == JOptionPane.OK_OPTION)
        styleBox.fillStyle(style);
    }
  }
  public static void main(String[] args) {
    JFrame frame = new StyleFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(400, 300);
    frame.setVisible(true);
  }
}
//StyleBox.java
//A control panel that can be used to edit a style"s paragraph attributes.
class StyleBox extends JPanel {
  private static final String[] fonts = { "Monospaced", "Serif", "SansSerif" };
  private static final String[] sizes = { "8", "10", "12", "18", "24", "36" };
  private JTextField nameField;
  private JComboBox fontCombo, sizeCombo;
  private JTextField leftField, rightField, aboveField, belowField;
  private JCheckBox boldCheck, italicCheck;
  public StyleBox() {
    // create the fields and lay them out
    super(new BorderLayout(4, 4));
    JPanel labelPanel = new JPanel(new GridLayout(8, 1, 0, 2));
    JPanel valuePanel = new JPanel(new GridLayout(8, 1, 0, 2));
    add(labelPanel, BorderLayout.WEST);
    add(valuePanel, BorderLayout.CENTER);
    JLabel lab;
    JPanel sidePanel;
    lab = new JLabel("Style Name", SwingConstants.RIGHT);
    labelPanel.add(lab);
    nameField = new JTextField();
    lab.setLabelFor(nameField);
    valuePanel.add(nameField);
    lab = new JLabel("Font", SwingConstants.RIGHT);
    labelPanel.add(lab);
    fontCombo = new JComboBox(fonts);
    fontCombo.setEditable(true); // user may enter custom value
    lab.setLabelFor(fontCombo);
    valuePanel.add(fontCombo);
    lab = new JLabel("Size", SwingConstants.RIGHT);
    labelPanel.add(lab);
    sizeCombo = new JComboBox(sizes);
    sizeCombo.setEditable(true); // user may enter custom value
    lab.setLabelFor(sizeCombo);
    sidePanel = new JPanel(new BorderLayout(4, 0));
    sidePanel.add(sizeCombo, BorderLayout.CENTER);
    sidePanel.add(new JLabel("points"), BorderLayout.EAST);
    valuePanel.add(sidePanel);
    lab = new JLabel("Left Indent", SwingConstants.RIGHT);
    labelPanel.add(lab);
    leftField = new JTextField();
    lab.setLabelFor(leftField);
    sidePanel = new JPanel(new BorderLayout(4, 0));
    sidePanel.add(leftField, BorderLayout.CENTER);
    sidePanel.add(new JLabel("points"), BorderLayout.EAST);
    valuePanel.add(sidePanel);
    lab = new JLabel("Right Indent", SwingConstants.RIGHT);
    labelPanel.add(lab);
    rightField = new JTextField();
    lab.setLabelFor(rightField);
    sidePanel = new JPanel(new BorderLayout(4, 0));
    sidePanel.add(rightField, BorderLayout.CENTER);
    sidePanel.add(new JLabel("points"), BorderLayout.EAST);
    valuePanel.add(sidePanel);
    lab = new JLabel("Space Above", SwingConstants.RIGHT);
    labelPanel.add(lab);
    aboveField = new JTextField();
    lab.setLabelFor(aboveField);
    sidePanel = new JPanel(new BorderLayout(4, 0));
    sidePanel.add(aboveField, BorderLayout.CENTER);
    sidePanel.add(new JLabel("points"), BorderLayout.EAST);
    valuePanel.add(sidePanel);
    lab = new JLabel("Space Below", SwingConstants.RIGHT);
    labelPanel.add(lab);
    belowField = new JTextField();
    lab.setLabelFor(belowField);
    sidePanel = new JPanel(new BorderLayout(4, 0));
    sidePanel.add(belowField, BorderLayout.CENTER);
    sidePanel.add(new JLabel("points"), BorderLayout.EAST);
    valuePanel.add(sidePanel);
    boldCheck = new JCheckBox("Bold");
    italicCheck = new JCheckBox("Italic");
    sidePanel = new JPanel(new GridLayout(1, 2));
    sidePanel.add(boldCheck);
    sidePanel.add(italicCheck);
    valuePanel.add(sidePanel);
    clear(); // sets initial values, etc.
  }
  public void clear() {
    // reset all fields (also sets nameField to be editable)
    nameField.setText("");
    nameField.setEditable(true);
    fontCombo.setSelectedIndex(0);
    sizeCombo.setSelectedIndex(2);
    leftField.setText("0.0");
    rightField.setText("0.0");
    aboveField.setText("0.0");
    belowField.setText("0.0");
    boldCheck.setSelected(false);
    italicCheck.setSelected(false);
  }
  public String getStyleName() {
    // return the name of the style
    String name = nameField.getText();
    if (name.length() > 0)
      return name;
    else
      return null;
  }
  public void fillStyle(Style style) {
    // mutate "style" with the values entered in the fields
    // (no value checking--could throw NumberFormatException)
    String font = (String) fontCombo.getSelectedItem();
    StyleConstants.setFontFamily(style, font);
    String size = (String) sizeCombo.getSelectedItem();
    StyleConstants.setFontSize(style, Integer.parseInt(size));
    String left = leftField.getText();
    StyleConstants.setLeftIndent(style, Float.valueOf(left).floatValue());
    String right = rightField.getText();
    StyleConstants.setRightIndent(style, Float.valueOf(right).floatValue());
    String above = aboveField.getText();
    StyleConstants.setSpaceAbove(style, Float.valueOf(above).floatValue());
    String below = belowField.getText();
    StyleConstants.setSpaceBelow(style, Float.valueOf(below).floatValue());
    boolean bold = boldCheck.isSelected();
    StyleConstants.setBold(style, bold);
    boolean italic = italicCheck.isSelected();
    StyleConstants.setItalic(style, italic);
  }
  // Load the form from an existing Style.
  public void loadFromStyle(Style style) {
    nameField.setText(style.getName());
    nameField.setEditable(false); // don"t allow name change
    String fam = StyleConstants.getFontFamily(style);
    fontCombo.setSelectedItem(fam);
    int size = StyleConstants.getFontSize(style);
    sizeCombo.setSelectedItem(Integer.toString(size));
    float left = StyleConstants.getLeftIndent(style);
    leftField.setText(Float.toString(left));
    float right = StyleConstants.getRightIndent(style);
    rightField.setText(Float.toString(right));
    float above = StyleConstants.getSpaceAbove(style);
    aboveField.setText(Float.toString(above));
    float below = StyleConstants.getSpaceBelow(style);
    belowField.setText(Float.toString(below));
    boolean bold = StyleConstants.isBold(style);
    boldCheck.setSelected(bold);
    boolean italic = StyleConstants.isItalic(style);
    italicCheck.setSelected(italic);
  }
}





An example that shows lots of little UndoManager details

 
/*
Java Swing, 2nd Edition
By Marc Loy, Robert Eckstein, Dave Wood, James Elliott, Brian Cole
ISBN: 0-596-00408-7
Publisher: O"Reilly 
*/
// UndoManagerDetails.java
//An example that shows lots of little UndoManager details
//
import java.util.Enumeration;
import java.util.Vector;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;
import javax.swing.undo.UndoableEdit;
public class UndoManagerDetails {
  public static void main(String[] args) {
    UndoManager mgr = new UndoManager();
    // Show how insignificant edits are skipped over.
    //
    //                                 # adds? sig? replace?
    mgr.addEdit(new SampleUndoableEdit(1, false, true, false));
    mgr.addEdit(new SampleUndoableEdit(2, false, true, false));
    mgr.addEdit(new SampleUndoableEdit(3, false, false, false));
    mgr.addEdit(new SampleUndoableEdit(4, false, false, false));
    System.out.println("--------------------------");
    System.out.println("Insignificant edit example");
    System.out.println("--------------------------");
    mgr.undo();
    mgr.redo();
    System.out.println(mgr.canRedo()); // No more sig. edits
    // Show how edits that call add/replace are used.
    //
    //                                 # adds? sig? replace?
    mgr.addEdit(new SampleUndoableEdit(5, true, true, false));
    mgr.addEdit(new SampleUndoableEdit(6, false, true, false));
    System.out.println("----------------------------------");
    System.out.println("Absorbed (by addEdit) edit example");
    System.out.println("----------------------------------");
    mgr.undo();
    mgr.discardAllEdits();
    //                                 # adds? sig? replace?
    mgr.addEdit(new SampleUndoableEdit(1, false, true, false));
    mgr.addEdit(new SampleUndoableEdit(2, false, true, true));
    System.out.println("--------------------------------------");
    System.out.println("Absorbed (by replaceEdit) edit example");
    System.out.println("--------------------------------------");
    mgr.undo();
    System.out.println(mgr.canUndo());
    // Show how changing limit works.
    mgr.discardAllEdits();
    //                                 # adds? sig? replace?
    mgr.addEdit(new SampleUndoableEdit(1, false, true, false));
    mgr.addEdit(new SampleUndoableEdit(2, false, true, false));
    mgr.addEdit(new SampleUndoableEdit(3, false, true, false));
    mgr.addEdit(new SampleUndoableEdit(4, false, true, false));
    mgr.addEdit(new SampleUndoableEdit(5, false, true, false));
    mgr.addEdit(new SampleUndoableEdit(6, false, true, false));
    System.out.println("----------------------");
    System.out.println("Changing limit example");
    System.out.println("----------------------");
    mgr.undo();
    mgr.undo();
    mgr.undo(); // Now 3 undoable, 3 redoable
    mgr.setLimit(4); // Now 2 undoable, 2 redoable!
    while (mgr.canUndo())
      mgr.undo();
    while (mgr.canRedo())
      mgr.redo();
    // undoOrRedo example
    mgr.discardAllEdits();
    mgr.setLimit(1);
    //                                 # adds? sig? replace?
    mgr.addEdit(new SampleUndoableEdit(1, false, true, false));
    System.out.println("------------------");
    System.out.println("undoOrRedo example");
    System.out.println("------------------");
    System.out.println(mgr.getUndoOrRedoPresentationName());
    mgr.undoOrRedo();
    System.out.println(mgr.getUndoOrRedoPresentationName());
    mgr.undoOrRedo();
    // Show how UndoManager becomes a CompositeEdit.
    mgr.discardAllEdits();
    mgr.setLimit(100);
    //                                 # adds? sig? replace?
    mgr.addEdit(new SampleUndoableEdit(1, false, true, false));
    mgr.addEdit(new SampleUndoableEdit(2, false, true, false));
    mgr.addEdit(new SampleUndoableEdit(3, false, true, false));
    System.out.println("------------------------------");
    System.out.println("Transform to composite example");
    System.out.println("------------------------------");
    mgr.end();
    mgr.undo();
    mgr.redo();
    // Show that adds are no longer allowed. Note that addEdit() returns
    // true in
    // pre-JDK 1.2 Swing releases. This is fixed in JDK 1.2.
    System.out.println(mgr.addEdit(new SampleUndoableEdit(4, false, true,
        false)));
    mgr.undo(); // note that edit 4 is not there
  }
}
//SampleUndoableEdit.java
//A simple (?) example of an undoable edit.
//
class SampleUndoableEdit extends AbstractUndoableEdit {
  private boolean isSignificant;
  private boolean isReplacer;
  private int number;
  private boolean allowAdds;
  private Vector addedEdits;
  private UndoableEdit replaced;
  // Create a new edit with an identifying number. The boolean arguments
  // define
  // the edit"s behavior.
  public SampleUndoableEdit(int number, boolean allowAdds,
      boolean isSignificant, boolean isReplacer) {
    this.number = number;
    this.allowAdds = allowAdds;
    if (allowAdds)
      addedEdits = new Vector();
    this.isSignificant = isSignificant;
    this.isReplacer = isReplacer;
  }
  // "Undo" the edit by printing a message to the screen.
  public void undo() throws CannotUndoException {
    super.undo();
    System.out.print("Undo " + number);
    dumpState();
  }
  // "Redo" the edit by printing a message to the screen.
  public void redo() throws CannotRedoException {
    super.redo();
    System.out.print("Redo " + number);
    dumpState();
  }
  // If allowAdds is true, we store the input edit. If not, just return false.
  public boolean addEdit(UndoableEdit anEdit) {
    if (allowAdds) {
      addedEdits.addElement(anEdit);
      return true;
    } else
      return false;
  }
  // If isReplacer is true, we store the edit we are replacing.
  public boolean replaceEdit(UndoableEdit anEdit) {
    if (isReplacer) {
      replaced = anEdit;
      return true;
    } else
      return false;
  }
  // Significance is based on constructor parameter.
  public boolean isSignificant() {
    return isSignificant;
  }
  // Just return our identifier.
  public String toString() {
    return "<" + number + ">";
  }
  // Debug output.
  public void dumpState() {
    if (allowAdds && addedEdits.size() > 0) {
      Enumeration e = addedEdits.elements();
      System.out.print(" (absorbed: ");
      while (e.hasMoreElements()) {
        System.out.print(e.nextElement());
      }
      System.out.print(")");
    }
    if (isReplacer && replaced != null) {
      System.out.print(" (replaced: " + replaced + ")");
    }
    System.out.println();
  }
}





A sample app showing the use of UndoableToggleEdit and CompoundEdit

 
/*
Java Swing, 2nd Edition
By Marc Loy, Robert Eckstein, Dave Wood, James Elliott, Brian Cole
ISBN: 0-596-00408-7
Publisher: O"Reilly 
*/
// UndoableToggleApp2.java
//A sample app showing the use of UndoableToggleEdit and CompoundEdit.
//
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JRadioButton;
import javax.swing.JToggleButton;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.rupoundEdit;
import javax.swing.undo.UndoableEdit;
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: 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());
      }
    });
    // Layout 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());
    // Layout 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);
  }
}
//UndoableToggleEdit.java
//An UndoableEdit used to undo the pressing of a JToggleButton.
//
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);
  }
}





Bind the undo action to ctl-Z

  
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;
public class Main {
  public static void main(String[] argv) {
    JTextComponent textcomp = new JTextArea();
    final UndoManager undo = new UndoManager();
    Document doc = textcomp.getDocument();
    doc.addUndoableEditListener(new UndoableEditListener() {
      public void undoableEditHappened(UndoableEditEvent evt) {
        undo.addEdit(evt.getEdit());
      }
    });
    textcomp.getActionMap().put("Undo", new AbstractAction("Undo") {
      public void actionPerformed(ActionEvent evt) {
        try {
          if (undo.canUndo()) {
            undo.undo();
          }
        } catch (CannotUndoException e) {
        }
      }
    });
    textcomp.getInputMap().put(KeyStroke.getKeyStroke("control Z"), "Undo");
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(new JScrollPane(textcomp));
    frame.setSize(380, 320);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}





Create an undo action and add it to the text component

  
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;
public class Main {
  public static void main(String[] argv) {
    JTextComponent textcomp = new JTextArea();
    final UndoManager undo = new UndoManager();
    Document doc = textcomp.getDocument();
    doc.addUndoableEditListener(new UndoableEditListener() {
      public void undoableEditHappened(UndoableEditEvent evt) {
        undo.addEdit(evt.getEdit());
      }
    });
    textcomp.getActionMap().put("Undo", new AbstractAction("Undo") {
      public void actionPerformed(ActionEvent evt) {
        try {
          if (undo.canUndo()) {
            undo.undo();
          }
        } catch (CannotUndoException e) {
        }
      }
    });
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(new JScrollPane(textcomp));
    frame.setSize(380, 320);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}





Create a redo action and add it to the text component (JTextComponent)

  
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;
public class Main {
  public static void main(String[] argv) {
    JTextComponent textcomp = new JTextArea();
    final UndoManager undo = new UndoManager();
    Document doc = textcomp.getDocument();
    doc.addUndoableEditListener(new UndoableEditListener() {
      public void undoableEditHappened(UndoableEditEvent evt) {
        undo.addEdit(evt.getEdit());
      }
    });
    textcomp.getActionMap().put("Undo", new AbstractAction("Undo") {
      public void actionPerformed(ActionEvent evt) {
        try {
          if (undo.canUndo()) {
            undo.undo();
          }
        } catch (CannotUndoException e) {
        }
      }
    });
    textcomp.getInputMap().put(KeyStroke.getKeyStroke("control Z"), "Undo");
    textcomp.getActionMap().put("Redo", new AbstractAction("Redo") {
      public void actionPerformed(ActionEvent evt) {
        try {
          if (undo.canRedo()) {
            undo.redo();
          }
        } catch (CannotRedoException e) {
        }
      }
    });
    textcomp.getInputMap().put(KeyStroke.getKeyStroke("control Y"), "Redo");
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(new JScrollPane(textcomp));
    frame.setSize(380, 320);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}





Creating TextArea with Undo, Redo Capabilities

  
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;
class UndoableTextArea extends JTextArea implements UndoableEditListener, FocusListener,
    KeyListener {
  private UndoManager m_undoManager;
  public UndoableTextArea() {
    this(new String());
  }
  public UndoableTextArea(String text) {
    super(text);
    getDocument().addUndoableEditListener(this);
    this.addKeyListener(this);
    this.addFocusListener(this);
  }
  private void createUndoMananger() {
    m_undoManager = new UndoManager();
    m_undoManager.setLimit(10);
  }
  private void removeUndoMananger() {
    m_undoManager.end();
  }
  public void focusGained(FocusEvent fe) {
    createUndoMananger();
  }
  public void focusLost(FocusEvent fe) {
    removeUndoMananger();
  }
  public void undoableEditHappened(UndoableEditEvent e) {
    m_undoManager.addEdit(e.getEdit());
  }
  public void keyPressed(KeyEvent e) {
    if ((e.getKeyCode() == KeyEvent.VK_Z) && (e.isControlDown())) {
      try {
        m_undoManager.undo();
      } catch (CannotUndoException cue) {
        Toolkit.getDefaultToolkit().beep();
      }
    }
    if ((e.getKeyCode() == KeyEvent.VK_Y) && (e.isControlDown())) {
      try {
        m_undoManager.redo();
      } catch (CannotRedoException cue) {
        Toolkit.getDefaultToolkit().beep();
      }
    }
  }
  public void keyReleased(KeyEvent e) {
  }
  public void keyTyped(KeyEvent e) {
  }
}
public class Main extends JFrame {
  UndoableTextArea m_undoableTextArea = new UndoableTextArea();
  public Main() {
    JScrollPane sc = new JScrollPane(m_undoableTextArea);
    getContentPane().setLayout(new BorderLayout(10, 10));
    getContentPane()
        .add(BorderLayout.NORTH, new JLabel("Press, CTRL+Z to Undo, CTRL+Y to Redo..."));
    getContentPane().add(BorderLayout.CENTER, sc);
  }
  public static void main(String[] arg) {
    Main m = new Main();
    m.setVisible(true);
    m.setSize(new Dimension(400, 300));
    m.validate();
  }
}





Listen for undo and redo events

  
public class Main{
public static void main(String[] argv){
    JTextComponent textcomp = new JTextArea();
    final UndoManager undo = new UndoManager();
    Document doc = textcomp.getDocument();
    
    doc.addUndoableEditListener(new UndoableEditListener() {
        public void undoableEditHappened(UndoableEditEvent evt) {
            undo.addEdit(evt.getEdit());
        }
    });    
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(new JScrollPane(textcomp));
    frame.setSize(380, 320);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
}}





Simple GUI demo of UndoManager and friends

 
/*
 * Copyright (c) Ian F. Darwin, http://www.darwinsys.ru/, 1996-2002.
 * All rights reserved. Software written by Ian F. Darwin and others.
 * $Id: LICENSE,v 1.8 2004/02/09 03:33:38 ian Exp $
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS""
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 * Java, the Duke mascot, and all variants of Sun"s Java "steaming coffee
 * cup" logo are trademarks of Sun Microsystems. Sun"s, and James Gosling"s,
 * pioneering role in inventing and promulgating (and standardizing) the Java 
 * language and environment is gratefully acknowledged.
 * 
 * The pioneering role of Dennis Ritchie and Bjarne Stroustrup, of AT&T, for
 * inventing predecessor languages C and C++ is also gratefully acknowledged.
 */
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.undo.UndoManager;
/**
 * Simple GUI demo of UndoManager and friends.
 * 
 * @author Ian Darwin http://www.darwinsys.ru/
 */
public class UndoDemo extends JFrame {
  JTextArea ta;
  UndoManager um;
  /** Simple main program that just constructs and shows the GUI */
  public static void main(String[] files) {
    new UndoDemo().setVisible(true);
  }
  /** Construct a GUI that demonstrates use of UndoManager */
  public UndoDemo() {
    Container cp = getContentPane();
    cp.add(ta = new JTextArea(20, 60), BorderLayout.CENTER);
    JPanel bp;
    cp.add(bp = new JPanel(), BorderLayout.SOUTH);
    // Create a javax.swing.undo.UndoManager; this is an amazing class that
    // keeps a Stack of UndoableEdits and lets you invoke them;
    // by registering it as a Listener on the TextComponent.Document,
    // the Document will create the UndoableEdit objects and send them
    // to the UndoManager. Between them they do ALL the work!
    um = new UndoManager();
    ta.getDocument().addUndoableEditListener(um);
    // Create the buttons
    JButton cutButton, copyButton, pasteButton, undoButton, redoButton;
    bp.add(cutButton = new JButton("Cut"));
    cutButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        ta.cut();
      }
    });
    bp.add(copyButton = new JButton("Copy"));
    copyButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        ta.copy();
      }
    });
    bp.add(pasteButton = new JButton("Paste"));
    pasteButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        ta.paste();
      }
    });
    bp.add(undoButton = new JButton("UnDo"));
    undoButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        if (um.canUndo()) {
          um.undo();
        } else {
          warn("Can"t undo");
        }
      }
    });
    bp.add(redoButton = new JButton("ReDo"));
    redoButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        if (um.canRedo()) {
          um.redo();
        } else {
          warn("Can"t redo");
        }
      }
    });
    pack();
    setDefaultCloseOperation(EXIT_ON_CLOSE);
  }
  void warn(String msg) {
    JOptionPane.showMessageDialog(this, "Warning: " + msg, "Warning",
        JOptionPane.WARNING_MESSAGE);
  }
}





The use of StateEdit(able)

 
/*
Java Swing, 2nd Edition
By Marc Loy, Robert Eckstein, Dave Wood, James Elliott, Brian Cole
ISBN: 0-596-00408-7
Publisher: O"Reilly 
*/
// UndoableToggleApp4.java
//A sample app showing the use of StateEdit(able).
//
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Hashtable;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JRadioButton;
import javax.swing.JToggleButton;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.StateEdit;
import javax.swing.undo.StateEditable;
public class UndoableToggleApp4 extends JFrame implements StateEditable {
  private JToggleButton tog;
  private JCheckBox cb;
  private JRadioButton radio;
  private JButton undoButton;
  private JButton redoButton;
  private JButton startButton;
  private JButton endButton;
  private StateEdit edit;
  // Create the main frame and everything in it.
  public UndoableToggleApp4() {
    // Create some toggle buttons (and subclasses).
    tog = new JToggleButton("ToggleButton");
    cb = new JCheckBox("CheckBox");
    radio = new JRadioButton("RadioButton");
    // Add our listener to the buttons.
    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, redo, start, and end buttons.
    startButton = new JButton("Start");
    endButton = new JButton("End");
    undoButton = new JButton("Undo");
    redoButton = new JButton("Redo");
    startButton.setEnabled(true);
    endButton.setEnabled(false);
    undoButton.setEnabled(false);
    redoButton.setEnabled(false);
    // Add a listener to the start button. It creates a new StateEdit,
    // passing in this frame as the StateEditable.
    startButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ev) {
        edit = new StateEdit(UndoableToggleApp4.this);
        startButton.setEnabled(false);
        endButton.setEnabled(true);
        //undoButton.setEnabled(edit.canUndo());
        //
        // NOTE: We really don"t want to be able to undo until end() is
        // pressed,
        // but StateEdit does not enforce this for us!
        undoButton.setEnabled(false);
        redoButton.setEnabled(edit.canRedo());
      }
    });
    // Add a listener to the end button. It will call end() on the
    // StateEdit.
    endButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ev) {
        edit.end();
        startButton.setEnabled(true);
        endButton.setEnabled(false);
        undoButton.setEnabled(edit.canUndo());
        redoButton.setEnabled(edit.canRedo());
      }
    });
    // 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: just like the undo listener.
    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 state/end and undo/redo buttons.
    Box undoRedoBox = new Box(BoxLayout.X_AXIS);
    undoRedoBox.add(Box.createGlue());
    undoRedoBox.add(startButton);
    undoRedoBox.add(Box.createHorizontalStrut(2));
    undoRedoBox.add(endButton);
    undoRedoBox.add(Box.createHorizontalStrut(2));
    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 any toggle button is clicked, we turn off the undo and redo
    // buttons, reflecting the fact that we can only undo/redo the last
    // set of state changes as long as no additional changes have been made.
    public void actionPerformed(ActionEvent ev) {
      undoButton.setEnabled(false);
      redoButton.setEnabled(false);
    }
  }
  // Save the state of the app by storing the current state of the three
  // buttons. We"ll use the buttons themselves as keys and their selected
  // state as values.
  public void storeState(Hashtable ht) {
    ht.put(tog, new Boolean(tog.isSelected()));
    ht.put(cb, new Boolean(cb.isSelected()));
    ht.put(radio, new Boolean(radio.isSelected()));
  }
  // Restore state based on the values we saved when storeState() was called.
  // Note that StateEdit discards any state info that did not change from
  // between the start state and the end state, so we can"t assume that the
  // state for all 3 buttons is in the Hashtable.
  public void restoreState(Hashtable ht) {
    Boolean b1 = (Boolean) ht.get(tog);
    if (b1 != null)
      tog.setSelected(b1.booleanValue());
    Boolean b2 = (Boolean) ht.get(cb);
    if (b2 != null)
      cb.setSelected(b2.booleanValue());
    Boolean b3 = (Boolean) ht.get(radio);
    if (b3 != null)
      radio.setSelected(b3.booleanValue());
  }
  // Main program just creates the frame and displays it.
  public static void main(String[] args) {
    JFrame f = new UndoableToggleApp4();
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.setVisible(true);
  }
}





The use of UndoableToggleEdit

 
/*
Java Swing, 2nd Edition
By Marc Loy, Robert Eckstein, Dave Wood, James Elliott, Brian Cole
ISBN: 0-596-00408-7
Publisher: O"Reilly 
*/
// UndoableToggleApp.java
//A sample app showing the use of UndoableToggleEdit.
//
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JRadioButton;
import javax.swing.JToggleButton;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoableEdit;
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);
    // Layout 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: 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());
        }
      }
    });
    // Layout 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());
    // Layout 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);
  }
}
//UndoableToggleEdit.java
//An UndoableEdit used to undo the pressing of a JToggleButton.
//
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);
  }
}





The use of UndoManager

 
/*
Java Swing, 2nd Edition
By Marc Loy, Robert Eckstein, Dave Wood, James Elliott, Brian Cole
ISBN: 0-596-00408-7
Publisher: O"Reilly 
*/
// UndoableToggleApp3.java
//A sample app showing the use of UndoManager.
//
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JToggleButton;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;
public class UndoableToggleApp3 extends JFrame {
  private UndoManager manager = new UndoManager();
  private JButton undoButton;
  private JButton redoButton;
  // Create the main frame and everything in it.
  public UndoableToggleApp3() {
    // Create some toggle buttons.
    UndoableJToggleButton tog1 = new UndoableJToggleButton("One");
    UndoableJToggleButton tog2 = new UndoableJToggleButton("Two");
    UndoableJToggleButton tog3 = new UndoableJToggleButton("Three");
    // Add our listener to each toggle button.
    SimpleUEListener sl = new SimpleUEListener();
    tog1.addUndoableEditListener(sl);
    tog2.addUndoableEditListener(sl);
    tog3.addUndoableEditListener(sl);
    // Lay out the buttons.
    Box buttonBox = new Box(BoxLayout.Y_AXIS);
    buttonBox.add(tog1);
    buttonBox.add(tog2);
    buttonBox.add(tog3);
    // 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
    // UndoManager, then enables/disables the undo/redo buttons as
    // appropriate.
    undoButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ev) {
        try {
          manager.undo();
        } catch (CannotUndoException ex) {
          ex.printStackTrace();
        } finally {
          updateButtons();
        }
      }
    });
    // Add a redo listener: just like the undo listener.
    redoButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ev) {
        try {
          manager.redo();
        } catch (CannotRedoException ex) {
          ex.printStackTrace();
        } finally {
          updateButtons();
        }
      }
    });
    // 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.
    getContentPane().setLayout(new BorderLayout());
    getContentPane().add(buttonBox, BorderLayout.CENTER);
    getContentPane().add(undoRedoBox, BorderLayout.SOUTH);
    setSize(400, 150);
  }
  public class SimpleUEListener implements UndoableEditListener {
    // When an UndoableEditEvent is generated (each time one of the buttons
    // is pressed), we add it to the UndoManager and then get the manager"s
    // undo/redo names and set the undo/redo button labels. Finally, we
    // enable/disable these buttons by asking the manager what we are
    // allowed to do.
    public void undoableEditHappened(UndoableEditEvent ev) {
      manager.addEdit(ev.getEdit());
      updateButtons();
    }
  }
  // Method to set the text and state of the undo/redo buttons.
  protected void updateButtons() {
    undoButton.setText(manager.getUndoPresentationName());
    redoButton.setText(manager.getRedoPresentationName());
    undoButton.getParent().validate();
    undoButton.setEnabled(manager.canUndo());
    redoButton.setEnabled(manager.canRedo());
  }
  // Main program just creates the frame and displays it.
  public static void main(String[] args) {
    JFrame f = new UndoableToggleApp3();
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.setVisible(true);
  }
}
//UndoableJToggleButton.java
//Sample undoable toggle button class. Supports only a single listener to
//simplify the code.
//
class UndoableJToggleButton extends JToggleButton {
  private UndoableEditListener listener;
  // For this example, we"ll just provide one constructor . . .
  public UndoableJToggleButton(String txt) {
    super(txt);
  }
  // Set the UndoableEditListener.
  public void addUndoableEditListener(UndoableEditListener l) {
    listener = l; // Should ideally throw an exception if listener != null
  }
  // Remove the UndoableEditListener.
  public void removeUndoableEditListener(UndoableEditListener l) {
    listener = null;
  }
  // We override this method to call the super implementation first (to fire
  // the
  // action event) and then fire a new UndoableEditEvent to our listener.
  protected void fireActionPerformed(ActionEvent ev) {
    // Fire the ActionEvent as usual.
    super.fireActionPerformed(ev);
    if (listener != null) {
      listener.undoableEditHappened(new UndoableEditEvent(this,
          new UndoableToggleEdit(this)));
    }
  }
}
//UndoableToggleEdit.java
//An UndoableEdit used to undo the pressing of a JToggleButton.
//
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);
  }
}





Undoable Drawing Panel 2

 
/*
Definitive Guide to Swing for Java 2, Second Edition
By John Zukowski     
ISBN: 1-893115-78-X
Publisher: APress
*/
import java.awt.BorderLayout;
import java.awt.ruponent;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Polygon;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Hashtable;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JToolBar;
import javax.swing.event.UndoableEditListener;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.StateEdit;
import javax.swing.undo.StateEditable;
import javax.swing.undo.UndoManager;
import javax.swing.undo.UndoableEditSupport;
public class UndoableDrawingPanel2 extends JPanel implements StateEditable {
  private static String POLYGON_KEY = "Polygon";
  UndoableEditSupport undoableEditSupport = new UndoableEditSupport(this);
  Polygon polygon = new Polygon();
  public UndoableDrawingPanel2() {
    MouseListener mouseListener = new MouseAdapter() {
      public void mouseReleased(MouseEvent mouseEvent) {
        StateEdit stateEdit = new StateEdit(UndoableDrawingPanel2.this);
        polygon.addPoint(mouseEvent.getX(), mouseEvent.getY());
        stateEdit.end();
        undoableEditSupport.postEdit(stateEdit);
        repaint();
      }
    };
    addMouseListener(mouseListener);
  }
  public void addUndoableEditListener(
      UndoableEditListener undoableEditListener) {
    undoableEditSupport.addUndoableEditListener(undoableEditListener);
  }
  public void removeUndoableEditListener(
      UndoableEditListener undoableEditListener) {
    undoableEditSupport.removeUndoableEditListener(undoableEditListener);
  }
  public void storeState(Hashtable state) {
    state.put(POLYGON_KEY, getPolygon());
  }
  public void restoreState(Hashtable state) {
    Polygon polygon = (Polygon) state.get(POLYGON_KEY);
    if (polygon != null) {
      setPolygon(polygon);
    }
  }
  public void setPolygon(Polygon newValue) {
    polygon = newValue;
    repaint();
  }
  public Polygon getPolygon() {
    Polygon returnValue;
    if (polygon.npoints == 0) {
      returnValue = new Polygon();
    } else {
      returnValue = new Polygon(polygon.xpoints, polygon.ypoints,
          polygon.npoints);
    }
    return returnValue;
  }
  protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    g.drawPolygon(polygon);
  }
  public static void main(String args[]) {
    JFrame frame = new JFrame("Drawing Sample2");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    UndoableDrawingPanel2 drawingPanel = new UndoableDrawingPanel2();
    UndoManager manager = new UndoManager();
    drawingPanel.addUndoableEditListener(manager);
    JToolBar toolbar = new JToolBar();
    toolbar.add(UndoManagerHelper.getUndoAction(manager));
    toolbar.add(UndoManagerHelper.getRedoAction(manager));
    Container content = frame.getContentPane();
    content.add(toolbar, BorderLayout.NORTH);
    content.add(drawingPanel, BorderLayout.CENTER);
    frame.setSize(300, 150);
    frame.setVisible(true);
  }
}
class UndoManagerHelper {
  public static Action getUndoAction(UndoManager manager, String label) {
    return new UndoAction(manager, label);
  }
  public static Action getUndoAction(UndoManager manager) {
    return new UndoAction(manager, "Undo");
  }
  public static Action getRedoAction(UndoManager manager, String label) {
    return new RedoAction(manager, label);
  }
  public static Action getRedoAction(UndoManager manager) {
    return new RedoAction(manager, "Redo");
  }
  private abstract static class UndoRedoAction extends AbstractAction {
    UndoManager undoManager = new UndoManager();
    String errorMessage = "Cannot undo";
    String errorTitle = "Undo Problem";
    protected UndoRedoAction(UndoManager manager, String name) {
      super(name);
      undoManager = manager;
    }
    public void setErrorMessage(String newValue) {
      errorMessage = newValue;
    }
    public void setErrorTitle(String newValue) {
      errorTitle = newValue;
    }
    protected void showMessage(Object source) {
      if (source instanceof Component) {
        JOptionPane.showMessageDialog((Component) source, errorMessage,
            errorTitle, JOptionPane.WARNING_MESSAGE);
      } else {
        System.err.println(errorMessage);
      }
    }
  }
  public static class UndoAction extends UndoRedoAction {
    public UndoAction(UndoManager manager, String name) {
      super(manager, name);
      setErrorMessage("Cannot undo");
      setErrorTitle("Undo Problem");
    }
    public void actionPerformed(ActionEvent actionEvent) {
      try {
        undoManager.undo();
      } catch (CannotUndoException cannotUndoException) {
        showMessage(actionEvent.getSource());
      }
    }
  }
  public static class RedoAction extends UndoRedoAction {
    String errorMessage = "Cannot redo";
    String errorTitle = "Redo Problem";
    public RedoAction(UndoManager manager, String name) {
      super(manager, name);
      setErrorMessage("Cannot redo");
      setErrorTitle("Redo Problem");
    }
    public void actionPerformed(ActionEvent actionEvent) {
      try {
        undoManager.redo();
      } catch (CannotRedoException cannotRedoException) {
        showMessage(actionEvent.getSource());
      }
    }
  }
}





Undo Drawing

 
/*
Definitive Guide to Swing for Java 2, Second Edition
By John Zukowski     
ISBN: 1-893115-78-X
Publisher: APress
*/
import java.awt.BorderLayout;
import java.awt.ruponent;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Polygon;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JToolBar;
import javax.swing.event.UndoableEditListener;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;
import javax.swing.undo.UndoableEditSupport;
public class UndoDrawing {
  public static void main(String args[]) {
    JFrame frame = new JFrame("Drawing Sample");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    UndoableDrawingPanel drawingPanel = new UndoableDrawingPanel();
    UndoManager manager = new UndoManager();
    drawingPanel.addUndoableEditListener(manager);
    JToolBar toolbar = new JToolBar();
    toolbar.add(UndoManagerHelper.getUndoAction(manager));
    toolbar.add(UndoManagerHelper.getRedoAction(manager));
    Container content = frame.getContentPane();
    content.add(toolbar, BorderLayout.NORTH);
    content.add(drawingPanel, BorderLayout.CENTER);
    frame.setSize(300, 150);
    frame.setVisible(true);
  }
}
class UndoableDrawingPanel extends JPanel {
  UndoableEditSupport undoableEditSupport = new UndoableEditSupport(this);
  Polygon polygon = new Polygon();
  public UndoableDrawingPanel() {
    MouseListener mouseListener = new MouseAdapter() {
      public void mouseReleased(MouseEvent mouseEvent) {
        undoableEditSupport.postEdit(new UndoableDrawEdit(
            UndoableDrawingPanel.this));
        polygon.addPoint(mouseEvent.getX(), mouseEvent.getY());
        repaint();
      }
    };
    addMouseListener(mouseListener);
  }
  public void addUndoableEditListener(
      UndoableEditListener undoableEditListener) {
    undoableEditSupport.addUndoableEditListener(undoableEditListener);
  }
  public void removeUndoableEditListener(
      UndoableEditListener undoableEditListener) {
    undoableEditSupport.removeUndoableEditListener(undoableEditListener);
  }
  public void setPolygon(Polygon newValue) {
    polygon = newValue;
    repaint();
  }
  public Polygon getPolygon() {
    Polygon returnValue;
    if (polygon.npoints == 0) {
      returnValue = new Polygon();
    } else {
      returnValue = new Polygon(polygon.xpoints, polygon.ypoints,
          polygon.npoints);
    }
    return returnValue;
  }
  protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    g.drawPolygon(polygon);
  }
}
class UndoManagerHelper {
  public static Action getUndoAction(UndoManager manager, String label) {
    return new UndoAction(manager, label);
  }
  public static Action getUndoAction(UndoManager manager) {
    return new UndoAction(manager, "Undo");
  }
  public static Action getRedoAction(UndoManager manager, String label) {
    return new RedoAction(manager, label);
  }
  public static Action getRedoAction(UndoManager manager) {
    return new RedoAction(manager, "Redo");
  }
  private abstract static class UndoRedoAction extends AbstractAction {
    UndoManager undoManager = new UndoManager();
    String errorMessage = "Cannot undo";
    String errorTitle = "Undo Problem";
    protected UndoRedoAction(UndoManager manager, String name) {
      super(name);
      undoManager = manager;
    }
    public void setErrorMessage(String newValue) {
      errorMessage = newValue;
    }
    public void setErrorTitle(String newValue) {
      errorTitle = newValue;
    }
    protected void showMessage(Object source) {
      if (source instanceof Component) {
        JOptionPane.showMessageDialog((Component) source, errorMessage,
            errorTitle, JOptionPane.WARNING_MESSAGE);
      } else {
        System.err.println(errorMessage);
      }
    }
  }
  public static class UndoAction extends UndoRedoAction {
    public UndoAction(UndoManager manager, String name) {
      super(manager, name);
      setErrorMessage("Cannot undo");
      setErrorTitle("Undo Problem");
    }
    public void actionPerformed(ActionEvent actionEvent) {
      try {
        undoManager.undo();
      } catch (CannotUndoException cannotUndoException) {
        showMessage(actionEvent.getSource());
      }
    }
  }
  public static class RedoAction extends UndoRedoAction {
    String errorMessage = "Cannot redo";
    String errorTitle = "Redo Problem";
    public RedoAction(UndoManager manager, String name) {
      super(manager, name);
      setErrorMessage("Cannot redo");
      setErrorTitle("Redo Problem");
    }
    public void actionPerformed(ActionEvent actionEvent) {
      try {
        undoManager.redo();
      } catch (CannotRedoException cannotRedoException) {
        showMessage(actionEvent.getSource());
      }
    }
  }
}
class UndoableDrawEdit extends AbstractUndoableEdit {
  UndoableDrawingPanel panel;
  Polygon polygon, savedPolygon;
  public UndoableDrawEdit(UndoableDrawingPanel panel) {
    this.panel = panel;
    polygon = panel.getPolygon();
  }
  public String getPresentationName() {
    return "Polygon of size " + polygon.npoints;
  }
  public void redo() throws CannotRedoException {
    super.redo();
    if (savedPolygon == null) {
      // Should never get here, as super() doesn"t permit redoing
      throw new CannotRedoException();
    } else {
      panel.setPolygon(savedPolygon);
      savedPolygon = null;
    }
  }
  public void undo() throws CannotUndoException {
    super.undo();
    savedPolygon = panel.getPolygon();
    panel.setPolygon(polygon);
  }
}





Undo Example 1

 
/*
Core SWING Advanced Programming 
By Kim Topley
ISBN: 0 13 083292 8       
Publisher: Prentice Hall  
*/

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.undo.*;
public class UndoExample1 extends JFrame {
  public UndoExample1() {
    super("Undo/Redo Example 1");
  
    pane = new JTextPane();
    pane.setEditable(true);    // Editable
    getContentPane().add(new JScrollPane(pane), "Center");
    // Add a menu bar
    menuBar = new JMenuBar();
    setJMenuBar(menuBar);
    // Populate the menu bar
    createMenuBar();
  }
  public void createMenuBar() {
    // Remove the existing menu items
    int count = menuBar.getMenuCount();
    for (int i = 0; i < count; i++) {
      menuBar.remove(menuBar.getMenu(0));
    }
    // Build the new menu.
    Action[] actions = pane.getActions();
    Hashtable actionHash = new Hashtable();
    count = actions.length;
    for (int i = 0; i < count; i++) {
      actionHash.put(actions[i].getValue(Action.NAME), actions[i]);
    }
    // Add the font menu
    JMenu menu = MenuBuilder.buildMenu("Font", fontSpec, actionHash);
    if (menu != null) {
      menuBar.add(menu);
    }
    // Add the alignment menu
    menu = MenuBuilder.buildMenu("Align", alignSpec, actionHash);
    if (menu != null) {
      menuBar.add(menu);
    }
  }
  public static void main(String[] args) {
    try {
        UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
    } catch (Exception evt) {}
  
    JFrame f = new UndoExample1();
    f.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent evt) {
        System.exit(0);
      }
    });
    f.setSize(250, 300);
    f.setVisible(true);
    // Create and show a frame monitoring undoable edits
    JFrame undoMonitor = new JFrame("Undo Monitor");
    final JTextArea textArea = new JTextArea();
    textArea.setEditable(false);
    undoMonitor.getContentPane().add(new JScrollPane(textArea));
    undoMonitor.setBounds(f.getLocation().x + f.getSize().width, 
              f.getLocation().y, 400, 200);
    undoMonitor.setVisible(true);
    pane.getDocument().addUndoableEditListener(new UndoableEditListener() {
      public void undoableEditHappened(UndoableEditEvent evt) {
        UndoableEdit edit = evt.getEdit();
        textArea.append(edit.getPresentationName() + "(" +
                edit.toString() + ")\n");
      }
    });
    
    // Create and show a frame monitoring document edits
    JFrame editMonitor = new JFrame("Edit Monitor");
    final JTextArea textArea2 = new JTextArea();
    textArea2.setEditable(false);
    editMonitor.getContentPane().add(new JScrollPane(textArea2));
    editMonitor.setBounds(undoMonitor.getLocation().x, 
          undoMonitor.getLocation().y + undoMonitor.getSize().height,
          400, 200);
    editMonitor.setVisible(true);
    pane.getDocument().addDocumentListener(new DocumentListener() {
      public void changedUpdate(DocumentEvent evt) {
        textArea2.append("Attribute change\n");
      }
      public void insertUpdate(DocumentEvent evt) {
        textArea2.append("Text insertion\n");
      }
      public void removeUpdate(DocumentEvent evt) {
        textArea2.append("Text removal\n");
      }
    });
  }
  private static JTextPane pane;
  private static JMenuBar menuBar;
  private static MenuSpec[] sizeSpec = new MenuSpec[] {
      new MenuSpec("Size 8",  "font-size-8"),
      new MenuSpec("Size 10", "font-size-10"),
      new MenuSpec("Size 12", "font-size-12"),
      new MenuSpec("Size 14", "font-size-14"),
      new MenuSpec("Size 16", "font-size-16"),
      new MenuSpec("Size 18", "font-size-18"),
      new MenuSpec("Size 24", "font-size-24"),
      new MenuSpec("Size 36", "font-size-36"),
      new MenuSpec("Size 48", "font-size-48")          
  };
  private static MenuSpec[] familySpec = new MenuSpec[] {
      new MenuSpec("Sans Serif", "font-family-SansSerif"),
      new MenuSpec("Monospaced", "font-family-Monospaced"),
      new MenuSpec("Serif", "font-family-Serif")
  };
  private static MenuSpec[] styleSpec = new MenuSpec[] {
      new MenuSpec("Bold", "font-bold"),
      new MenuSpec("Italics", "font-italic"),
      new MenuSpec("Underline", "font-underline")
  };
  // Menu definitions for fonts
  private static MenuSpec[] fontSpec = new MenuSpec[] {
      new MenuSpec("Size", sizeSpec),
      new MenuSpec("Family", familySpec),
      new MenuSpec("Style", styleSpec)
  };
  // Alignment
  private static MenuSpec[] alignSpec = new MenuSpec[] {
      new MenuSpec("Left", "left-justify"),
      new MenuSpec("Center", "center-justify"),
      new MenuSpec("Right", "right-justify")
  };
}

class MenuSpec {
  public MenuSpec(String name, MenuSpec[] subMenus) {
    this.name = name;
    this.subMenus = subMenus;
  }
  public MenuSpec(String name, String actionName) {
    this.name = name;
    this.actionName = actionName;
  }
  public MenuSpec(String name, Action action) {
    this.name = name;
    this.action = action;
  }
  public boolean isSubMenu() {
    return subMenus != null;
  }
  public boolean isAction() {
    return action != null;
  }
  public String getName() {
    return name;
  }
  public MenuSpec[] getSubMenus() {
    return subMenus;
  }
  public String getActionName() {
    return actionName;
  }
  public Action getAction() {
    return action;
  }
  private String name;
  private String actionName;
  private Action action;
  private MenuSpec[] subMenus;
}

class MenuBuilder {
  public static JMenu buildMenu(String name, MenuSpec[] menuSpecs,
      Hashtable actions) {
    int count = menuSpecs.length;
    JMenu menu = new JMenu(name);
    for (int i = 0; i < count; i++) {
      MenuSpec spec = menuSpecs[i];
      if (spec.isSubMenu()) {
        // Recurse to handle a sub menu
        JMenu subMenu = buildMenu(spec.getName(), spec.getSubMenus(),
            actions);
        if (subMenu != null) {
          menu.add(subMenu);
        }
      } else if (spec.isAction()) {
        // It"s an Action - add it directly to the menu
        menu.add(spec.getAction());
      } else {
        // It"s an action name - add it if possible
        String actionName = spec.getActionName();
        Action targetAction = (Action) actions.get(actionName);
        // Create the menu item
        JMenuItem menuItem = menu.add(spec.getName());
        if (targetAction != null) {
          // The editor kit knows the action
          menuItem.addActionListener(targetAction);
        } else {
          // Action not known - disable the menu item
          menuItem.setEnabled(false);
        }
      }
    }
    // Return null if nothing was added to the menu.
    if (menu.getMenuComponentCount() == 0) {
      menu = null;
    }
    return menu;
  }
}





Undo Example 2

 
/*
Core SWING Advanced Programming 
By Kim Topley
ISBN: 0 13 083292 8       
Publisher: Prentice Hall  
*/

import java.awt.BorderLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Hashtable;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextPane;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;
import javax.swing.undo.UndoableEdit;
public class UndoExample2 extends JFrame {
  public UndoExample2() {
    super("Undo/Redo Example 2");
    pane = new JTextPane();
    pane.setEditable(true); // Editable
    getContentPane().add(new JScrollPane(pane), BorderLayout.CENTER);
    // Add a menu bar
    menuBar = new JMenuBar();
    setJMenuBar(menuBar);
    // Populate the menu bar
    createMenuBar();
    // Create the undo manager and actions
    UndoManager manager = new UndoManager();
    pane.getDocument().addUndoableEditListener(manager);
    Action undoAction = new UndoAction(manager);
    Action redoAction = new RedoAction(manager);
    // Add the actions to buttons
    JPanel panel = new JPanel();
    JButton undoButton = new JButton("Undo");
    JButton redoButton = new JButton("Redo");
    undoButton.addActionListener(undoAction);
    redoButton.addActionListener(redoAction);
    panel.add(undoButton);
    panel.add(redoButton);
    getContentPane().add(panel, BorderLayout.SOUTH);
    // Assign the actions to keys
    pane.registerKeyboardAction(undoAction, KeyStroke.getKeyStroke(
        KeyEvent.VK_Z, InputEvent.CTRL_MASK), JComponent.WHEN_FOCUSED);
    pane.registerKeyboardAction(redoAction, KeyStroke.getKeyStroke(
        KeyEvent.VK_Y, InputEvent.CTRL_MASK), JComponent.WHEN_FOCUSED);
  }
  public void createMenuBar() {
    // Remove the existing menu items
    int count = menuBar.getMenuCount();
    for (int i = 0; i < count; i++) {
      menuBar.remove(menuBar.getMenu(0));
    }
    // Build the new menu.
    Action[] actions = pane.getActions();
    Hashtable actionHash = new Hashtable();
    count = actions.length;
    for (int i = 0; i < count; i++) {
      actionHash.put(actions[i].getValue(Action.NAME), actions[i]);
    }
    // Add the font menu
    JMenu menu = MenuBuilder.buildMenu("Font", fontSpec, actionHash);
    if (menu != null) {
      menuBar.add(menu);
    }
    // Add the alignment menu
    menu = MenuBuilder.buildMenu("Align", alignSpec, actionHash);
    if (menu != null) {
      menuBar.add(menu);
    }
  }
  // The Undo action
  public class UndoAction extends AbstractAction {
    public UndoAction(UndoManager manager) {
      this.manager = manager;
    }
    public void actionPerformed(ActionEvent evt) {
      try {
        manager.undo();
      } catch (CannotUndoException e) {
        Toolkit.getDefaultToolkit().beep();
      }
    }
    private UndoManager manager;
  }
  // The Redo action
  public class RedoAction extends AbstractAction {
    public RedoAction(UndoManager manager) {
      this.manager = manager;
    }
    public void actionPerformed(ActionEvent evt) {
      try {
        manager.redo();
      } catch (CannotRedoException e) {
        Toolkit.getDefaultToolkit().beep();
      }
    }
    private UndoManager manager;
  }
  public static void main(String[] args) {
    try {
        UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
    } catch (Exception evt) {}
  
    JFrame f = new UndoExample2();
    f.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent evt) {
        System.exit(0);
      }
    });
    f.setSize(250, 300);
    f.setVisible(true);
    // Create and show a frame monitoring undoable edits
    JFrame undoMonitor = new JFrame("Undo Monitor");
    final JTextArea textArea = new JTextArea();
    textArea.setEditable(false);
    undoMonitor.getContentPane().add(new JScrollPane(textArea));
    undoMonitor.setBounds(f.getLocation().x + f.getSize().width, f
        .getLocation().y, 400, 200);
    undoMonitor.setVisible(true);
    pane.getDocument().addUndoableEditListener(new UndoableEditListener() {
      public void undoableEditHappened(UndoableEditEvent evt) {
        UndoableEdit edit = evt.getEdit();
        textArea.append(edit.getPresentationName() + "("
            + edit.toString() + ")\n");
      }
    });
    // Create and show a frame monitoring document edits
    JFrame editMonitor = new JFrame("Edit Monitor");
    final JTextArea textArea2 = new JTextArea();
    textArea2.setEditable(false);
    editMonitor.getContentPane().add(new JScrollPane(textArea2));
    editMonitor.setBounds(undoMonitor.getLocation().x, undoMonitor
        .getLocation().y
        + undoMonitor.getSize().height, 400, 200);
    editMonitor.setVisible(true);
    pane.getDocument().addDocumentListener(new DocumentListener() {
      public void changedUpdate(DocumentEvent evt) {
        textArea2.append("Attribute change\n");
      }
      public void insertUpdate(DocumentEvent evt) {
        textArea2.append("Text insertion\n");
      }
      public void removeUpdate(DocumentEvent evt) {
        textArea2.append("Text removal\n");
      }
    });
  }
  private static JTextPane pane;
  private static JMenuBar menuBar;
  private static MenuSpec[] sizeSpec = new MenuSpec[] {
      new MenuSpec("Size 8", "font-size-8"),
      new MenuSpec("Size 10", "font-size-10"),
      new MenuSpec("Size 12", "font-size-12"),
      new MenuSpec("Size 14", "font-size-14"),
      new MenuSpec("Size 16", "font-size-16"),
      new MenuSpec("Size 18", "font-size-18"),
      new MenuSpec("Size 24", "font-size-24"),
      new MenuSpec("Size 36", "font-size-36"),
      new MenuSpec("Size 48", "font-size-48") };
  private static MenuSpec[] familySpec = new MenuSpec[] {
      new MenuSpec("Sans Serif", "font-family-SansSerif"),
      new MenuSpec("Monospaced", "font-family-Monospaced"),
      new MenuSpec("Serif", "font-family-Serif") };
  private static MenuSpec[] styleSpec = new MenuSpec[] {
      new MenuSpec("Bold", "font-bold"),
      new MenuSpec("Italics", "font-italic"),
      new MenuSpec("Underline", "font-underline") };
  // Menu definitions for fonts
  private static MenuSpec[] fontSpec = new MenuSpec[] {
      new MenuSpec("Size", sizeSpec), new MenuSpec("Family", familySpec),
      new MenuSpec("Style", styleSpec) };
  // Alignment
  private static MenuSpec[] alignSpec = new MenuSpec[] {
      new MenuSpec("Left", "left-justify"),
      new MenuSpec("Center", "center-justify"),
      new MenuSpec("Right", "right-justify") };
}
class MenuSpec {
  public MenuSpec(String name, MenuSpec[] subMenus) {
    this.name = name;
    this.subMenus = subMenus;
  }
  public MenuSpec(String name, String actionName) {
    this.name = name;
    this.actionName = actionName;
  }
  public MenuSpec(String name, Action action) {
    this.name = name;
    this.action = action;
  }
  public boolean isSubMenu() {
    return subMenus != null;
  }
  public boolean isAction() {
    return action != null;
  }
  public String getName() {
    return name;
  }
  public MenuSpec[] getSubMenus() {
    return subMenus;
  }
  public String getActionName() {
    return actionName;
  }
  public Action getAction() {
    return action;
  }
  private String name;
  private String actionName;
  private Action action;
  private MenuSpec[] subMenus;
}
class MenuBuilder {
  public static JMenu buildMenu(String name, MenuSpec[] menuSpecs,
      Hashtable actions) {
    int count = menuSpecs.length;
    JMenu menu = new JMenu(name);
    for (int i = 0; i < count; i++) {
      MenuSpec spec = menuSpecs[i];
      if (spec.isSubMenu()) {
        // Recurse to handle a sub menu
        JMenu subMenu = buildMenu(spec.getName(), spec.getSubMenus(),
            actions);
        if (subMenu != null) {
          menu.add(subMenu);
        }
      } else if (spec.isAction()) {
        // It"s an Action - add it directly to the menu
        menu.add(spec.getAction());
      } else {
        // It"s an action name - add it if possible
        String actionName = spec.getActionName();
        Action targetAction = (Action) actions.get(actionName);
        // Create the menu item
        JMenuItem menuItem = menu.add(spec.getName());
        if (targetAction != null) {
          // The editor kit knows the action
          menuItem.addActionListener(targetAction);
        } else {
          // Action not known - disable the menu item
          menuItem.setEnabled(false);
        }
      }
    }
    // Return null if nothing was added to the menu.
    if (menu.getMenuComponentCount() == 0) {
      menu = null;
    }
    return menu;
  }
}





Undo Example 3

 
/*
Core SWING Advanced Programming 
By Kim Topley
ISBN: 0 13 083292 8       
Publisher: Prentice Hall  
*/
import java.awt.BorderLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.event.UndoableEditListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;
import javax.swing.undo.UndoableEditSupport;
public class UndoExample3 extends JFrame {
  public UndoExample3() {
    super("Undo/Redo Example 3");
    DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("root");
    DefaultMutableTreeNode node = new DefaultMutableTreeNode("Apollo 8");
    rootNode.add(node);
    node.add(new DefaultMutableTreeNode("Borman"));
    node.add(new DefaultMutableTreeNode("Lovell"));
    node.add(new DefaultMutableTreeNode("Anders"));
    node = new DefaultMutableTreeNode("Apollo 11");
    rootNode.add(node);
    node.add(new DefaultMutableTreeNode("Armstrong"));
    node.add(new DefaultMutableTreeNode("Aldrin"));
    node.add(new DefaultMutableTreeNode("Collins"));
    node = new DefaultMutableTreeNode("Apollo 12");
    rootNode.add(node);
    node.add(new DefaultMutableTreeNode("Conrad"));
    node.add(new DefaultMutableTreeNode("Gordon"));
    node.add(new DefaultMutableTreeNode("Bean"));
    UndoableTree tree = new UndoableTree(rootNode);
    getContentPane().add(new JScrollPane(tree), BorderLayout.CENTER);
    // Create the undo manager and actions
    UndoManager manager = new UndoManager();
    tree.addUndoableEditListener(manager);
    Action undoAction = new UndoAction(manager);
    Action redoAction = new RedoAction(manager);
    // Add the actions to buttons
    JPanel panel = new JPanel();
    JButton undoButton = new JButton("Undo");
    JButton redoButton = new JButton("Redo");
    undoButton.addActionListener(undoAction);
    redoButton.addActionListener(redoAction);
    panel.add(undoButton);
    panel.add(redoButton);
    getContentPane().add(panel, BorderLayout.SOUTH);
    // Assign the actions to keys
    ((JComponent) getContentPane()).registerKeyboardAction(undoAction,
        KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_MASK),
        JComponent.WHEN_IN_FOCUSED_WINDOW);
    ((JComponent) getContentPane()).registerKeyboardAction(redoAction,
        KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_MASK),
        JComponent.WHEN_IN_FOCUSED_WINDOW);
  }
  // The Undo action
  public class UndoAction extends AbstractAction {
    public UndoAction(UndoManager manager) {
      this.manager = manager;
    }
    public void actionPerformed(ActionEvent evt) {
      try {
        manager.undo();
      } catch (CannotUndoException e) {
        Toolkit.getDefaultToolkit().beep();
      }
    }
    private UndoManager manager;
  }
  // The Redo action
  public class RedoAction extends AbstractAction {
    public RedoAction(UndoManager manager) {
      this.manager = manager;
    }
    public void actionPerformed(ActionEvent evt) {
      try {
        manager.redo();
      } catch (CannotRedoException e) {
        Toolkit.getDefaultToolkit().beep();
      }
    }
    private UndoManager manager;
  }
  public static void main(String[] args) {
    try {
        UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
    } catch (Exception evt) {}
  
    JFrame f = new UndoExample3();
    f.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent evt) {
        System.exit(0);
      }
    });
    f.pack();
    f.setVisible(true);
  }
}
class UndoableTree extends JTree {
  // Only one constructor for brevity
  public UndoableTree(TreeNode root) {
    super(root);
  }
  public void addUndoableEditListener(UndoableEditListener l) {
    support.addUndoableEditListener(l);
  }
  public void removeUndoableEditListener(UndoableEditListener l) {
    support.removeUndoableEditListener(l);
  }
  public void collapsePath(TreePath path) {
    boolean wasExpanded = isExpanded(path);
    super.collapsePath(path);
    boolean isExpanded = isExpanded(path);
    if (isExpanded != wasExpanded) {
      support.postEdit(new CollapseEdit(path));
    }
  }
  public void expandPath(TreePath path) {
    boolean wasExpanded = isExpanded(path);
    super.expandPath(path);
    boolean isExpanded = isExpanded(path);
    if (isExpanded != wasExpanded) {
      support.postEdit(new ExpandEdit(path));
    }
  }
  private void undoExpansion(TreePath path) {
    super.collapsePath(path);
  }
  private void undoCollapse(TreePath path) {
    super.expandPath(path);
  }
  private class CollapseEdit extends AbstractUndoableEdit {
    public CollapseEdit(TreePath path) {
      this.path = path;
    }
    public void undo() throws CannotUndoException {
      super.undo();
      UndoableTree.this.undoCollapse(path);
    }
    public void redo() throws CannotRedoException {
      super.redo();
      UndoableTree.this.undoExpansion(path);
    }
    public String getPresentationName() {
      return "node collapse";
    }
    private TreePath path;
  }
  private class ExpandEdit extends AbstractUndoableEdit {
    public ExpandEdit(TreePath path) {
      this.path = path;
    }
    public void undo() throws CannotUndoException {
      super.undo();
      UndoableTree.this.undoExpansion(path);
    }
    public void redo() throws CannotRedoException {
      super.redo();
      UndoableTree.this.undoCollapse(path);
    }
    public String getPresentationName() {
      return "node expansion";
    }
    private TreePath path;
  }
  private UndoableEditSupport support = new UndoableEditSupport(this);
}





Undo Example 4

 
/*
Core SWING Advanced Programming 
By Kim Topley
ISBN: 0 13 083292 8       
Publisher: Prentice Hall  
*/

import java.awt.BorderLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.event.UndoableEditListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;
import javax.swing.undo.UndoableEditSupport;
public class UndoExample4 extends JFrame {
  public UndoExample4() {
    super("Undo/Redo Example 4");
    DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("root");
    DefaultMutableTreeNode node = new DefaultMutableTreeNode("Apollo 8");
    rootNode.add(node);
    node.add(new DefaultMutableTreeNode("Borman"));
    node.add(new DefaultMutableTreeNode("Lovell"));
    node.add(new DefaultMutableTreeNode("Anders"));
    node = new DefaultMutableTreeNode("Apollo 11");
    rootNode.add(node);
    node.add(new DefaultMutableTreeNode("Armstrong"));
    node.add(new DefaultMutableTreeNode("Aldrin"));
    node.add(new DefaultMutableTreeNode("Collins"));
    node = new DefaultMutableTreeNode("Apollo 12");
    rootNode.add(node);
    node.add(new DefaultMutableTreeNode("Conrad"));
    node.add(new DefaultMutableTreeNode("Gordon"));
    node.add(new DefaultMutableTreeNode("Bean"));
    UndoableTree2 tree = new UndoableTree2(rootNode);
    getContentPane().add(new JScrollPane(tree), BorderLayout.CENTER);
    // Create the undo manager and actions
    UndoManager manager = new UndoManager();
    tree.addUndoableEditListener(manager);
    Action undoAction = new UndoAction(manager);
    Action redoAction = new RedoAction(manager);
    // Add the actions to buttons
    JPanel panel = new JPanel();
    JButton undoButton = new JButton("Undo");
    JButton redoButton = new JButton("Redo");
    undoButton.addActionListener(undoAction);
    redoButton.addActionListener(redoAction);
    panel.add(undoButton);
    panel.add(redoButton);
    getContentPane().add(panel, BorderLayout.SOUTH);
    // Assign the actions to keys
    ((JComponent) getContentPane()).registerKeyboardAction(undoAction,
        KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_MASK),
        JComponent.WHEN_IN_FOCUSED_WINDOW);
    ((JComponent) getContentPane()).registerKeyboardAction(redoAction,
        KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_MASK),
        JComponent.WHEN_IN_FOCUSED_WINDOW);
  }
  // The Undo action
  public class UndoAction extends AbstractAction {
    public UndoAction(UndoManager manager) {
      this.manager = manager;
    }
    public void actionPerformed(ActionEvent evt) {
      try {
        manager.undo();
      } catch (CannotUndoException e) {
        Toolkit.getDefaultToolkit().beep();
      }
    }
    private UndoManager manager;
  }
  // The Redo action
  public class RedoAction extends AbstractAction {
    public RedoAction(UndoManager manager) {
      this.manager = manager;
    }
    public void actionPerformed(ActionEvent evt) {
      try {
        manager.redo();
      } catch (CannotRedoException e) {
        Toolkit.getDefaultToolkit().beep();
      }
    }
    private UndoManager manager;
  }
  public static void main(String[] args) {
    try {
        UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
    } catch (Exception evt) {}
  
    JFrame f = new UndoExample4();
    f.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent evt) {
        System.exit(0);
      }
    });
    f.pack();
    f.setVisible(true);
  }
}
class UndoableTree2 extends JTree {
  public UndoableTree2(TreeNode root) {
    super(root);
  }
  public void addUndoableEditListener(UndoableEditListener l) {
    support.addUndoableEditListener(l);
  }
  public void removeUndoableEditListener(UndoableEditListener l) {
    support.removeUndoableEditListener(l);
  }
  public void collapsePath(TreePath path) {
    boolean wasExpanded = isExpanded(path);
    TreePath[] selections = getSelectionPaths();
    super.collapsePath(path);
    boolean isExpanded = isExpanded(path);
    if (isExpanded != wasExpanded) {
      TreePath[] newSelections = getSelectionPaths();
      support.beginUpdate();
      support.postEdit(new SelectionEdit(selections, newSelections));
      support.postEdit(new CollapseEdit(path));
      support.endUpdate();
    }
  }
  public void expandPath(TreePath path) {
    boolean wasExpanded = isExpanded(path);
    TreePath[] selections = getSelectionPaths();
    super.expandPath(path);
    boolean isExpanded = isExpanded(path);
    if (isExpanded != wasExpanded) {
      TreePath[] newSelections = getSelectionPaths();
      support.beginUpdate();
      support.postEdit(new SelectionEdit(selections, newSelections));
      support.postEdit(new ExpandEdit(path));
      support.endUpdate();
    }
  }
  private void undoExpansion(TreePath path) {
    super.collapsePath(path);
  }
  private void undoCollapse(TreePath path) {
    super.expandPath(path);
  }
  private class CollapseEdit extends AbstractUndoableEdit {
    public CollapseEdit(TreePath path) {
      this.path = path;
    }
    public void undo() throws CannotUndoException {
      super.undo();
      UndoableTree2.this.undoCollapse(path);
    }
    public void redo() throws CannotRedoException {
      super.redo();
      UndoableTree2.this.undoExpansion(path);
    }
    public String getPresentationName() {
      return "node collapse";
    }
    private TreePath path;
  }
  private class ExpandEdit extends AbstractUndoableEdit {
    public ExpandEdit(TreePath path) {
      this.path = path;
    }
    public void undo() throws CannotUndoException {
      super.undo();
      UndoableTree2.this.undoExpansion(path);
    }
    public void redo() throws CannotRedoException {
      super.redo();
      UndoableTree2.this.undoCollapse(path);
    }
    public String getPresentationName() {
      return "node expansion";
    }
    private TreePath path;
  }
  private class SelectionEdit extends AbstractUndoableEdit {
    public SelectionEdit(TreePath[] oldSelections, TreePath[] newSelections) {
      this.oldSelections = oldSelections;
      this.newSelections = newSelections;
    }
    public void undo() throws CannotUndoException {
      super.undo();
      UndoableTree2.this.setSelectionPaths(oldSelections);
    }
    public void redo() throws CannotRedoException {
      super.redo();
      UndoableTree2.this.setSelectionPaths(newSelections);
    }
    public String getPresentationName() {
      return "selection change";
    }
    private TreePath[] oldSelections;
    private TreePath[] newSelections;
  }
  private UndoableEditSupport support = new UndoableEditSupport(this);
}





Undo Example 5

 
/*
Core SWING Advanced Programming 
By Kim Topley
ISBN: 0 13 083292 8       
Publisher: Prentice Hall  
*/

import java.awt.BorderLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Hashtable;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextPane;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.EventListenerList;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;
import javax.swing.undo.UndoableEdit;
public class UndoExample5 extends JFrame {
  public UndoExample5() {
    super("Undo/Redo Example 5");
    pane = new JTextPane();
    pane.setEditable(true); // Editable
    getContentPane().add(new JScrollPane(pane), BorderLayout.CENTER);
    // Add a menu bar
    menuBar = new JMenuBar();
    setJMenuBar(menuBar);
    // Populate the menu bar
    createMenuBar();
    // Create the undo manager and actions
    MonitorableUndoManager manager = new MonitorableUndoManager();
    pane.getDocument().addUndoableEditListener(manager);
    Action undoAction = new UndoAction(manager);
    Action redoAction = new RedoAction(manager);
    // Add the actions to buttons
    JPanel panel = new JPanel();
    final JButton undoButton = new JButton("Undo");
    final JButton redoButton = new JButton("Redo");
    undoButton.addActionListener(undoAction);
    redoButton.addActionListener(redoAction);
    undoButton.setEnabled(false);
    redoButton.setEnabled(false);
    panel.add(undoButton);
    panel.add(redoButton);
    getContentPane().add(panel, BorderLayout.SOUTH);
    // Assign the actions to keys
    pane.registerKeyboardAction(undoAction, KeyStroke.getKeyStroke(
        KeyEvent.VK_Z, InputEvent.CTRL_MASK), JComponent.WHEN_FOCUSED);
    pane.registerKeyboardAction(redoAction, KeyStroke.getKeyStroke(
        KeyEvent.VK_Y, InputEvent.CTRL_MASK), JComponent.WHEN_FOCUSED);
    // Handle events from the MonitorableUndoManager
    manager.addChangeListener(new ChangeListener() {
      public void stateChanged(ChangeEvent evt) {
        MonitorableUndoManager m = (MonitorableUndoManager) evt
            .getSource();
        boolean canUndo = m.canUndo();
        boolean canRedo = m.canRedo();
        undoButton.setEnabled(canUndo);
        redoButton.setEnabled(canRedo);
        undoButton.setToolTipText(canUndo ? m.getUndoPresentationName()
            : null);
        redoButton.setToolTipText(canRedo ? m.getRedoPresentationName()
            : null);
      }
    });
  }
  public void createMenuBar() {
    // Remove the existing menu items
    int count = menuBar.getMenuCount();
    for (int i = 0; i < count; i++) {
      menuBar.remove(menuBar.getMenu(0));
    }
    // Build the new menu.
    Action[] actions = pane.getActions();
    Hashtable actionHash = new Hashtable();
    count = actions.length;
    for (int i = 0; i < count; i++) {
      actionHash.put(actions[i].getValue(Action.NAME), actions[i]);
    }
    // Add the font menu
    JMenu menu = MenuBuilder.buildMenu("Font", fontSpec, actionHash);
    if (menu != null) {
      menuBar.add(menu);
    }
    // Add the alignment menu
    menu = MenuBuilder.buildMenu("Align", alignSpec, actionHash);
    if (menu != null) {
      menuBar.add(menu);
    }
  }
  // The Undo action
  public class UndoAction extends AbstractAction {
    public UndoAction(UndoManager manager) {
      this.manager = manager;
    }
    public void actionPerformed(ActionEvent evt) {
      try {
        manager.undo();
      } catch (CannotUndoException e) {
        Toolkit.getDefaultToolkit().beep();
      }
    }
    private UndoManager manager;
  }
  // The Redo action
  public class RedoAction extends AbstractAction {
    public RedoAction(UndoManager manager) {
      this.manager = manager;
    }
    public void actionPerformed(ActionEvent evt) {
      try {
        manager.redo();
      } catch (CannotRedoException e) {
        Toolkit.getDefaultToolkit().beep();
      }
    }
    private UndoManager manager;
  }
  public static void main(String[] args) {
    try {
        UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
    } catch (Exception evt) {}
  
    JFrame f = new UndoExample5();
    f.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent evt) {
        System.exit(0);
      }
    });
    f.setSize(250, 300);
    f.setVisible(true);
    // Create and show a frame monitoring undoable edits
    JFrame undoMonitor = new JFrame("Undo Monitor");
    final JTextArea textArea = new JTextArea();
    textArea.setEditable(false);
    undoMonitor.getContentPane().add(new JScrollPane(textArea));
    undoMonitor.setBounds(f.getLocation().x + f.getSize().width, f
        .getLocation().y, 400, 200);
    undoMonitor.setVisible(true);
    pane.getDocument().addUndoableEditListener(new UndoableEditListener() {
      public void undoableEditHappened(UndoableEditEvent evt) {
        UndoableEdit edit = evt.getEdit();
        textArea.append(edit.getPresentationName() + "("
            + edit.toString() + ")\n");
      }
    });
    // Create and show a frame monitoring document edits
    JFrame editMonitor = new JFrame("Edit Monitor");
    final JTextArea textArea2 = new JTextArea();
    textArea2.setEditable(false);
    editMonitor.getContentPane().add(new JScrollPane(textArea2));
    editMonitor.setBounds(undoMonitor.getLocation().x, undoMonitor
        .getLocation().y
        + undoMonitor.getSize().height, 400, 200);
    editMonitor.setVisible(true);
    pane.getDocument().addDocumentListener(new DocumentListener() {
      public void changedUpdate(DocumentEvent evt) {
        textArea2.append("Attribute change\n");
      }
      public void insertUpdate(DocumentEvent evt) {
        textArea2.append("Text insertion\n");
      }
      public void removeUpdate(DocumentEvent evt) {
        textArea2.append("Text removal\n");
      }
    });
  }
  private static JTextPane pane;
  private static JMenuBar menuBar;
  private static MenuSpec[] sizeSpec = new MenuSpec[] {
      new MenuSpec("Size 8", "font-size-8"),
      new MenuSpec("Size 10", "font-size-10"),
      new MenuSpec("Size 12", "font-size-12"),
      new MenuSpec("Size 14", "font-size-14"),
      new MenuSpec("Size 16", "font-size-16"),
      new MenuSpec("Size 18", "font-size-18"),
      new MenuSpec("Size 24", "font-size-24"),
      new MenuSpec("Size 36", "font-size-36"),
      new MenuSpec("Size 48", "font-size-48") };
  private static MenuSpec[] familySpec = new MenuSpec[] {
      new MenuSpec("Sans Serif", "font-family-SansSerif"),
      new MenuSpec("Monospaced", "font-family-Monospaced"),
      new MenuSpec("Serif", "font-family-Serif") };
  private static MenuSpec[] styleSpec = new MenuSpec[] {
      new MenuSpec("Bold", "font-bold"),
      new MenuSpec("Italics", "font-italic"),
      new MenuSpec("Underline", "font-underline") };
  // Menu definitions for fonts
  private static MenuSpec[] fontSpec = new MenuSpec[] {
      new MenuSpec("Size", sizeSpec), new MenuSpec("Family", familySpec),
      new MenuSpec("Style", styleSpec) };
  // Alignment
  private static MenuSpec[] alignSpec = new MenuSpec[] {
      new MenuSpec("Left", "left-justify"),
      new MenuSpec("Center", "center-justify"),
      new MenuSpec("Right", "right-justify") };
}
class MonitorableUndoManager extends UndoManager {
  // List of listeners for events from this object
  protected EventListenerList listenerList = new EventListenerList();
  // A ChangeEvent dedicated to a single MonitorableUndoManager
  protected ChangeEvent changeEvent;
  // Super class overrides
  public synchronized void setLimit(int l) {
    super.setLimit(l);
    fireChangeEvent();
  }
  public synchronized void discardAllEdits() {
    super.discardAllEdits();
    fireChangeEvent();
  }
  public synchronized void undo() throws CannotUndoException {
    super.undo();
    fireChangeEvent();
  }
  public synchronized void redo() throws CannotRedoException {
    super.redo();
    fireChangeEvent();
  }
  public synchronized boolean addEdit(UndoableEdit anEdit) {
    boolean retval = super.addEdit(anEdit);
    fireChangeEvent();
    return retval;
  }
  // Support for ChangeListeners
  public void addChangeListener(ChangeListener l) {
    listenerList.add(ChangeListener.class, l);
  }
  public void removeChangeListener(ChangeListener l) {
    listenerList.remove(ChangeListener.class, l);
  }
  protected void fireChangeEvent() {
    Object[] listeners = listenerList.getListenerList();
    for (int i = listeners.length - 2; i >= 0; i -= 2) {
      if (listeners[i] == ChangeListener.class) {
        if (changeEvent == null) {
          changeEvent = new ChangeEvent(this);
        }
        ((ChangeListener) listeners[i + 1]).stateChanged(changeEvent);
      }
    }
  }
}
class MenuSpec {
  public MenuSpec(String name, MenuSpec[] subMenus) {
    this.name = name;
    this.subMenus = subMenus;
  }
  public MenuSpec(String name, String actionName) {
    this.name = name;
    this.actionName = actionName;
  }
  public MenuSpec(String name, Action action) {
    this.name = name;
    this.action = action;
  }
  public boolean isSubMenu() {
    return subMenus != null;
  }
  public boolean isAction() {
    return action != null;
  }
  public String getName() {
    return name;
  }
  public MenuSpec[] getSubMenus() {
    return subMenus;
  }
  public String getActionName() {
    return actionName;
  }
  public Action getAction() {
    return action;
  }
  private String name;
  private String actionName;
  private Action action;
  private MenuSpec[] subMenus;
}
class MenuBuilder {
  public static JMenu buildMenu(String name, MenuSpec[] menuSpecs,
      Hashtable actions) {
    int count = menuSpecs.length;
    JMenu menu = new JMenu(name);
    for (int i = 0; i < count; i++) {
      MenuSpec spec = menuSpecs[i];
      if (spec.isSubMenu()) {
        // Recurse to handle a sub menu
        JMenu subMenu = buildMenu(spec.getName(), spec.getSubMenus(),
            actions);
        if (subMenu != null) {
          menu.add(subMenu);
        }
      } else if (spec.isAction()) {
        // It"s an Action - add it directly to the menu
        menu.add(spec.getAction());
      } else {
        // It"s an action name - add it if possible
        String actionName = spec.getActionName();
        Action targetAction = (Action) actions.get(actionName);
        // Create the menu item
        JMenuItem menuItem = menu.add(spec.getName());
        if (targetAction != null) {
          // The editor kit knows the action
          menuItem.addActionListener(targetAction);
        } else {
          // Action not known - disable the menu item
          menuItem.setEnabled(false);
        }
      }
    }
    // Return null if nothing was added to the menu.
    if (menu.getMenuComponentCount() == 0) {
      menu = null;
    }
    return menu;
  }
}





Undo Example 6

 
/*
Core SWING Advanced Programming 
By Kim Topley
ISBN: 0 13 083292 8       
Publisher: Prentice Hall  
*/

import java.awt.Dimension;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.dnd.Autoscroll;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.ruparator;
import javax.swing.JFrame;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
public class FileTreeTest {
  public static void main(String[] args) {
    try {
        UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
    } catch (Exception evt) {}
  
    try {
      JFrame f = new JFrame("File Tree Test");
      final FileTree ft = new FileTree("D:\\");
      ft.addTreeSelectionListener(new TreeSelectionListener() {
        public void valueChanged(TreeSelectionEvent evt) {
          TreePath path = evt.getPath();
          String name = ft.getPathName(path);
          System.out.println("File " + name + " has been "
              + (evt.isAddedPath() ? "selected" : "deselected"));
        }
      });
      f.getContentPane().add(new JScrollPane(ft));
      f.setSize(300, 300);
      f.addWindowListener(new WindowAdapter() {
        public void windowClosing(WindowEvent evt) {
          System.exit(0);
        }
      });
      f.setVisible(true);
    } catch (FileNotFoundException e) {
      System.out.println("File " + args[0] + " not found");
    }
  }
}
class FileTree extends JTree implements Autoscroll {
  public static final Insets defaultScrollInsets = new Insets(8, 8, 8, 8);
  protected Insets scrollInsets = defaultScrollInsets;
  public FileTree(String path) throws FileNotFoundException,
      SecurityException {
    super((TreeModel) null); // Create the JTree itself
    // Use horizontal and vertical lines
    putClientProperty("JTree.lineStyle", "Angled");
    // Create the first node
    FileTreeNode rootNode = new FileTreeNode(null, path);
    // Populate the root node with its subdirectories
    boolean addedNodes = rootNode.populateDirectories(true);
    setModel(new DefaultTreeModel(rootNode));
    // Listen for Tree Selection Events
    addTreeExpansionListener(new TreeExpansionHandler());
  }
  // Returns the full pathname for a path, or null if not a known path
  public String getPathName(TreePath path) {
    Object o = path.getLastPathComponent();
    if (o instanceof FileTreeNode) {
      return ((FileTreeNode) o).fullName;
    }
    return null;
  }
  // Adds a new node to the tree after construction.
  // Returns the inserted node, or null if the parent
  // directory has not been expanded.
  public FileTreeNode addNode(FileTreeNode parent, String name) {
    int index = parent.addNode(name);
    if (index != -1) {
      ((DefaultTreeModel) getModel()).nodesWereInserted(parent,
          new int[] { index });
      return (FileTreeNode) parent.getChildAt(index);
    }
    // No node was created
    return null;
  }
  // Autoscrolling support
  public void setScrollInsets(Insets insets) {
    this.scrollInsets = insets;
  }
  public Insets getScrollInsets() {
    return scrollInsets;
  }
  // Implementation of Autoscroll interface
  public Insets getAutoscrollInsets() {
    Rectangle r = getVisibleRect();
    Dimension size = getSize();
    Insets i = new Insets(r.y + scrollInsets.top, r.x + scrollInsets.left,
        size.height - r.y - r.height + scrollInsets.bottom, size.width
            - r.x - r.width + scrollInsets.right);
    return i;
  }
  public void autoscroll(Point location) {
    JScrollPane scroller = (JScrollPane) SwingUtilities.getAncestorOfClass(
        JScrollPane.class, this);
    if (scroller != null) {
      JScrollBar hBar = scroller.getHorizontalScrollBar();
      JScrollBar vBar = scroller.getVerticalScrollBar();
      Rectangle r = getVisibleRect();
      if (location.x <= r.x + scrollInsets.left) {
        // Need to scroll left
        hBar.setValue(hBar.getValue() - hBar.getUnitIncrement(-1));
      }
      if (location.y <= r.y + scrollInsets.top) {
        // Need to scroll up
        vBar.setValue(vBar.getValue() - vBar.getUnitIncrement(-1));
      }
      if (location.x >= r.x + r.width - scrollInsets.right) {
        // Need to scroll right
        hBar.setValue(hBar.getValue() + hBar.getUnitIncrement(1));
      }
      if (location.y >= r.y + r.height - scrollInsets.bottom) {
        // Need to scroll down
        vBar.setValue(vBar.getValue() + vBar.getUnitIncrement(1));
      }
    }
  }
  // Inner class that represents a node in this file system tree
  public static class FileTreeNode extends DefaultMutableTreeNode {
    public FileTreeNode(String parent, String name)
        throws SecurityException, FileNotFoundException {
      this.name = name;
      // See if this node exists and whether it is a directory
      fullName = parent == null ? name : parent + File.separator + name;
      File f = new File(fullName);
      if (f.exists() == false) {
        throw new FileNotFoundException("File " + fullName
            + " does not exist");
      }
      isDir = f.isDirectory();
      // Hack for Windows which doesn"t consider a drive to be a
      // directory!
      if (isDir == false && f.isFile() == false) {
        isDir = true;
      }
    }
    // Override isLeaf to check whether this is a directory
    public boolean isLeaf() {
      return !isDir;
    }
    // Override getAllowsChildren to check whether this is a directory
    public boolean getAllowsChildren() {
      return isDir;
    }
    // Return whether this is a directory
    public boolean isDir() {
      return isDir;
    }
    // Get full path
    public String getFullName() {
      return fullName;
    }
    // For display purposes, we return our own name
    public String toString() {
      return name;
    }
    // If we are a directory, scan our contents and populate
    // with children. In addition, populate those children
    // if the "descend" flag is true. We only descend once,
    // to avoid recursing the whole subtree.
    // Returns true if some nodes were added
    boolean populateDirectories(boolean descend) {
      boolean addedNodes = false;
      // Do this only once
      if (populated == false) {
        File f;
        try {
          f = new File(fullName);
        } catch (SecurityException e) {
          populated = true;
          return false;
        }
        if (interim == true) {
          // We have had a quick look here before:
          // remove the dummy node that we added last time
          removeAllChildren();
          interim = false;
        }
        String[] names = f.list(); // Get list of contents
        // Process the contents
        ArrayList list = new ArrayList();
        for (int i = 0; i < names.length; i++) {
          String name = names[i];
          File d = new File(fullName, name);
          try {
            FileTreeNode node = new FileTreeNode(fullName, name);
            list.add(node);
            if (descend && d.isDirectory()) {
              node.populateDirectories(false);
            }
            addedNodes = true;
            if (descend == false) {
              // Only add one node if not descending
              break;
            }
          } catch (Throwable t) {
            // Ignore phantoms or access problems
          }
        }
        if (addedNodes == true) {
          // Now sort the list of contained files and directories
          Object[] nodes = list.toArray();
          Arrays.sort(nodes, new Comparator() {
            public boolean equals(Object o) {
              return false;
            }
            public int compare(Object o1, Object o2) {
              FileTreeNode node1 = (FileTreeNode) o1;
              FileTreeNode node2 = (FileTreeNode) o2;
              // Directories come first
              if (node1.isDir != node2.isDir) {
                return node1.isDir ? -1 : +1;
              }
              // Both directories or both files -
              // compare based on pathname
              return node1.fullName.rupareTo(node2.fullName);
            }
          });
          // Add sorted items as children of this node
          for (int j = 0; j < nodes.length; j++) {
            this.add((FileTreeNode) nodes[j]);
          }
        }
        // If we were scanning to get all subdirectories,
        // or if we found no content, there is no
        // reason to look at this directory again, so
        // set populated to true. Otherwise, we set interim
        // so that we look again in the future if we need to
        if (descend == true || addedNodes == false) {
          populated = true;
        } else {
          // Just set interim state
          interim = true;
        }
      }
      return addedNodes;
    }
    // Adding a new file or directory after
    // constructing the FileTree. Returns
    // the index of the inserted node.
    public int addNode(String name) {
      // If not populated yet, do nothing
      if (populated == true) {
        // Do not add a new node if
        // the required node is already there
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
          FileTreeNode node = (FileTreeNode) getChildAt(i);
          if (node.name.equals(name)) {
            // Already exists - ensure
            // we repopulate
            if (node.isDir()) {
              node.interim = true;
              node.populated = false;
            }
            return -1;
          }
        }
        // Add a new node
        try {
          FileTreeNode node = new FileTreeNode(fullName, name);
          add(node);
          return childCount;
        } catch (Exception e) {
        }
      }
      return -1;
    }
    protected String name; // Name of this component
    protected String fullName; // Full pathname
    protected boolean populated;// true if we have been populated
    protected boolean interim; // true if we are in interim state
    protected boolean isDir; // true if this is a directory
  }
  // Inner class that handles Tree Expansion Events
  protected class TreeExpansionHandler implements TreeExpansionListener {
    public void treeExpanded(TreeExpansionEvent evt) {
      TreePath path = evt.getPath(); // The expanded path
      JTree tree = (JTree) evt.getSource(); // The tree
      // Get the last component of the path and
      // arrange to have it fully populated.
      FileTreeNode node = (FileTreeNode) path.getLastPathComponent();
      if (node.populateDirectories(true)) {
        ((DefaultTreeModel) tree.getModel()).nodeStructureChanged(node);
      }
    }
    public void treeCollapsed(TreeExpansionEvent evt) {
      // Nothing to do
    }
  }
}





Undo Example 7

 
/*
Definitive Guide to Swing for Java 2, Second Edition
By John Zukowski     
ISBN: 1-893115-78-X
Publisher: APress
*/
import java.awt.BorderLayout;
import java.awt.ruponent;
import java.awt.Container;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JToolBar;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;
public class UndoSample {
  public static void main(String args[]) {
    JFrame frame = new JFrame("Undo Sample");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    JTextArea textArea = new JTextArea();
    JScrollPane scrollPane = new JScrollPane(textArea);
    UndoManager manager = new UndoManager();
    textArea.getDocument().addUndoableEditListener(manager);
    JToolBar toolbar = new JToolBar();
    toolbar.add(UndoManagerHelper.getUndoAction(manager));
    toolbar.add(UndoManagerHelper.getRedoAction(manager));
    Container content = frame.getContentPane();
    content.add(toolbar, BorderLayout.NORTH);
    content.add(scrollPane, BorderLayout.CENTER);
    frame.setSize(300, 150);
    frame.setVisible(true);
  }
}
class UndoManagerHelper {
  public static Action getUndoAction(UndoManager manager, String label) {
    return new UndoAction(manager, label);
  }
  public static Action getUndoAction(UndoManager manager) {
    return new UndoAction(manager, "Undo");
  }
  public static Action getRedoAction(UndoManager manager, String label) {
    return new RedoAction(manager, label);
  }
  public static Action getRedoAction(UndoManager manager) {
    return new RedoAction(manager, "Redo");
  }
  private abstract static class UndoRedoAction extends AbstractAction {
    UndoManager undoManager = new UndoManager();
    String errorMessage = "Cannot undo";
    String errorTitle = "Undo Problem";
    protected UndoRedoAction(UndoManager manager, String name) {
      super(name);
      undoManager = manager;
    }
    public void setErrorMessage(String newValue) {
      errorMessage = newValue;
    }
    public void setErrorTitle(String newValue) {
      errorTitle = newValue;
    }
    protected void showMessage(Object source) {
      if (source instanceof Component) {
        JOptionPane.showMessageDialog((Component) source, errorMessage,
            errorTitle, JOptionPane.WARNING_MESSAGE);
      } else {
        System.err.println(errorMessage);
      }
    }
  }
  public static class UndoAction extends UndoRedoAction {
    public UndoAction(UndoManager manager, String name) {
      super(manager, name);
      setErrorMessage("Cannot undo");
      setErrorTitle("Undo Problem");
    }
    public void actionPerformed(ActionEvent actionEvent) {
      try {
        undoManager.undo();
      } catch (CannotUndoException cannotUndoException) {
        showMessage(actionEvent.getSource());
      }
    }
  }
  public static class RedoAction extends UndoRedoAction {
    String errorMessage = "Cannot redo";
    String errorTitle = "Redo Problem";
    public RedoAction(UndoManager manager, String name) {
      super(manager, name);
      setErrorMessage("Cannot redo");
      setErrorTitle("Redo Problem");
    }
    public void actionPerformed(ActionEvent actionEvent) {
      try {
        undoManager.redo();
      } catch (CannotRedoException cannotRedoException) {
        showMessage(actionEvent.getSource());
      }
    }
  }
}





Undo manager

 
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Enumeration;
import java.util.Vector;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.event.UndoableEditEvent;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.UndoManager;
public class UndoManagerDemo extends JFrame {
  protected Vector pointVector = new Vector();
  protected PaintCanvas canvas = new PaintCanvas(pointVector);
  protected UndoManager undoManager = new UndoManager();
  protected JButton undoButton = new JButton("Undo");
  protected JButton redoButton = new JButton("Redo");
  public UndoManagerDemo() {
    super("Undo/Redo Demo");
    undoButton.setEnabled(false);
    redoButton.setEnabled(false);
    JPanel buttonPanel = new JPanel(new GridLayout());
    buttonPanel.add(undoButton);
    buttonPanel.add(redoButton);
    getContentPane().add(buttonPanel, BorderLayout.NORTH);
    getContentPane().add(canvas, BorderLayout.CENTER);
    canvas.addMouseListener(new MouseAdapter() {
      public void mousePressed(MouseEvent e) {
        Point point = new Point(e.getX(), e.getY());
        pointVector.addElement(point);
        undoManager.undoableEditHappened(new UndoableEditEvent(
            canvas, new UndoablePaintSquare(point, pointVector)));
        undoButton.setText(undoManager.getUndoPresentationName());
        redoButton.setText(undoManager.getRedoPresentationName());
        undoButton.setEnabled(undoManager.canUndo());
        redoButton.setEnabled(undoManager.canRedo());
        canvas.repaint();
      }
    });
    undoButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        try {
          undoManager.undo();
        } catch (CannotRedoException cre) {
          cre.printStackTrace();
        }
        canvas.repaint();
        undoButton.setEnabled(undoManager.canUndo());
        redoButton.setEnabled(undoManager.canRedo());
      }
    });
    redoButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        try {
          undoManager.redo();
        } catch (CannotRedoException cre) {
          cre.printStackTrace();
        }
        canvas.repaint();
        undoButton.setEnabled(undoManager.canUndo());
        redoButton.setEnabled(undoManager.canRedo());
      }
    });
    setSize(400, 300);
    setVisible(true);
  }
  public static void main(String argv[]) {
    new UndoManagerDemo();
  }
  class PaintCanvas extends JPanel {
    private Vector points;
    protected int width = 50;
    protected int height = 50;
    public PaintCanvas(Vector v) {
      super();
      points = v;
      setOpaque(true);
      setBackground(Color.white);
    }
    public void paintComponent(Graphics g) {
      super.paintComponent(g);
      g.setColor(Color.black);
      
      for(int i=0;i<points.size();i++){
        Point point = (Point) points.get(i);
        g.drawRect(point.x, point.y, width, height);
      }
    }
  }
  class UndoablePaintSquare extends AbstractUndoableEdit {
    protected Vector points;
    protected Point point;
    public UndoablePaintSquare(Point p, Vector v) {
      points = v;
      point = p;
    }
    public String getPresentationName() {
      return "Square Addition";
    }
    public void undo() {
      super.undo();
      points.remove(point);
    }
    public void redo() {
      super.redo();
      points.add(point);
    }
  }
}





Undo redo textarea

 
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.UndoManager;
public class UndoRedoTextArea extends JFrame {
  protected JTextArea textArea = new JTextArea();
  protected UndoManager undoManager = new UndoManager();
  protected JButton undoButton = new JButton("Undo");
  protected JButton redoButton = new JButton("Redo");
  public UndoRedoTextArea() {
    super("Undo/Redo Demo");
    undoButton.setEnabled(false);
    redoButton.setEnabled(false);
    JPanel buttonPanel = new JPanel(new GridLayout());
    buttonPanel.add(undoButton);
    buttonPanel.add(redoButton);
    JScrollPane scroller = new JScrollPane(textArea);
    getContentPane().add(buttonPanel, BorderLayout.NORTH);
    getContentPane().add(scroller, BorderLayout.CENTER);
    textArea.getDocument().addUndoableEditListener(
        new UndoableEditListener() {
          public void undoableEditHappened(UndoableEditEvent e) {
            undoManager.addEdit(e.getEdit());
            updateButtons();
          }
        });
    undoButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        try {
          undoManager.undo();
        } catch (CannotRedoException cre) {
          cre.printStackTrace();
        }
        updateButtons();
      }
    });
    redoButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        try {
          undoManager.redo();
        } catch (CannotRedoException cre) {
          cre.printStackTrace();
        }
        updateButtons();
      }
    });
    setSize(400, 300);
    setVisible(true);
  }
  public void updateButtons() {
    undoButton.setText(undoManager.getUndoPresentationName());
    redoButton.setText(undoManager.getRedoPresentationName());
    undoButton.setEnabled(undoManager.canUndo());
    redoButton.setEnabled(undoManager.canRedo());
  }
  public static void main(String argv[]) {
    new UndoRedoTextArea();
  }
}