Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using undo and redo for JTextArea

Tags:

java

swing

I am making a text editor using Java swing. I am using JTextArea for the same. I want to know how I can use Undo and Redo functionality in JTextArea as I am not able to use it.

like image 399
Logan Avatar asked Mar 30 '10 17:03

Logan


4 Answers

I created a simple class that can assign undo functionality to a JTextcomponent (JTextField, JTextArea, etc.) with a single method call:

UndoTool.addUndoFunctionality(area);

or alternatively construct a new JTextArea with the undo functionality pre-assigned:

UndoTool.createJTextFieldWithUndo();

Here is the implementation of the utility class:

public class UndoTool {
    private static final String REDO_KEY = "redo";
    private static final String UNDO_KEY = "undo";

    private JTextComponent component;
    private KeyStroke undo = KeyStroke.getKeyStroke("control Z");
    private KeyStroke redo = KeyStroke.getKeyStroke("control Y");

    public UndoTool(JTextComponent component) {
        this.component = component;
    }

    public void setUndo(KeyStroke undo) {
        this.undo = undo;
    }

    public void setRedo(KeyStroke redo) {
        this.redo = redo;
    }

    public static void addUndoFunctionality(JTextComponent component) {
        UndoTool tool = new UndoTool(component);
        UndoManager undo = tool.createAndBindUndoManager();
        tool.bindUndo(undo);
        tool.bindRedo(undo);
    }

    public static JTextArea createJTextAreaWithUndo() {
        JTextArea area = new JTextArea();
        addUndoFunctionality(area);
        return area;
    }

    public static JTextField createJTextFieldWithUndo() {
        JTextField field = new JTextField();
        addUndoFunctionality(field);
        return field;
    }

    public UndoManager createAndBindUndoManager() {
        Check.notNull(component);

        UndoManager manager = new UndoManager();
        Document document = component.getDocument();
        document.addUndoableEditListener(event -> manager.addEdit(event.getEdit()));
        return manager;
    }

    public void bindRedo(UndoManager manager) {
        component.getActionMap().put(REDO_KEY, new AbstractAction(REDO_KEY) {
            @Override
            public void actionPerformed(ActionEvent evt) {
                try {
                    if (manager.canRedo()) {
                        manager.redo();
                    }
                } catch (CannotRedoException ignore) {
                }
            }
        });
        component.getInputMap().put(redo, REDO_KEY);
    }

    public void bindUndo(UndoManager manager) {
        component.getActionMap().put(UNDO_KEY, new AbstractAction(UNDO_KEY) {
            @Override
            public void actionPerformed(ActionEvent evt) {
                try {
                    if (manager.canUndo()) {
                        manager.undo();
                    }
                } catch (CannotUndoException ignore) {
                }
            }
        });
        component.getInputMap().put(undo, UNDO_KEY);
    }
}
like image 94
Erik Lievaart Avatar answered Nov 16 '22 02:11

Erik Lievaart


You can do like this

UndoManager manager = new UndoManager();
textArea.getDocument().addUndoableEditListener(manager);

Once the manager is attached to the document of the JTextArea, it will monitor all changes to the contents of the text area.

After attaching the manager to the text component, you must provide some means to tell the manager to undo/redo an operation.

Call the public void undo() and public void redo() method of the UndoManager where necessary(Eg. actionPerformed() method of an actionlistener)

You can attach Action objects to a button in the following way instead of calling undo() and redo() methods which simplifies the task:

JButton undoButton = new JButton(UndoManagerHelper.getUndoAction(manager));
JButton redoButton = new JButton(UndoManagerHelper.getRedoAction(manager));
like image 28
Riyafa Abdul Hameed Avatar answered Nov 16 '22 01:11

Riyafa Abdul Hameed


I had to go through multiple links just to get enough help. I'm adding here what I implemented successfully just to help future visitors. I implemented this using JTextPane but am assuming the same would apply for the JTextArea

    JTextArea textArea = new JTextArea();
    JButton undo = new JButton("Undo");
    JButton redo = new JButton("Redo");
    KeyStroke undoKeyStroke = KeyStroke.getKeyStroke(
            KeyEvent.VK_Z, Event.CTRL_MASK);
    KeyStroke redoKeyStroke = KeyStroke.getKeyStroke(
            KeyEvent.VK_Y, Event.CTRL_MASK);

    UndoManager undoManager = new UndoManager();

    Document document = textArea.getDocument();
    document.addUndoableEditListener(new UndoableEditListener() {
        @Override
        public void undoableEditHappened(UndoableEditEvent e) {
            undoManager.addEdit(e.getEdit());
        }
    });

    // Add ActionListeners
    undo.addActionListener((ActionEvent e) -> {
        try {
            undoManager.undo();
        } catch (CannotUndoException cue) {}
    });
    redo.addActionListener((ActionEvent e) -> {
        try {
            undoManager.redo();
        } catch (CannotRedoException cre) {}
    });

    // Map undo action
    textArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
            .put(undoKeyStroke, "undoKeyStroke");
    textArea.getActionMap().put("undoKeyStroke", new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent e) {
            try {
                undoManager.undo();
             } catch (CannotUndoException cue) {}
        }
    });
    // Map redo action
    textArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
            .put(redoKeyStroke, "redoKeyStroke");
    textArea.getActionMap().put("redoKeyStroke", new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent e) {
            try {
                undoManager.redo();
             } catch (CannotRedoException cre) {}
        }
    });
like image 33
giantas Avatar answered Nov 16 '22 01:11

giantas


As I understand it, JTextArea has no inherent Undo/Redo functionality built in, but a Google search did find this article which might be helpful.

There apparently exists an Undo Manager in javax.swing which you can hook up to the JTextArea's change events.

like image 25
Seb Charrot Avatar answered Nov 16 '22 03:11

Seb Charrot