File: UndoSupport.java

package info (click to toggle)
libglazedlists-java 1.9.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 3,024 kB
  • ctags: 4,252
  • sloc: java: 22,561; xml: 818; sh: 51; makefile: 5
file content (175 lines) | stat: -rw-r--r-- 7,785 bytes parent folder | download | duplicates (3)
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(); }
        }
    }
}