Memento pattern undo/redo texteditor

326 Views Asked by At

I tried to apply memento pattern to include undo/redo functions in my tex editor app. Let's say it is simplified version :). So far, I haven't figured out how to save and then restore the exact text input from the keyboard. I mean, I have to link the state attribute with the textarea in my code, plus some other modifications. I tried to stuck the whole code in three classes, Main, Editor, Memento like below Any hints? Thank you

public class Main {
    public static void main(String[] args) {
        
        Editor viewEditor = new Editor();
        viewEditor.setVisible(true);

        //Storing changes in ArrayList
        List<Memento> mementoList = new ArrayList<Memento>();
        
        viewEditor.setState(" first and only state :)");
        mementoList.add(viewEditor.saveStatetoMemento());
    
        viewEditor.getStatefromMemento(mementoList.get(0));
       

    }

}

public class Editor extends JFrame {

    String state;

    public void setState(String state) {
        this.state = state;
    }
    public String getState(String state) {
        return state;
    }
    public Memento saveStatetoMemento() {
        System.out.println("Saving state to Memento in Editor.java ");
        return new Memento(state);
    }

    public void getStatefromMemento(Memento memento) {
        state = memento.getState();
        System.out.println("State restored from memento" + state);
    }
    //Using UndoManager for handling undo/redo/ operations 
    private UndoManager um = new UndoManager();

    public Editor() {
        initUI();
    }

    public final void initUI() {
        //Panel
        JPanel panel = new JPanel();
        //Text Field
        final JTextArea textArea = new JTextArea("");
        textArea.getDocument().addUndoableEditListener(new UndoableEditListener() {
            public void undoableEditHappened(UndoableEditEvent e) {
                um.addEdit(e.getEdit());
            
         state = textArea.getText();
            }
        });
        textArea.setPreferredSize(new Dimension(550, 600));
        textArea.setLineWrap(true);
        textArea.setFont(new Font("Arial", Font.PLAIN, 20));
        textArea.setEditable(true);

        // Addind text field to panel
        panel.add(textArea);

        // Adding panel to JFrame
        add(panel);
        pack();

        // Menubar
        JMenuBar menubar = new JMenuBar();
        // Menu Brudnopis
        JMenu brudnopis = new JMenu("Brudnopis");
        brudnopis.setMnemonic(KeyEvent.VK_B);
        // Menu Items: Zakoncz
        JMenuItem eMenuItemZakoncz = new JMenuItem("Zakoncz");
        eMenuItemZakoncz.setMnemonic(KeyEvent.VK_K);
        eMenuItemZakoncz.setToolTipText("Zakoncz program");
        // Adding action for the item: "Zakoncz"
        eMenuItemZakoncz.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                System.exit(0);
            }
        });

        //Menu Edit Item
        JMenu edit = new JMenu("Edycja");
        edit.setMnemonic(KeyEvent.VK_H);
        //Menu items: undo and redo
        JMenuItem undo = new JMenuItem("Undo");
        undo.setMnemonic(KeyEvent.VK_Z);
        undo.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, ActionEvent.CTRL_MASK));
        //undo.setAction(a);
        undo.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                if (um.canUndo()) {
                    um.undo();
                }
            }
        });

        JMenuItem redo = new JMenuItem("Redo");
        redo.setMnemonic(KeyEvent.VK_Y);
        redo.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Y, ActionEvent.CTRL_MASK));
        redo.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                if (um.canRedo()) {
                    um.redo();
                }
            }
        });

        //Adding 'brudnopis' to menubar
        menubar.add(brudnopis);
        menubar.add(edit);
        setJMenuBar(menubar);

        //Dodanie opcji do paska menu
        brudnopis.add(eMenuItemZakoncz);
        edit.add(undo);
        edit.add(redo);

        setTitle("Brudnopis");
        setSize(600, 700);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }
}




public class Memento {

private final String state;

public Memento(String state) {
this.state = state;
    }

public String getState() {
    return state;
}

 }

1

There are 1 best solutions below

0
Peter Dongan On

You need to attach an event handler to when the TextArea's state changes. Currently you only store its state when the program starts. The exact way you do this is a design decision to make - if you experiment with existing text editors you will see that they generally do something different than store state on every keystroke.

You have defined an ArrayList to store states. A stack is a more suitable structure to use for the Memento pattern. If you are supporting Redo, then you need a Redo stack as well.

I don't know if you are attempting this as an academic exercise or for a product. If it is the latter, then it would seem more efficient to just find an existing Java text control that already has a mature undo/redo feature.