| /* |
| * Copyright 2003-2007 the original author or authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package groovy.ui.text; |
| |
| import java.beans.PropertyChangeListener; |
| |
| import javax.swing.event.SwingPropertyChangeSupport; |
| import javax.swing.event.UndoableEditEvent; |
| import javax.swing.undo.CompoundEdit; |
| import javax.swing.undo.UndoManager; |
| import javax.swing.undo.UndoableEdit; |
| |
| /** |
| * To use this, simply drop this as an UndoableEditListener into your document, |
| * and then create actions to call undo/redo as needed (checking can undo/redo |
| * first, of course). |
| * |
| * @author Evan "Hippy" Slatis |
| */ |
| public class TextUndoManager extends UndoManager { |
| |
| private SwingPropertyChangeSupport propChangeSupport = |
| new SwingPropertyChangeSupport(this); |
| |
| private StructuredEdit compoundEdit = new StructuredEdit(); |
| |
| private long firstModified; |
| |
| private UndoableEdit modificationMarker = editToBeUndone(); |
| |
| /** |
| * Creates a new instance of TextUndoManager. |
| */ |
| public TextUndoManager() { |
| } |
| |
| public void addPropertyChangeListener(PropertyChangeListener pcl) { |
| propChangeSupport.addPropertyChangeListener(pcl); |
| } |
| |
| public void die() { |
| boolean undoable = canUndo(); |
| super.die(); |
| firePropertyChangeEvent(UndoManager.UndoName, undoable, canUndo()); |
| } |
| |
| public void discardAllEdits() { |
| boolean undoable = canUndo(); |
| boolean redoable = canRedo(); |
| |
| boolean changed = hasChanged(); |
| super.discardAllEdits(); |
| modificationMarker = editToBeUndone(); |
| |
| firePropertyChangeEvent(UndoManager.UndoName, undoable, canUndo()); |
| firePropertyChangeEvent(UndoManager.UndoName, redoable, canRedo()); |
| } |
| |
| protected void firePropertyChangeEvent(String name, |
| boolean oldValue, |
| boolean newValue) { |
| propChangeSupport.firePropertyChange(name, oldValue, newValue); |
| } |
| |
| public boolean hasChanged() { |
| return modificationMarker != editToBeUndone(); |
| } |
| |
| public void redo() throws javax.swing.undo.CannotRedoException { |
| compoundEdit.end(); |
| |
| if (firstModified == 0) { |
| firstModified = ((StructuredEdit)editToBeRedone()).editedTime(); |
| } |
| |
| boolean undoable = canUndo(); |
| |
| boolean changed = hasChanged(); |
| super.redo(); |
| |
| firePropertyChangeEvent(UndoManager.UndoName, undoable, canUndo()); |
| } |
| |
| protected void redoTo(UndoableEdit edit) { |
| compoundEdit.end(); |
| |
| if (firstModified == 0) { |
| firstModified = ((StructuredEdit)editToBeRedone()).editedTime(); |
| } |
| |
| boolean undoable = canUndo(); |
| |
| boolean changed = hasChanged(); |
| super.redoTo(edit); |
| |
| firePropertyChangeEvent(UndoManager.UndoName, undoable, canUndo()); |
| |
| } |
| |
| public void removePropertyChangeListener(PropertyChangeListener pcl) { |
| propChangeSupport.removePropertyChangeListener(pcl); |
| } |
| |
| public void reset() { |
| boolean changed = modificationMarker != editToBeUndone(); |
| if (changed) { |
| modificationMarker = editToBeUndone(); |
| } |
| } |
| |
| protected void trimEdits(int from, int to) { |
| boolean undoable = canUndo(); |
| boolean redoable = canRedo(); |
| |
| boolean changed = hasChanged(); |
| super.trimEdits(from, to); |
| |
| firePropertyChangeEvent(UndoManager.UndoName, undoable, canUndo()); |
| firePropertyChangeEvent(UndoManager.RedoName, redoable, canRedo()); |
| } |
| |
| public void undo() throws javax.swing.undo.CannotUndoException { |
| compoundEdit.end(); |
| |
| UndoableEdit edit = editToBeUndone(); |
| if (((StructuredEdit)editToBeUndone()).editedTime() == |
| firstModified) { |
| firstModified = 0; |
| } |
| else if (firstModified == 0) { |
| firstModified = ((StructuredEdit)editToBeUndone()).editedTime(); |
| } |
| |
| boolean redoable = canRedo(); |
| boolean changed = hasChanged(); |
| super.undo(); |
| firePropertyChangeEvent(UndoManager.RedoName, redoable, canRedo()); |
| } |
| |
| public void undoableEditHappened(UndoableEditEvent uee) { |
| UndoableEdit edit = uee.getEdit(); |
| boolean undoable = canUndo(); |
| |
| long editTime = System.currentTimeMillis(); |
| |
| if (firstModified == 0 || |
| editTime - compoundEdit.editedTime() > 700) { |
| compoundEdit.end(); |
| compoundEdit = new StructuredEdit(); |
| } |
| compoundEdit.addEdit(edit); |
| |
| firstModified = firstModified == 0 ? |
| compoundEdit.editedTime() : firstModified; |
| |
| if (lastEdit() != compoundEdit) { |
| boolean changed = hasChanged(); |
| addEdit(compoundEdit); |
| firePropertyChangeEvent(UndoManager.UndoName, undoable, canUndo()); |
| } |
| |
| } |
| |
| private class StructuredEdit extends CompoundEdit { |
| |
| private long editedTime; |
| |
| public boolean addEdit(UndoableEdit edit) { |
| boolean result = super.addEdit(edit); |
| if (result && editedTime == 0) { |
| editedTime = System.currentTimeMillis(); |
| } |
| return result; |
| } |
| |
| public boolean canUndo() { |
| return edits.size() > 0; |
| } |
| |
| protected long editedTime() { |
| return editedTime; |
| } |
| |
| public boolean isInProgress() { |
| return false; |
| } |
| } |
| } |