fix: undo manager works properly
This commit is contained in:
parent
648941706d
commit
71e1b7deff
|
@ -1,6 +1,8 @@
|
|||
package se.lantz;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.GraphicsDevice;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
|
@ -32,8 +34,13 @@ public class PCUGameManager
|
|||
SwingUtilities.invokeLater(() -> {
|
||||
|
||||
MainWindow mainWindow = MainWindow.getInstance();
|
||||
mainWindow.setSize(1400, 850);
|
||||
mainWindow.setMinimumSize(new Dimension(1300, 700));
|
||||
|
||||
GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
|
||||
int width = gd.getDisplayMode().getWidth();
|
||||
int height = gd.getDisplayMode().getHeight();
|
||||
|
||||
mainWindow.setSize(Math.min(width, 1500), Math.min(height-40, 825));
|
||||
mainWindow.setMinimumSize(new Dimension(Math.min(width, 1300), Math.min(height-40, 700)));
|
||||
mainWindow.setVisible(true);
|
||||
mainWindow.setLocationRelativeTo(null);
|
||||
mainWindow.initialize();
|
||||
|
|
|
@ -19,6 +19,7 @@ import javax.swing.event.DocumentListener;
|
|||
import javax.swing.text.DefaultStyledDocument;
|
||||
|
||||
import se.lantz.model.InfoModel;
|
||||
import se.lantz.util.CustomUndoPlainDocument;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.GridBagConstraints;
|
||||
|
@ -73,7 +74,7 @@ public class DescriptionPanel extends JPanel
|
|||
descriptionTextArea.setWrapStyleWord(true);
|
||||
descriptionTextArea.setLineWrap(true);
|
||||
|
||||
DefaultStyledDocument doc = new DefaultStyledDocument();
|
||||
CustomUndoPlainDocument doc = new CustomUndoPlainDocument();
|
||||
doc.addDocumentListener(new DocumentListener()
|
||||
{
|
||||
@Override
|
||||
|
|
|
@ -147,7 +147,7 @@ public class GameDetailsBackgroundPanel extends JPanel
|
|||
{
|
||||
GridBagLayout gbl_settingsPanel = new GridBagLayout();
|
||||
settingsPanel = new ScrollablePanel(gbl_settingsPanel);
|
||||
settingsPanel.setPreferredSize(new Dimension(1120,425));
|
||||
settingsPanel.setPreferredSize(new Dimension(1120,400));
|
||||
settingsPanel.setScrollableHeight(ScrollableSizeHint.STRETCH);
|
||||
settingsPanel.setScrollableWidth(ScrollableSizeHint.STRETCH);
|
||||
GridBagConstraints gbc_systemPanel = new GridBagConstraints();
|
||||
|
|
|
@ -26,6 +26,7 @@ import javax.swing.SwingUtilities;
|
|||
import javax.swing.text.DefaultEditorKit;
|
||||
|
||||
import se.lantz.model.InfoModel;
|
||||
import se.lantz.util.CustomUndoPlainDocument;
|
||||
import se.lantz.util.TextComponentSupport;
|
||||
|
||||
public class InfoPanel extends JPanel
|
||||
|
@ -210,6 +211,7 @@ public class InfoPanel extends JPanel
|
|||
if (titleField == null)
|
||||
{
|
||||
titleField = new JTextField();
|
||||
titleField.setDocument(new CustomUndoPlainDocument());
|
||||
titleField.addKeyListener(new KeyAdapter()
|
||||
{
|
||||
@Override
|
||||
|
@ -273,6 +275,7 @@ public class InfoPanel extends JPanel
|
|||
if (authorField == null)
|
||||
{
|
||||
authorField = new JTextField();
|
||||
authorField.setDocument(new CustomUndoPlainDocument());
|
||||
authorField.addKeyListener(new KeyAdapter()
|
||||
{
|
||||
@Override
|
||||
|
@ -291,6 +294,7 @@ public class InfoPanel extends JPanel
|
|||
if (composerField == null)
|
||||
{
|
||||
composerField = new JTextField();
|
||||
composerField.setDocument(new CustomUndoPlainDocument());
|
||||
composerField.addKeyListener(new KeyAdapter()
|
||||
{
|
||||
@Override
|
||||
|
|
|
@ -19,6 +19,7 @@ import javax.swing.SwingConstants;
|
|||
import javax.swing.border.TitledBorder;
|
||||
|
||||
import se.lantz.model.JoystickModel;
|
||||
import se.lantz.util.CustomUndoPlainDocument;
|
||||
import se.lantz.util.TextComponentSupport;
|
||||
|
||||
public class JoystickPanel extends JPanel
|
||||
|
@ -256,6 +257,7 @@ public class JoystickPanel extends JPanel
|
|||
if (configTextField == null)
|
||||
{
|
||||
configTextField = new JTextField();
|
||||
configTextField.setDocument(new CustomUndoPlainDocument());
|
||||
configTextField.setColumns(10);
|
||||
configTextField.addFocusListener(new FocusListener()
|
||||
{
|
||||
|
|
|
@ -0,0 +1,292 @@
|
|||
package se.lantz.util;
|
||||
|
||||
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.
|
||||
*/
|
||||
public class CompoundUndoManager 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 CompoundUndoManager(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()
|
||||
{
|
||||
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()
|
||||
{
|
||||
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.
|
||||
|
||||
// int offsetChange = textComponent.getCaretPosition() - lastOffset;
|
||||
// int lengthChange = textComponent.getDocument().getLength() - lastLength;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
//
|
||||
// Implement DocumentListener
|
||||
//
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
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("control Z"));
|
||||
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
|
||||
*/
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package se.lantz.util;
|
||||
|
||||
import javax.swing.event.UndoableEditEvent;
|
||||
import javax.swing.text.AttributeSet;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.PlainDocument;
|
||||
import javax.swing.undo.CompoundEdit;
|
||||
|
||||
/**
|
||||
* Document supporting undo/redo better than default
|
||||
*
|
||||
*/
|
||||
public class CustomUndoPlainDocument extends PlainDocument
|
||||
{
|
||||
private CompoundEdit compoundEdit;
|
||||
|
||||
@Override
|
||||
protected void fireUndoableEditUpdate(UndoableEditEvent e)
|
||||
{
|
||||
if (compoundEdit == null)
|
||||
{
|
||||
super.fireUndoableEditUpdate(e);
|
||||
} else
|
||||
{
|
||||
compoundEdit.addEdit(e.getEdit());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replace(int offset, int length, String text, AttributeSet attrs) throws BadLocationException
|
||||
{
|
||||
if (length == 0)
|
||||
{
|
||||
super.replace(offset, length, text, attrs);
|
||||
} else
|
||||
{
|
||||
compoundEdit = new CompoundEdit();
|
||||
super.fireUndoableEditUpdate(new UndoableEditEvent(this, compoundEdit));
|
||||
super.replace(offset, length, text, attrs);
|
||||
compoundEdit.end();
|
||||
compoundEdit = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,8 @@ package se.lantz.util;
|
|||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -10,9 +12,12 @@ import javax.swing.AbstractAction;
|
|||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.UndoableEditEvent;
|
||||
import javax.swing.event.UndoableEditListener;
|
||||
import javax.swing.text.AbstractDocument.DefaultDocumentEvent;
|
||||
import javax.swing.text.DefaultEditorKit;
|
||||
import javax.swing.text.Document;
|
||||
import javax.swing.text.JTextComponent;
|
||||
import javax.swing.text.TextAction;
|
||||
import javax.swing.undo.CannotRedoException;
|
||||
|
@ -24,7 +29,7 @@ public class TextComponentSupport
|
|||
{
|
||||
public final static String UNDO_ACTION = "Undo";
|
||||
public final static String REDO_ACTION = "Redo";
|
||||
|
||||
|
||||
private static List<UndoManager> managerList = new ArrayList<>();
|
||||
|
||||
private TextComponentSupport()
|
||||
|
@ -51,17 +56,17 @@ public class TextComponentSupport
|
|||
addAction(popupMenu, new DefaultEditorKit.CutAction(), KeyEvent.VK_X, "Cut");
|
||||
addAction(popupMenu, new DefaultEditorKit.CopyAction(), KeyEvent.VK_C, "Copy");
|
||||
addAction(popupMenu, new DefaultEditorKit.PasteAction(), KeyEvent.VK_V, "Paste");
|
||||
|
||||
|
||||
tc.setComponentPopupMenu(popupMenu);
|
||||
makeUndoable(popupMenu, tc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void clearUndoManagers()
|
||||
{
|
||||
for (UndoManager undoManager : managerList)
|
||||
{
|
||||
undoManager.discardAllEdits();
|
||||
undoManager.discardAllEdits();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,17 +76,26 @@ public class TextComponentSupport
|
|||
managerList.add(undoMgr);
|
||||
|
||||
// Add listener for undoable events
|
||||
pTextComponent.getDocument().addUndoableEditListener(undoMgr);
|
||||
// pTextComponent.getDocument().addUndoableEditListener(new UndoableEditListener()
|
||||
// {
|
||||
// public void undoableEditHappened(UndoableEditEvent evt)
|
||||
// {
|
||||
// if (evt.getEdit().isSignificant())
|
||||
// pTextComponent.getDocument().addUndoableEditListener(undoMgr);
|
||||
pTextComponent.getDocument().addUndoableEditListener(new UndoableEditListener()
|
||||
{
|
||||
public void undoableEditHappened(UndoableEditEvent evt)
|
||||
{
|
||||
// DefaultDocumentEvent event = (DefaultDocumentEvent) evt.getEdit();
|
||||
//
|
||||
// System.out.println(
|
||||
// "EVENT TYPE=" + event.getType() + ", LENGTH=" + event.getLength() + ", OFFSET=" + event.getOffset());
|
||||
//
|
||||
// if (event.getType().equals(DocumentEvent.EventType.REMOVE) && event.getLength() > 0 && event.getOffset() == 0)
|
||||
// {
|
||||
// undoMgr.addEdit(evt.getEdit());
|
||||
// //undoMgr.discardAllEdits();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
undoMgr.addEdit(evt.getEdit());
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
}
|
||||
});
|
||||
|
||||
// Add undo/redo actions
|
||||
AbstractAction undoAction = new AbstractAction(UNDO_ACTION)
|
||||
|
@ -93,7 +107,7 @@ public class TextComponentSupport
|
|||
if (undoMgr.canUndo())
|
||||
{
|
||||
undoMgr.undo();
|
||||
//this.setEnabled(undoMgr.canUndo());
|
||||
// this.setEnabled(undoMgr.canUndo());
|
||||
}
|
||||
} catch (CannotUndoException e)
|
||||
{
|
||||
|
@ -102,7 +116,8 @@ public class TextComponentSupport
|
|||
}
|
||||
};
|
||||
pTextComponent.getActionMap().put(UNDO_ACTION, undoAction);
|
||||
undoAction.putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_DOWN_MASK));
|
||||
undoAction.putValue(AbstractAction.ACCELERATOR_KEY,
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_DOWN_MASK));
|
||||
AbstractAction redoAction = new AbstractAction(REDO_ACTION)
|
||||
{
|
||||
public void actionPerformed(ActionEvent evt)
|
||||
|
@ -112,7 +127,7 @@ public class TextComponentSupport
|
|||
if (undoMgr.canRedo())
|
||||
{
|
||||
undoMgr.redo();
|
||||
//this.setEnabled(undoMgr.canRedo());
|
||||
// this.setEnabled(undoMgr.canRedo());
|
||||
}
|
||||
} catch (CannotRedoException e)
|
||||
{
|
||||
|
@ -121,7 +136,8 @@ public class TextComponentSupport
|
|||
}
|
||||
};
|
||||
pTextComponent.getActionMap().put(REDO_ACTION, redoAction);
|
||||
redoAction.putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_DOWN_MASK));
|
||||
redoAction.putValue(AbstractAction.ACCELERATOR_KEY,
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_DOWN_MASK));
|
||||
// Create keyboard accelerators for undo/redo actions (Ctrl+Z/Ctrl+Y)
|
||||
pTextComponent.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_DOWN_MASK), UNDO_ACTION);
|
||||
pTextComponent.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_DOWN_MASK), REDO_ACTION);
|
||||
|
|
Loading…
Reference in New Issue