Search code examples
javajtextareaundo-redo

Undoable Text Area in Java


I'm just approaching the programming in java.... I'd like to create a small Text Editor (Windows Notepad clone...).

I'm searching for a Class that extend the JTextArea implementing the undo & redo actions. I found a code that match my needs, and I tried to adapt it to my purposes.

Here's the code:

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.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 static final long serialVersionUID = 1L;
    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(50);
    }

    private void removeUndoMananger() {
        m_undoManager.end();
    }

    public void focusGained(FocusEvent fe) {
        createUndoMananger();
    }

    public void focusLost(FocusEvent fe) {
        removeUndoMananger();
    }

    public void undo()
    {
          try {
                m_undoManager.undo();
              } catch (CannotUndoException cue) {
                Toolkit.getDefaultToolkit().beep();
              }
    }

    public void redo()
    {
          try {
                m_undoManager.redo();
              } catch (CannotRedoException cue) {
                Toolkit.getDefaultToolkit().beep();
              }
    }

    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) {
    }
}

Using Accellerators Key CTRL+Z,CTRL+Y while typing in the object, there's no problem.... but methods undo() and redo() added by me for use this actions from menuItem objetcs, don't work...

(...)       
menuItem15 = new JMenuItem("Undo");
menuItem16 = new JMenuItem("Redo");
(...)



public void actionPerformed(ActionEvent e)
{

//Undo
if (e.getSource() == menuItem15)
{
    textArea1.undo();
}


//Redo
if (e.getSource() == menuItem16)
{
    textArea1.redo();
}
}

Any suggestions? Sorry for my bad english...

Thanks!


Solution

  • The problem is likely due to the code adding and removing the UndoManager upon focusGained and focusLost events:

    public void focusGained(FocusEvent fe) {
        createUndoMananger();
    }
    
    public void focusLost(FocusEvent fe) {
        removeUndoMananger();
    }
    

    When you click on the menu, the text area temporarily loses the focus, whereupon the UndoManager is discarded and then a new instance is created. The new instance doesn't know about previous undoable edit events, and thus undo and redo do not work.

    I suggest creating the UndoManager once and keeping it attached to the document until the GUI is disposed (i.e. until the dialog / window is closed).