001package jahuwaldt.swing.undo; 002 003import java.util.ArrayList; 004import javax.swing.event.UndoableEditEvent; 005import javax.swing.event.UndoableEditListener; 006import javax.swing.undo.UndoManager; 007import javax.swing.undo.UndoableEdit; 008import javax.swing.undo.UndoableEditSupport; 009 010/** 011 * An extension of UndoManager that provides two additional features: (1) The ability to 012 * add & remove listeners and (2) the ability to gain more extensive access to the 013 * edits being managed. See: O'Reilly's Java Swing (1st edition). 014 * 015 * <p> Modified by: Joseph A. Huwaldt, Date: February 22, 2025</p> 016 */ 017@SuppressWarnings("serial") 018public class ExtendedUndoManager extends UndoManager implements UndoableEditListener { 019 020 private final ExtendedUndoableEditSupport support = new ExtendedUndoableEditSupport(); 021 022 private Object source = this; // The source of the last edit 023 024 /** 025 * Returns the the next significant edit to be undone if undo is called. May return 026 * null. 027 */ 028 @Override 029 public UndoableEdit editToBeUndone() { 030 return super.editToBeUndone(); 031 } 032 033 /** 034 * Returns the the next significant edit to be redone if redo is called. May return 035 * null. 036 */ 037 @Override 038 public UndoableEdit editToBeRedone() { 039 return super.editToBeRedone(); 040 } 041 042 /** 043 * Return the complete list of edits in an array. 044 * 045 * @return The complete list of edits in an array. 046 */ 047 public synchronized UndoableEdit[] getEdits() { 048 UndoableEdit[] array = new UndoableEdit[edits.size()]; 049 edits.copyInto(array); 050 return array; 051 } 052 053 /** 054 * Return all currently significant undoable edits. The first edit is the next one to 055 * be undone. 056 * 057 * @return All currently significant undoable edits. 058 */ 059 public synchronized UndoableEdit[] getUndoableEdits() { 060 int size = edits.size(); 061 ArrayList<UndoableEdit> v = new ArrayList(size); 062 for (int i = size - 1; i >= 0; i--) { 063 UndoableEdit u = edits.elementAt(i); 064 if (u.canUndo() && u.isSignificant()) 065 v.add(u); 066 } 067 UndoableEdit[] array = new UndoableEdit[v.size()]; 068 v.toArray(array); 069 return array; 070 } 071 072 /** 073 * Return all currently significant redoable edits. The first edit is the next one to 074 * be redone. 075 * 076 * @return All currently significant redoable edits. 077 */ 078 public synchronized UndoableEdit[] getRedoableEdits() { 079 int size = edits.size(); 080 ArrayList<UndoableEdit> v = new ArrayList(size); 081 for (int i = 0; i < size; i++) { 082 UndoableEdit u = edits.elementAt(i); 083 if (u.canRedo() && u.isSignificant()) 084 v.add(u); 085 } 086 UndoableEdit[] array = new UndoableEdit[v.size()]; 087 v.toArray(array); 088 return array; 089 } 090 091 /** 092 * Add an edit and notify our listeners. 093 * 094 * @param anEdit An edit and notify our listeners. 095 */ 096 @Override 097 public synchronized boolean addEdit(UndoableEdit anEdit) { 098 boolean b = super.addEdit(anEdit); 099 if (b) 100 support.postEdit(anEdit); // If the edit was added, notify listeners. 101 return b; 102 } 103 104 /** 105 * When an edit is sent to us, call addEdit() to notify any of our listeners. 106 */ 107 @Override 108 public synchronized void undoableEditHappened(UndoableEditEvent ev) { 109 UndoableEdit ue = ev.getEdit(); 110 source = ev.getSource(); 111 addEdit(ue); 112 } 113 114 /** 115 * Add a listener to be notified each time an edit is added to this manager. This 116 * makes it easy to update undo/redo menus as edits are added. 117 * 118 * @param l The listener to be notified each time an edit is added to this manager. 119 */ 120 public synchronized void addUndoableEditListener(UndoableEditListener l) { 121 support.addUndoableEditListener(l); 122 } 123 124 /** 125 * Remove a listener from this manager. 126 * 127 * @param l The listener to be notified each time an edit is added to this manager. 128 */ 129 public synchronized void removeUndoableEditListener(UndoableEditListener l) { 130 support.removeUndoableEditListener(l); 131 } 132 133 /** 134 * A simple extension of UndoableEditSupport that lets us specify the event source 135 * each time we post an edit 136 */ 137 class ExtendedUndoableEditSupport extends UndoableEditSupport { 138 139 // Post an edit to added listeners. 140 @Override 141 public synchronized void postEdit(UndoableEdit ue) { 142 realSource = source; // From our enclosing manager object 143 super.postEdit(ue); 144 } 145 } 146}