How To Support Undo And Redo In Components Based On The Jtextcomponent Class

Introduction

We have to create a JFrame that illustrates how to support undo and redo in components based on the JtextComponent class. In this Frame, we are going to create three JButtons and one JtextArea that implements the undo and redo operations. The label for the JButtons are “Undo”, “Redo” and “Exit”. Let us see in this program how the undo and redo operations are supported in JFC.

Working

This program actually consists of three classes,

  • UndoDemo is a public class that will contain the main entry point of the program.
  • UndoDemoPanel is a class derived from Jpanel, contains the user interface for the application.
  • UndoableTextArea class is an extension of TextArea, which is an implementation of an undoable text area.

The main entry point creates a Jframe object and an instance of UndoDemo. The object of this class will be added to the frame. The constructor for UndoDemoPanel creates a toolbar with two buttons and an undoable text area (an instance of Undoable class). Then the listeners for the buttons call the undo and redo methods of the undoable text area object.

Note
Here the text variable representing the text area must be declared as final so that it can be accessed by anonymous inner classes that want to implement the button listeners.

The UndoableTextAarea class extends the JFC JTextArea class. In this class, the TextArea constructors are overridden with constructors that call a helper method, called initUndoable, to handle the initialization related to making the class undoable. Here the constructors will call the corresponding superclass constructor.

Source Code

UndoDemo.java

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.undo.*;
import java.util.Hashtable;
public class UndoDemo {
    public static void main(String args[]) {
        //create app panel
        UndoDemoPanel panel = new UndoDemoPanel();
        //Create a frame for app
        JFrame frame = new JFrame("Undo and Redo Demo");
        //Add a window listener for window close events
        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
        //Add app panel to content pane
        frame.getContentPane().add(panel);
        //Set initial frame size and make visible
        frame.setSize(300, 200);
        frame.setVisible(true);
    }
}

UndoDemoPanel.java

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.undo.*;
import java.util.Hashtable;
class UndoDemoPanel extends JPanel {
    public UndoDemoPanel() {
        setLayout(new BorderLayout());
        //create an undoable text area must be final to be accessed by //anonymous inner classes (listeners)
        final UndoableTextArea text = new UndoableTextArea("Type your text here to test undo and redo operations");
        //Create a toolbar for buttons
        JToolBar toolbar = new JToolBar();
        //Create undo and redo buttons and add listeners for buttons
        JButton Undo, Redo, Exit;
        Undo = new JButton("Undo");
        Undo.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                text.undo();
            }
        });
        Redo = new JButton("Redo");
        Redo.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                text.redo();
            }
        });
        Exit = new JButton("Exit");
        Exit.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                System.exit(0);
            }
        });
        //add buttons to toolbar
        toolbar.add(Undo);
        toolbar.add(Redo);
        toolbar.add(Exit);
        //add toolbar and text area to panel
        add(toolbar, "North");
        add(text, "Center");
    }
}

UndoableTextArea.java

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.undo.*;
import java.util.Hashtable;
class UndoableTextArea extends TextArea implements StateEditable {
    private final static String KEY_STATE = "UndoableTextAreaKey"; //hash key
    private boolean textChanged = false;
    private UndoManager undoManager;
    private StateEdit currentEdit;
    public UndoableTextArea() {
        super();
        initUndoable();
    }
    public UndoableTextArea(String string) {
        super(string);
        initUndoable();
    }
    public UndoableTextArea(int rows, int columns) {
        super(rows, columns);
        initUndoable();
    }
    public UndoableTextArea(String string, int rows, int columns) {
        super(string, rows, columns);
        initUndoable();
    }
    public UndoableTextArea(String string, int rows, int columns, int scrollbars) {
        super(string, rows, columns, scrollbars);
        initUndoable();
    }
    public boolean undo() {
        try {
            undoManager.undo();
            return true;
        } catch (CannotUndoException exc) {
            System.out.println("Can not undo");
            return false;
        }
    }
    public boolean redo() {
        try {
            undoManager.redo();
            return true;
        } catch (CannotRedoException ex) {
            System.out.println("Cannot redo");
            return false;
        }
    }
    public void storeState(Hashtable state) {
        state.put(KEY_STATE, getText());
    }
    public void restoreState(Hashtable state) {
        Object data = state.get(KEY_STATE);
        if (data != null) {
            setText((String) data);
        }
    }
    private void takeSnapshot() {
        if (textChanged) {
            currentEdit.end();
            undoManager.addEdit(currentEdit);
            textChanged = false;
            currentEdit = new StateEdit(this);
        }
    }
    private void initUndoable() {
        undoManager = new UndoManager();
        currentEdit = new StateEdit(this);
        addKeyListener(new KeyAdapter() {
            public void keyPressed(KeyEvent event) {
                if (event.isActionKey()) {
                    takeSnapshot(); //snapshot on any action keys
                }
            }
        });
        addFocusListener(new FocusAdapter() {
            public void focusLost(FocusEvent event) {
                takeSnapshot();
            }
        });
        addTextListener(new TextListener() {
            public void textValueChanged(TextEvent event) {
                textChanged = true;
                takeSnapshot();
            }
        });
    }
}

Output

Compile the above three classes separately, then run the class which has the main function.

How To Support Undo And Redo In Components Based On The Jtextcomponent Class

After Compilation, when we run the program as

E:\ashish bhatnagar\java>java UndoDemo

The following panel will be displayed,

How To Support Undo And Redo In Components Based On The Jtextcomponent Class

How To Support Undo And Redo In Components Based On The Jtextcomponent Class

Summary

JFrame is an extended version of java.awt.* that adds support for the JFC & Swing architecture. JFrame is the core class of javax.swing package and is used to develop Graphical User Interface, GUI in which various visual objects like Check box, Radio Button, Scroll Bar, Text Field, Push buttons, etc are embedded. This GUI is called Window pane.The UndoableTextAarea class extends the JFC JTextArea class. In this class, the TextArea constructors are overridden with constructors that call a helper method, called initUndoable, to handle the initialization related to making the class undoable.


Similar Articles