/*
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;
}
}