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