Java/Swing JFC/Undo Redo
Содержание
- 1 Adding Undo and Redo to a Text Component
- 2 Add Undo and Redo to a text component
- 3 Add undo support to the StyleFrame example
- 4 An example that shows lots of little UndoManager details
- 5 A sample app showing the use of UndoableToggleEdit and CompoundEdit
- 6 Bind the undo action to ctl-Z
- 7 Create an undo action and add it to the text component
- 8 Create a redo action and add it to the text component (JTextComponent)
- 9 Creating TextArea with Undo, Redo Capabilities
- 10 Listen for undo and redo events
- 11 Simple GUI demo of UndoManager and friends
- 12 The use of StateEdit(able)
- 13 The use of UndoableToggleEdit
- 14 The use of UndoManager
- 15 Undoable Drawing Panel 2
- 16 Undo Drawing
- 17 Undo Example 1
- 18 Undo Example 2
- 19 Undo Example 3
- 20 Undo Example 4
- 21 Undo Example 5
- 22 Undo Example 6
- 23 Undo Example 7
- 24 Undo manager
- 25 Undo redo textarea
Adding Undo and Redo to a Text Component
<source lang="java">
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");
}}
</source>
Add Undo and Redo to a text component
<source lang="java">
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"); }
}
</source>
Add undo support to the StyleFrame example
<source lang="java">
/* 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); }
}
</source>
An example that shows lots of little UndoManager details
<source lang="java">
/* 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(); }
}
</source>
A sample app showing the use of UndoableToggleEdit and CompoundEdit
<source lang="java">
/* 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); }
}
</source>
Bind the undo action to ctl-Z
<source lang="java">
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); }
}
</source>
Create an undo action and add it to the text component
<source lang="java">
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); }
}
</source>
Create a redo action and add it to the text component (JTextComponent)
<source lang="java">
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); }
}
</source>
Creating TextArea with Undo, Redo Capabilities
<source lang="java">
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(); }
}
</source>
Listen for undo and redo events
<source lang="java">
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);
}}
</source>
Simple GUI demo of UndoManager and friends
<source lang="java">
/*
* 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); }
}
</source>
The use of StateEdit(able)
<source lang="java">
/* 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); }
}
</source>
The use of UndoableToggleEdit
<source lang="java">
/* 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); }
}
</source>
The use of UndoManager
<source lang="java">
/* 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); }
}
</source>
Undoable Drawing Panel 2
<source lang="java">
/* 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()); } } }
}
</source>
Undo Drawing
<source lang="java">
/* 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); }
}
</source>
Undo Example 1
<source lang="java">
/* 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; }
}
</source>
Undo Example 2
<source lang="java">
/* 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; }
}
</source>
Undo Example 3
<source lang="java">
/* 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);
}
</source>
Undo Example 4
<source lang="java">
/* 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);
}
</source>
Undo Example 5
<source lang="java">
/* 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; }
}
</source>
Undo Example 6
<source lang="java">
/* 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 } }
}
</source>
Undo Example 7
<source lang="java">
/* 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()); } } }
}
</source>
Undo manager
<source lang="java">
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); } }
}
</source>
Undo redo textarea
<source lang="java">
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(); }
}
</source>