/** * Copyright (c) 2001-2017 Mathew A. Nelson and Robocode contributors * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://robocode.sourceforge.net/license/epl-v10.html */ package net.sf.robocode.ui.editor; import javax.swing.event.UndoableEditEvent; import javax.swing.event.DocumentEvent.EventType; import javax.swing.text.AbstractDocument.DefaultDocumentEvent; import javax.swing.text.BadLocationException; import javax.swing.undo.CannotUndoException; import javax.swing.undo.CompoundEdit; import javax.swing.undo.UndoableEdit; /** * Undo manager that compounds undo and redo edits. * * @author Flemming N. Larsen (original) */ @SuppressWarnings("serial") public class CompoundUndoManager extends UndoManagerWithActions { private CompoundEdit currentCompoundEdit; private EventType lastEventType; private boolean isCompoundMarkStart; public CompoundUndoManager() { super(); reset(); } @Override public void undoableEditHappened(UndoableEditEvent undoableEditEvent) { UndoableEdit edit = undoableEditEvent.getEdit(); // Make sure this event is a document event if (edit instanceof DefaultDocumentEvent) { // Get the event type DefaultDocumentEvent event = (DefaultDocumentEvent) edit; EventType eventType = event.getType(); // Check if the event type is not a change on character attributes, but instead an insertion or removal of // text. if (eventType != EventType.CHANGE) { boolean isEndCompoundEdit = false; // Check if current compound edit must be ended as it contains at least one new line if (eventType == EventType.INSERT) { try { // Check if the inserted text contains a new line character String insertedText = event.getDocument().getText(event.getOffset(), event.getLength()); isEndCompoundEdit = insertedText.contains("\n"); } catch (BadLocationException e) { e.printStackTrace(); } } // Make sure we are not in an explicit marked compound edit if (!isCompoundMarkStart) { // Check if current compound edit must be ended due to change between insertion or removal change isEndCompoundEdit |= (eventType != lastEventType); // Check if the current compound edit should be ended and a new one started if (isEndCompoundEdit) { endCurrentCompoundEdit(); } // Save the last event type lastEventType = eventType; } // Create new compound edit if the current one has been ended or does not exist if (currentCompoundEdit == null) { newCurrentCompoundEdit(); } } // Added event edit to the current compound edit if (currentCompoundEdit != null) { currentCompoundEdit.addEdit(edit); } } // Update the state of the actions updateUndoRedoState(); } @Override public void discardAllEdits() { super.discardAllEdits(); reset(); } /** * Ends the current compound edit, and mark the start for combining the next insertions or removals of text to be * put into the same compound edit so that these combined operations acts like as one single edit. * * @see #markCompoundEnd() */ public void markCompoundStart() { endCurrentCompoundEdit(); isCompoundMarkStart = true; } /** * Ends the current compound edit so that previous edits acts like a single edit. * * @see #markCompoundStart() */ public void markCompoundEnd() { endCurrentCompoundEdit(); isCompoundMarkStart = false; } private void reset() { currentCompoundEdit = null; lastEventType = EventType.INSERT; // important } private void endCurrentCompoundEdit() { if (currentCompoundEdit != null) { currentCompoundEdit.end(); currentCompoundEdit = null; } } private void newCurrentCompoundEdit() { // Set current compound edit to a new one currentCompoundEdit = new CompoundEdit() { // Make sure canUndo() and canRedo() works @Override public boolean isInProgress() { return false; } @Override public void undo() throws CannotUndoException { endCurrentCompoundEdit(); super.undo(); } }; // Add the current compound edit to the internal edits addEdit(currentCompoundEdit); } }