code: /* LanguageTool, a natural language style checker 
   * Copyright (C) 2005 Daniel Naber (http://www.danielnaber.de)
   * 
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
   * USA
   */
  package org.languagetool.gui;
  
  import java.awt.Image;
  import java.awt.Toolkit;
  import java.awt.event.ActionEvent;
  import java.awt.event.KeyEvent;
  import java.util.ResourceBundle;
  import javax.swing.AbstractAction;
  import javax.swing.Action;
  import javax.swing.ImageIcon;
  import javax.swing.InputMap;
  import javax.swing.KeyStroke;
  import javax.swing.event.UndoableEditEvent;
  import javax.swing.event.UndoableEditListener;
  import javax.swing.text.JTextComponent;
  import javax.swing.undo.CannotRedoException;
  import javax.swing.undo.CannotUndoException;
  import javax.swing.undo.CompoundEdit;
  import javax.swing.undo.UndoManager;
  import org.languagetool.JLanguageTool;
  
  /**
   * Provides Undo/Redo support and actions for JTextComponent
   *
   * @author Panagiotis Minos
   */
  class UndoRedoSupport {
  
    final UndoAction undoAction;
    final RedoAction redoAction;
    
    private final UndoManager undoManager;
    private final ResourceBundle messages;
    
    private boolean compoundMode = false;
    private CompoundEdit ce = null;
  
    UndoRedoSupport(JTextComponent textComponent, ResourceBundle messages) {
      this.messages = messages;
      undoManager = new UndoManager();
      undoAction = new UndoAction();
      redoAction = new RedoAction();
      textComponent.getDocument().addUndoableEditListener(new UndoableEditListener() {
        @Override
        public void undoableEditHappened(UndoableEditEvent e) {
          if(compoundMode) {
            ce.addEdit(e.getEdit());
          } else {
            undoManager.addEdit(e.getEdit());
          }
          undoAction.updateUndoState();
          redoAction.updateRedoState();
        }
      });
      InputMap inputMap = textComponent.getInputMap();
      KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_Z,
              Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
      inputMap.put(key, "undo");
      textComponent.getActionMap().put("undo", undoAction);
      key = KeyStroke.getKeyStroke(KeyEvent.VK_Z,
              Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() | java.awt.event.InputEvent.SHIFT_DOWN_MASK);
      inputMap.put(key, "redo");
      textComponent.getActionMap().put("redo", redoAction);
    }
  
    /**
     * Notify manager to start merging undoable edits.
     * 
     * Calling startCompoundEdit when already in compound mode is an error
     * and will throw a RuntimeException.
     *
     * @since 2.7
     */
    void startCompoundEdit()
    {
        if(compoundMode) {
          throw new RuntimeException("already in compound mode");
        }
        ce = new CompoundEdit();
        compoundMode = true;
    }
  
    /**
     * Notify manager to stop merging undoable edits.
     * 
     * Calling endCompoundEdit when not in compound mode is an error
     * and will throw a RuntimeException.
     *
     * @since 2.7
     */  
    void endCompoundEdit()
    {
        if(!compoundMode) {
          throw new RuntimeException("not in compound mode");
        }      
        ce.end();
        undoManager.addEdit(ce);
        ce = null;
        compoundMode = false;
    }
  
    class UndoAction extends AbstractAction {
  
      private UndoAction() {
        super(messages.getString("guiUndo"));
        Image img;
        img = Toolkit.getDefaultToolkit().getImage(
                JLanguageTool.getDataBroker().getFromResourceDirAsUrl("sc_undo.png"));
        putValue(Action.SMALL_ICON, new ImageIcon(img));
        img = Toolkit.getDefaultToolkit().getImage(
                JLanguageTool.getDataBroker().getFromResourceDirAsUrl("lc_undo.png"));
        putValue(Action.LARGE_ICON_KEY, new ImageIcon(img));
        KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_Z,
                Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
        putValue(Action.ACCELERATOR_KEY, key);
        putValue(Action.MNEMONIC_KEY, KeyEvent.VK_U);
        setEnabled(false);
      }
  
      @Override
      public void actionPerformed(ActionEvent e) {
        try {
          undoManager.undo();
        } catch (CannotUndoException ex) {
          // ignore
        }
        updateUndoState();
        redoAction.updateRedoState();
      }
  
      private void updateUndoState() {
        if (undoManager.canUndo()) {
          setEnabled(true);
        } else {
          setEnabled(false);
        }
      }
    }
  
    class RedoAction extends AbstractAction {
  
      private RedoAction() {
        super(messages.getString("guiRedo"));
        Image img;
        img = Toolkit.getDefaultToolkit().getImage(
                JLanguageTool.getDataBroker().getFromResourceDirAsUrl("sc_redo.png"));
        putValue(Action.SMALL_ICON, new ImageIcon(img));
        img = Toolkit.getDefaultToolkit().getImage(
                JLanguageTool.getDataBroker().getFromResourceDirAsUrl("lc_redo.png"));
        putValue(Action.LARGE_ICON_KEY, new ImageIcon(img));
        KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_Z,
                Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() | java.awt.event.InputEvent.SHIFT_DOWN_MASK);
        putValue(Action.ACCELERATOR_KEY, key);
        putValue(Action.MNEMONIC_KEY, KeyEvent.VK_R);
        setEnabled(false);
      }
  
      @Override
      public void actionPerformed(ActionEvent e) {
        try {
          undoManager.redo();
        } catch (CannotRedoException ex) {
          // ignore
        }
        updateRedoState();
        undoAction.updateUndoState();
      }
  
      private void updateRedoState() {
        setEnabled(undoManager.canRedo());
      }
    }
  }
  