1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
|
/* Glazed Lists (c) 2003-2006 */
/* http://publicobject.com/glazedlists/ publicobject.com,*/
/* O'Dell Engineering Ltd.*/
package ca.odell.glazedlists.swing;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.FunctionList;
import ca.odell.glazedlists.UndoRedoSupport;
import javax.swing.undo.*;
import javax.swing.*;
/**
* This class adapts the generic {@link UndoRedoSupport} provided by Glazed
* Lists for specific use with Swing's native {@link UndoManager}. Each
* {@link UndoRedoSupport.Edit} produced by Glazed List's
* {@link UndoRedoSupport} is adapted to Swing's {@link UndoableEdit} interface
* and then {@link UndoManager#addEdit added} into the given UndoManager.
*
* <p>Fine grain control of the {@link UndoableEdit} that is ultimately added
* to the {@link UndoableEdit} can be achieved by using
* {@link #install(UndoManager, EventList, FunctionList.Function) this} install
* method and specifying a custom Function.
*
* @author James Lemieux
*/
public final class UndoSupport<E> {
/** the manager of all undoable edits for the entire Swing application */
private UndoManager undoManager;
/** glazed List's undo/redo support which is being adapted to Swing's native undo/redo framework */
private UndoRedoSupport undoRedoSupport;
/** a listener that transforms GL-style edits into Swing-style edits using the {@link #editAdapter} and adds them to the {@link #undoManager} */
private UndoRedoSupport.Listener undoSupportHandler = new UndoSupportHandler();
/** the function which transforms GL-style edits into Swing-style edits */
private FunctionList.Function<UndoRedoSupport.Edit, UndoableEdit> editAdapter;
/**
* The private constructor creates an UndoSupport that provides undo/redo
* capabilties for changes to the given <code>source</code>. Whenever
* changes occur to the <code>source</code>, a GL-style
* {@link UndoRedoSupport.Edit} is produced, which is then transformed into
* a Swing-style {@link UndoableEdit} and ultimately added to the given
* <code>undoManager</code>.
*
* @param undoManager the manager of all undoable edits for the entire Swing application
* @param source the EventList to watch for undoable edits
* @param editAdapter the function that converts GL-style edits into Swing-style edits
*/
private UndoSupport(UndoManager undoManager, EventList<E> source, FunctionList.Function<UndoRedoSupport.Edit, UndoableEdit> editAdapter) {
this.undoManager = undoManager;
this.undoRedoSupport = UndoRedoSupport.install(source);
this.editAdapter = editAdapter;
// begin watching the GL undoRedoSupport for undoable edits
this.undoRedoSupport.addUndoSupportListener(undoSupportHandler);
}
/**
* Installs support for undoing/redoing edits on the given
* <code>source</code>. Specifically, {@link UndoableEdit}s are added to the
* <code>undoManager</code> each time the <code>source</code> changes.
* {@link UndoableEdit#undo Undoing} and {@link UndoableEdit#redo redoing}
* these edits will unapply/reapply the corresponding changes to the
* <code>source</code>.
*
* <p>This method uses a default strategy for mapping the GL-style edits
* to {@link UndoableEdit}s.
*
* @param undoManager the manager of all undoable edits for the entire Swing application
* @param source the EventList to watch for undoable edits
* @return an instance of the support class providing undo/redo edit features
*
* @throws IllegalStateException if this method is called from any Thread
* other than the Swing Event Dispatch Thread
*/
public static <E> UndoSupport install(UndoManager undoManager, EventList<E> source) {
return install(undoManager, source, new DefaultEditAdapter());
}
/**
* Installs support for undoing/redoing edits on the given
* <code>source</code>. Specifically, {@link UndoableEdit}s are added to the
* <code>undoManager</code> each time the <code>source</code> changes.
* {@link UndoableEdit#undo Undoing} and {@link UndoableEdit#redo redoing}
* these edits will unapply/reapply the corresponding changes to the
* <code>source</code>.
*
* <p>This method uses the given <code>editAdapter</code> for mapping the
* GL-style edits to {@link UndoableEdit}s.
*
* @param undoManager the manager of all undoable edits for the entire Swing application
* @param source the EventList to watch for undoable edits
* @param editAdapter the function that converts GL-style edits into Swing-style edits
* @return an instance of the support class providing undo/redo edit features
*
* @throws IllegalStateException if this method is called from any Thread
* other than the Swing Event Dispatch Thread
*/
public static <E> UndoSupport install(UndoManager undoManager, EventList<E> source, FunctionList.Function<UndoRedoSupport.Edit, UndoableEdit> editAdapter) {
checkAccessThread();
return new UndoSupport<E>(undoManager, source, editAdapter);
}
/**
* This method removes undo/redo support from the {@link EventList} it was
* installed on. This method is useful when the {@link EventList} must
* outlive the undo/redo support itself. Calling this method will make
* this support object available for garbage collection independently of
* the {@link EventList} of items.
*
* @throws IllegalStateException if this method is called from any Thread
* other than the Swing Event Dispatch Thread
*/
public void uninstall() {
checkAccessThread();
undoRedoSupport.removeUndoSupportListener(undoSupportHandler);
undoRedoSupport.uninstall();
undoSupportHandler = null;
undoRedoSupport = null;
undoManager = null;
editAdapter = null;
}
/**
* A convenience method to ensure {@link UndoSupport} is being accessed
* from the Event Dispatch Thread.
*/
private static void checkAccessThread() {
if (!SwingUtilities.isEventDispatchThread())
throw new IllegalStateException("UndoRedoSupport must be accessed from the Swing Event Dispatch Thread, but was called on Thread \"" + Thread.currentThread().getName() + "\"");
}
/**
* Listens for GL edits and responds by transforming them into Swing edits
* and posting them to the given UndoManager.
*/
private class UndoSupportHandler implements UndoRedoSupport.Listener {
public void undoableEditHappened(UndoRedoSupport.Edit edit) {
undoManager.addEdit(editAdapter.evaluate(edit));
}
}
/**
* The default strategy for transforming GL edits into Swing edits.
*/
private static class DefaultEditAdapter implements FunctionList.Function<UndoRedoSupport.Edit, UndoableEdit> {
public UndoableEdit evaluate(UndoRedoSupport.Edit edit) {
return new EditAdapter(edit);
}
private static class EditAdapter extends AbstractUndoableEdit {
private final UndoRedoSupport.Edit edit;
public EditAdapter(UndoRedoSupport.Edit edit) {
this.edit = edit;
}
@Override
public void undo() { edit.undo(); }
@Override
public boolean canUndo() { return edit.canUndo(); }
@Override
public void redo() { edit.redo(); }
@Override
public boolean canRedo() { return edit.canRedo(); }
}
}
}
|