Home > Software engineering >  Creating an undoable JTextField
Creating an undoable JTextField

Time:07-04

I've implemented an undoable JTextField, the issue is when I create more than one, it is only undoing the last instance that is created. The code is below - to replicate, enter some text into the third field then enter some code into the first text field and type to ctrl-z to undo - it only removes text from the third text field. Can anyone see what I'm doing incorrectly?

import java.awt.BorderLayout;
import java.awt.Event;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
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;

public class UndoableTextFieldTest 
{
    public static void main(String... s)
    {
        new UndoableTextFieldTest();
    }

    public UndoableTextFieldTest() 
    {
        JFrame frame = new JFrame("UndoableTextFieldTest");

        UndoableTextField f1 = new UndoableTextField();
        UndoableTextField f2 = new UndoableTextField();
        UndoableTextField f3 = new UndoableTextField();

        frame.setLayout(new BorderLayout());
        frame.getContentPane().add(f1, BorderLayout.NORTH);
        frame.getContentPane().add(f2, BorderLayout.CENTER);
        frame.getContentPane().add(f3, BorderLayout.SOUTH);

        frame.setSize(360, 115);
        frame.setVisible(true);
    }


    public class UndoableTextField extends JTextField
    {    
        KeyStroke undoKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_Z, Event.CTRL_MASK);
        KeyStroke redoKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_Y, Event.CTRL_MASK);

        public UndoableTextField()
        {
            UndoManager undoManager = new UndoManager();

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

            getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(undoKeyStroke, "undoKeyStroke");

            getActionMap().put("undoKeyStroke", new AbstractAction() 
            {
                @Override
                public void actionPerformed(ActionEvent e) 
                {
                    try 
                    {
                        undoManager.undo();
                    } catch (CannotUndoException cue) {}
                }
            });

            getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(redoKeyStroke, "redoKeyStroke");
            getActionMap().put("redoKeyStroke", new AbstractAction() 
            {
                @Override
                public void actionPerformed(ActionEvent e) 
                {
                    try 
                    {
                        undoManager.redo();
                    } catch (CannotRedoException cre) {}
                }
            });
        }    
    }
}

CodePudding user response:

I'm not 100% sure, but my guess is that the input map you use is shared among all components in the window. The puts for the first two fields are overwritten by that of the third.

If I use getInputMap() instead then undoing seems to work per text field.

CodePudding user response:

Corrected code for posterity:

import java.awt.BorderLayout;
import java.awt.Event;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
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;

public class UndoableTextFieldTest 
{
    public static void main(String... s)
    {
        new UndoableTextFieldTest();
    }

    public UndoableTextFieldTest() 
{    
    KeyStroke undoKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_Z, Event.CTRL_MASK);
    KeyStroke redoKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_Y, Event.CTRL_MASK);
    
    public UndoableJTextField()
    {
        UndoManager undoManager = new UndoManager();
    
        getDocument().addUndoableEditListener(new UndoableEditListener() 
        {
            @Override
            public void undoableEditHappened(UndoableEditEvent e) 
            {
                undoManager.addEdit(e.getEdit());
            }
        });
                
        getInputMap().put(undoKeyStroke, "undoKeyStroke");
        getActionMap().put("undoKeyStroke", new AbstractAction() 
        {
            @Override
            public void actionPerformed(ActionEvent e) 
            {
                try { undoManager.undo(); } 
                catch (CannotUndoException cue) {}
            }
        });

        getInputMap().put(redoKeyStroke, "redoKeyStroke");
        getActionMap().put("redoKeyStroke", new AbstractAction() 
        {
            @Override
            public void actionPerformed(ActionEvent e) 
            {
                try { undoManager.redo(); } 
                catch (CannotRedoException cre) {}
            }
       });
    }    
}
  • Related