package javaforce; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.text.*; import javax.swing.event.*; import javax.swing.plaf.*; import javax.swing.undo.*; /** * Extends JTextArea to provide some extra features such as overwrite mode, and * undo/redo */ public class JFTextArea extends JTextArea { private static Toolkit toolkit = Toolkit.getDefaultToolkit(); private static boolean isOvertypeMode; private Caret defaultCaret; private Caret overtypeCaret; private UndoManager undo; public void clearHistory() { undo.discardAllEdits(); } private void init() { //setup overwrite mode setCaretColor(Color.black); defaultCaret = getCaret(); overtypeCaret = new OvertypeCaret(); overtypeCaret.setBlinkRate(defaultCaret.getBlinkRate()); setOvertypeMode(false); //setup undo manager undo = new UndoManager(); // Listen for undo and redo events getDocument().addUndoableEditListener(new UndoableEditListener() { public void undoableEditHappened(UndoableEditEvent evt) { undo.addEdit(evt.getEdit()); } }); // Create an undo action and add it to the text component getActionMap().put("Undo", new AbstractAction("Undo") { public void actionPerformed(ActionEvent evt) { try { if (undo.canUndo()) { undo.undo(); } } catch (CannotUndoException e) { } } }); // Bind the undo action to ctl-Z getInputMap().put(KeyStroke.getKeyStroke("control Z"), "Undo"); // Create a redo action and add it to the text component getActionMap().put("Redo", new AbstractAction("Redo") { public void actionPerformed(ActionEvent evt) { try { if (undo.canRedo()) { undo.redo(); } } catch (CannotRedoException e) { } } }); // Bind the redo action to ctl-Y getInputMap().put(KeyStroke.getKeyStroke("control Y"), "Redo"); } public JFTextArea() { super(); init(); } public JFTextArea(int row, int column) { super(row, column); init(); } /* * Return the overtype/insert mode */ public boolean isOvertypeMode() { return isOvertypeMode; } /* * Set the caret to use depending on overtype/insert mode */ public void setOvertypeMode(boolean isOvertypeMode) { this.isOvertypeMode = isOvertypeMode; int pos = getCaretPosition(); if (isOvertypeMode()) { setCaret(overtypeCaret); } else { setCaret(defaultCaret); } setCaretPosition(pos); } /* * Override method from JComponent */ public void replaceSelection(String text) { // Implement overtype mode by selecting the character at the current // caret position if (isOvertypeMode()) { int pos = getCaretPosition(); if ((getSelectedText() == null) && (pos < getDocument().getLength())) { moveCaretPosition(pos + 1); } } super.replaceSelection(text); } /* * Override method from JComponent */ protected void processKeyEvent(KeyEvent e) { super.processKeyEvent(e); // Handle release of Insert key to toggle overtype/insert mode if (e.getID() == KeyEvent.KEY_RELEASED && e.getKeyCode() == KeyEvent.VK_INSERT) { setOvertypeMode(!isOvertypeMode()); } } /* * Paint a horizontal line the width of a column and 1 pixel high */ private class OvertypeCaret extends DefaultCaret { /* * The overtype caret will simply be a horizontal line one pixel high * (once we determine where to paint it) */ public void paint(Graphics g) { if (isVisible()) { try { JTextComponent component = getComponent(); TextUI mapper = component.getUI(); Rectangle r = mapper.modelToView(component, getDot()); g.setColor(component.getCaretColor()); int width = g.getFontMetrics().charWidth('w'); int y = r.y + r.height - 2; g.drawLine(r.x, y, r.x + width - 2, y); } catch (BadLocationException e) { } } } /* * Damage must be overridden whenever the paint method is overridden * (The damaged area is the area the caret is painted in. We must * consider the area for the default caret and this caret) */ protected synchronized void damage(Rectangle r) { if (r != null) { JTextComponent component = getComponent(); x = r.x; y = r.y; width = component.getFontMetrics(component.getFont()).charWidth('w'); height = r.height; repaint(); } } } }