diff --git a/.gitattributes b/.gitattributes index e569c711c60..d47458dd902 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15414,6 +15414,7 @@ forge-gui/src/main/java/forge/gui/toolbox/FTabbedPane.java -text forge-gui/src/main/java/forge/gui/toolbox/FTextArea.java -text forge-gui/src/main/java/forge/gui/toolbox/FTextEditor.java -text forge-gui/src/main/java/forge/gui/toolbox/FTextField.java -text +forge-gui/src/main/java/forge/gui/toolbox/FUndoManager.java -text forge-gui/src/main/java/forge/gui/toolbox/LayoutHelper.java -text forge-gui/src/main/java/forge/gui/toolbox/SaveOpenDialog.java -text forge-gui/src/main/java/forge/gui/toolbox/imaging/FImagePanel.java -text diff --git a/forge-gui/src/main/java/forge/gui/toolbox/FTextEditor.java b/forge-gui/src/main/java/forge/gui/toolbox/FTextEditor.java index df04dd21226..870c66ad2b8 100644 --- a/forge-gui/src/main/java/forge/gui/toolbox/FTextEditor.java +++ b/forge-gui/src/main/java/forge/gui/toolbox/FTextEditor.java @@ -1,24 +1,17 @@ package forge.gui.toolbox; import java.awt.Insets; -import java.awt.event.ActionEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; -import javax.swing.AbstractAction; -import javax.swing.Action; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.event.DocumentListener; -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; @SuppressWarnings("serial") public class FTextEditor extends JScrollPane { private final JTextArea tarEditor; + private final FUndoManager undoManager; public FTextEditor() { tarEditor = new JTextArea(); @@ -28,8 +21,9 @@ public class FTextEditor extends JScrollPane { skin.setBackground(FSkin.getColor(FSkin.Colors.CLR_THEME2)); skin.setCaretColor(FSkin.getColor(FSkin.Colors.CLR_TEXT)); + undoManager = new FUndoManager(tarEditor); + tarEditor.setMargin(new Insets(3, 3, 3, 3)); - tarEditor.getDocument().addUndoableEditListener(new MyUndoableEditListener()); tarEditor.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { @@ -37,15 +31,15 @@ public class FTextEditor extends JScrollPane { switch (e.getKeyCode()) { case KeyEvent.VK_Z: if (e.isShiftDown()) { - redoAction.actionPerformed(null); + undoManager.redo(); } else { - undoAction.actionPerformed(null); + undoManager.undo(); } break; case KeyEvent.VK_Y: if (!e.isShiftDown()) { - redoAction.actionPerformed(null); + undoManager.redo(); } break; } @@ -89,73 +83,4 @@ public class FTextEditor extends JScrollPane { public void addDocumentListener(DocumentListener listener) { tarEditor.getDocument().addDocumentListener(listener); } - - //Undo/Redo - private UndoAction undoAction = new UndoAction(); - private RedoAction redoAction = new RedoAction(); - private UndoManager undoManager = new UndoManager(); - - private class MyUndoableEditListener implements UndoableEditListener { - public void undoableEditHappened(UndoableEditEvent e) { - undoManager.addEdit(e.getEdit()); - undoAction.updateUndoState(); - redoAction.updateRedoState(); - } - } - - private class UndoAction extends AbstractAction { - public UndoAction() { - super("Undo"); - setEnabled(false); - } - - public void actionPerformed(ActionEvent e) { - try { - undoManager.undo(); - } catch (CannotUndoException ex) { - System.out.println("Unable to undo: " + ex); - ex.printStackTrace(); - } - updateUndoState(); - redoAction.updateRedoState(); - } - - protected void updateUndoState() { - if (undoManager.canUndo()) { - setEnabled(true); - putValue(Action.NAME, undoManager.getUndoPresentationName()); - } else { - setEnabled(false); - putValue(Action.NAME, "Undo"); - } - } - } - - private class RedoAction extends AbstractAction { - public RedoAction() { - super("Redo"); - setEnabled(false); - } - - public void actionPerformed(ActionEvent e) { - try { - undoManager.redo(); - } catch (CannotRedoException ex) { - System.out.println("Unable to redo: " + ex); - ex.printStackTrace(); - } - updateRedoState(); - undoAction.updateUndoState(); - } - - protected void updateRedoState() { - if (undoManager.canRedo()) { - setEnabled(true); - putValue(Action.NAME, undoManager.getRedoPresentationName()); - } else { - setEnabled(false); - putValue(Action.NAME, "Redo"); - } - } - } } diff --git a/forge-gui/src/main/java/forge/gui/toolbox/FUndoManager.java b/forge-gui/src/main/java/forge/gui/toolbox/FUndoManager.java new file mode 100644 index 00000000000..238e78db639 --- /dev/null +++ b/forge-gui/src/main/java/forge/gui/toolbox/FUndoManager.java @@ -0,0 +1,250 @@ +package forge.gui.toolbox; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.KeyStroke; +import javax.swing.SwingUtilities; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.event.UndoableEditEvent; +import javax.swing.event.UndoableEditListener; +import javax.swing.text.AbstractDocument; +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 javax.swing.undo.UndoableEdit; + +/* +** This class will merge individual edits into a single larger edit. +** That is, characters entered sequentially will be grouped together and +** undone as a group. Any attribute changes will be considered as part +** of the group and will therefore be undone when the group is undone. +*/ +@SuppressWarnings("serial") +public class FUndoManager extends UndoManager + implements UndoableEditListener, DocumentListener +{ + private UndoManager undoManager; + private CompoundEdit compoundEdit; + private JTextComponent textComponent; + private UndoAction undoAction; + private RedoAction redoAction; + + // These fields are used to help determine whether the edit is an + // incremental edit. The offset and length should increase by 1 for + // each character added or decrease by 1 for each character removed. + + private int lastOffset; + private int lastLength; + + public FUndoManager(JTextComponent textComponent) { + this.textComponent = textComponent; + undoManager = this; + undoAction = new UndoAction(); + redoAction = new RedoAction(); + textComponent.getDocument().addUndoableEditListener(this); + } + + /* + ** Add a DocumentLister before the undo is done so we can position + ** the Caret correctly as each edit is undone. + */ + public void undo() { + if (canUndo()) { + textComponent.getDocument().addDocumentListener(this); + super.undo(); + textComponent.getDocument().removeDocumentListener(this); + } + } + + /* + ** Add a DocumentLister before the redo is done so we can position + ** the Caret correctly as each edit is redone. + */ + public void redo() { + if (canRedo()) { + textComponent.getDocument().addDocumentListener(this); + super.redo(); + textComponent.getDocument().removeDocumentListener(this); + } + } + + /* + ** Whenever an UndoableEdit happens the edit will either be absorbed + ** by the current compound edit or a new compound edit will be started + */ + public void undoableEditHappened(UndoableEditEvent e) { + // Start a new compound edit + if (compoundEdit == null) { + compoundEdit = startCompoundEdit(e.getEdit()); + return; + } + + int offsetChange = textComponent.getCaretPosition() - lastOffset; + int lengthChange = textComponent.getDocument().getLength() - lastLength; + + // Check for an attribute change + AbstractDocument.DefaultDocumentEvent event = (AbstractDocument.DefaultDocumentEvent)e.getEdit(); + + if (event.getType().equals(DocumentEvent.EventType.CHANGE)) { + if (offsetChange == 0) { + compoundEdit.addEdit(e.getEdit()); + return; + } + } + + // Check for an incremental edit or backspace. + // The Change in Caret position and Document length should both be + // either 1 or -1. + if (offsetChange == lengthChange && Math.abs(offsetChange) == 1) { + compoundEdit.addEdit(e.getEdit()); + lastOffset = textComponent.getCaretPosition(); + lastLength = textComponent.getDocument().getLength(); + return; + } + + // Not incremental edit, end previous edit and start a new one + compoundEdit.end(); + compoundEdit = startCompoundEdit(e.getEdit()); + } + + /* + ** Each CompoundEdit will store a group of related incremental edits + ** (ie. each character typed or backspaced is an incremental edit) + */ + private CompoundEdit startCompoundEdit(UndoableEdit anEdit) { + // Track Caret and Document information of this compound edit + lastOffset = textComponent.getCaretPosition(); + lastLength = textComponent.getDocument().getLength(); + + // The compound edit is used to store incremental edits + compoundEdit = new MyCompoundEdit(); + compoundEdit.addEdit(anEdit); + + // The compound edit is added to the UndoManager. All incremental + // edits stored in the compound edit will be undone/redone at once + addEdit(compoundEdit); + + undoAction.updateUndoState(); + redoAction.updateRedoState(); + + return compoundEdit; + } + + /* + * The Action to Undo changes to the Document. + * The state of the Action is managed by the CompoundUndoManager + */ + public Action getUndoAction() { + return undoAction; + } + + /* + * The Action to Redo changes to the Document. + * The state of the Action is managed by the CompoundUndoManager + */ + public Action getRedoAction() { + return redoAction; + } + + /* + * Updates to the Document as a result of Undo/Redo will cause the + * Caret to be repositioned + */ + public void insertUpdate(final DocumentEvent e) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + int offset = e.getOffset() + e.getLength(); + offset = Math.min(offset, textComponent.getDocument().getLength()); + textComponent.setCaretPosition(offset); + } + }); + } + + public void removeUpdate(DocumentEvent e) { + textComponent.setCaretPosition(e.getOffset()); + } + + public void changedUpdate(DocumentEvent e) {} + + private class MyCompoundEdit extends CompoundEdit { + public boolean isInProgress() { + // in order for the canUndo() and canRedo() methods to work + // assume that the compound edit is never in progress + return false; + } + + public void undo() throws CannotUndoException { + // End the edit so future edits don't get absorbed by this edit + if (compoundEdit != null) + compoundEdit.end(); + + super.undo(); + + // Always start a new compound edit after an undo + compoundEdit = null; + } + } + + /* + * Perform the Undo and update the state of the undo/redo Actions + */ + private class UndoAction extends AbstractAction { + public UndoAction() { + putValue(Action.NAME, "Undo"); + putValue(Action.SHORT_DESCRIPTION, getValue(Action.NAME)); + putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_U)); + putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_MASK)); + setEnabled(false); + } + + public void actionPerformed(ActionEvent e) { + try { + undoManager.undo(); + textComponent.requestFocusInWindow(); + } + catch (CannotUndoException ex) {} + + updateUndoState(); + redoAction.updateRedoState(); + } + + private void updateUndoState() { + setEnabled(undoManager.canUndo()); + } + } + + /* + * Perform the Redo and update the state of the undo/redo Actions + */ + private class RedoAction extends AbstractAction { + public RedoAction() { + putValue(Action.NAME, "Redo"); + putValue(Action.SHORT_DESCRIPTION, getValue(Action.NAME)); + putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_R)); + putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_MASK)); + setEnabled(false); + } + + public void actionPerformed(ActionEvent e) { + try { + undoManager.redo(); + textComponent.requestFocusInWindow(); + } + catch (CannotRedoException ex) {} + + updateRedoState(); + undoAction.updateUndoState(); + } + + protected void updateRedoState() { + setEnabled(undoManager.canRedo()); + } + } +} diff --git a/forge-gui/src/main/java/forge/gui/workshop/controllers/CCardScript.java b/forge-gui/src/main/java/forge/gui/workshop/controllers/CCardScript.java index 9a8c419d17e..39e5e5995e4 100644 --- a/forge-gui/src/main/java/forge/gui/workshop/controllers/CCardScript.java +++ b/forge-gui/src/main/java/forge/gui/workshop/controllers/CCardScript.java @@ -4,7 +4,6 @@ import java.io.File; import java.io.PrintWriter; import javax.swing.JOptionPane; -import javax.swing.JTextArea; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener;