KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > xml > xam > ui > undo > CompoundUndoManager


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.xml.xam.ui.undo;
21
22 import javax.swing.event.UndoableEditEvent JavaDoc;
23 import javax.swing.undo.AbstractUndoableEdit JavaDoc;
24 import javax.swing.undo.CannotRedoException JavaDoc;
25 import javax.swing.undo.CannotUndoException JavaDoc;
26 import javax.swing.undo.UndoableEdit JavaDoc;
27 import org.openide.awt.UndoRedo;
28
29 /**
30  * A proxy for another UndoRedo.Manager instance, which permits a set
31  * of undoable edits to be treated as a "compound" edit, when this
32  * manager is not in the "compound" mode. This is useful for the
33  * source editor to treat document edits individually, but the model
34  * editor can treat all of the document edits as a single change.
35  *
36  * @author Nathan Fiedler
37  */

38 public class CompoundUndoManager extends FilterUndoManager {
39     /** silence compiler warnings */
40     private static final long serialVersionUID = 1L;
41     /** If true, undo manager is operating in "compound" mode. */
42     private boolean isCompound;
43     /** True when the undoable edit has been reached during the undo/redo
44      * of a compound set. */

45     private boolean undoRedoComplete;
46     /** If true, the begin or end compound edit was encountered and the
47      * undo()/redo() methods need to operate on the entire set. */

48     private boolean undoRedoCompound;
49     /** If true, addEdit() will wrap the next UndoableEdit in a begin
50      * compound undoable edit. */

51     private boolean consumeNextEdit;
52     /** If true, indicates that an undoable edit has been added since
53      * beginCompound() was called, and as such, an end edit is required. */

54     private boolean appendEndEdit;
55     /** If true, an end edit was undone without the corresponding start
56      * edit, indicating that if a new edit is added, a new end edit must
57      * be added to close the compound set. */

58     private boolean openCompound;
59     /** True if undo/redo operation is in progress while outside of
60      * compound mode. In this case, the begin/end compound edits are
61      * significant as they must be performed. */

62     private boolean compoundInProgress;
63
64     /**
65      * Creates a new instance of CompoundUndoManager.
66      *
67      * @param original UndoRedo.Manager to be proxied.
68      */

69     public CompoundUndoManager(UndoRedo.Manager original) {
70         super(original);
71     }
72
73     /**
74      * Starts the compound mode of this manager, in which all subsequent
75      * edits will be treated as a single edit when undo/redo is performed
76      * outside of compound mode.
77      */

78     public synchronized void beginCompound() {
79         if (!isCompound) {
80             isCompound = true;
81             consumeNextEdit = true;
82             appendEndEdit = false;
83         }
84     }
85
86     /**
87      * Stops the compound mode of this manager.
88      *
89      * @see #beginCompound()
90      */

91     public synchronized void endCompound() {
92         if (isCompound) {
93             isCompound = false;
94             if (appendEndEdit) {
95                 // We are leaving compound mode with document edits on
96
// the queue, so treat this as the 'open' state.
97
openCompound = true;
98             }
99         }
100     }
101
102     /**
103      * Indicates if this undo manager is currently in compound mode.
104      *
105      * @return true if in compound mode, false otherwise.
106      */

107     public synchronized boolean isCompound() {
108         return isCompound;
109     }
110
111     @Override JavaDoc
112     public boolean addEdit(UndoableEdit JavaDoc anEdit) {
113         // Respond to the additon of the edit, and then add it (or its
114
// replacement) to the queue via the superclass.
115
anEdit = processEdit(anEdit);
116         return super.addEdit(anEdit);
117     }
118
119     @Override JavaDoc
120     public void discardAllEdits() {
121         super.discardAllEdits();
122         if (isCompound) {
123             // In compound mode, be ready to consume the next edit.
124
consumeNextEdit = true;
125         }
126         // Reset the append state as there are no edits on the queue.
127
appendEndEdit = false;
128         // No more edits, no more open compound issue.
129
openCompound = false;
130     }
131
132     /**
133      * Process the edit that is about to be added to the queue.
134      *
135      * @param anEdit the undoable edit to process.
136      * @return the edit, possibly replaced by another.
137      */

138     private UndoableEdit JavaDoc processEdit(UndoableEdit JavaDoc anEdit) {
139         if (isCompound) {
140             if (openCompound) {
141                 // Transition back to the state of having a begin edit
142
// followed by document edits, and ready to add an end
143
// edit to the queue, when needed.
144
openCompound = false;
145                 appendEndEdit = true;
146             } else if (!appendEndEdit) {
147                 appendEndEdit = true;
148                 if (consumeNextEdit) {
149                     consumeNextEdit = false;
150                     // Replace the original with our filter edit.
151
anEdit = new BeginCompoundEdit(anEdit);
152                 }
153             }
154         } else if (appendEndEdit) {
155             // Reset to avoid looping if addEdit() is called again.
156
appendEndEdit = false;
157             // An edit, probably a model edit, is about to be added.
158
// We left compound mode and there were document edits on
159
// the queue, so we must close that compound set.
160
openCompound = false;
161             super.addEdit(new EndCompoundEdit());
162         }
163         return anEdit;
164     }
165
166     @Override JavaDoc
167     public void undoableEditHappened(UndoableEditEvent JavaDoc event) {
168         // Process the edit without adding it to the queue.
169
UndoableEdit JavaDoc anEdit = processEdit(event.getEdit());
170         // Need to provide our replacement edit, if any, otherwise we
171
// will lose it and never get the chance to add it again.
172
event = new UndoableEditEvent JavaDoc(event.getSource(), anEdit);
173         // Delegate to the superclass, which will add the edit to the
174
// queue, and fire off the necessary state change event.
175
super.undoableEditHappened(event);
176     }
177
178     @Override JavaDoc
179     public synchronized void redo() throws CannotRedoException JavaDoc {
180         if (isCompound) {
181             // Within compound mode, handle redo as usual.
182
super.redo();
183         } else {
184             compoundInProgress = true;
185             try {
186                 // Outside of compound mode, redo the entire set of
187
// compound edits, if the next edit marks the beginning.
188
undoRedoCompound = false;
189                 super.redo();
190                 if (undoRedoCompound || openCompound) {
191                     undoRedoComplete = false;
192                     while (!undoRedoComplete && canRedo()) {
193                         super.redo();
194                     }
195                 }
196             } finally {
197                 compoundInProgress = false;
198             }
199         }
200     }
201
202     @Override JavaDoc
203     public synchronized void undo() throws CannotUndoException JavaDoc {
204         if (isCompound) {
205             // Within compound mode, handle undo as usual.
206
super.undo();
207         } else {
208             compoundInProgress = true;
209             try {
210                 // Outside of compound mode, undo the entire set of
211
// compound edits, if the previous edit marks the end.
212
undoRedoCompound = false;
213                 super.undo();
214                 if (undoRedoCompound || openCompound) {
215                     undoRedoComplete = false;
216                     while (!undoRedoComplete && canUndo()) {
217                         super.undo();
218                     }
219                 }
220             } finally {
221                 compoundInProgress = false;
222             }
223         }
224     }
225
226     /**
227      * Sentinel edit that marks the beginning of a compound set of edits.
228      */

229     private class BeginCompoundEdit extends FilterUndoableEdit {
230         /** silence compiler warnings */
231         private static final long serialVersionUID = 1L;
232
233         /**
234          * Creates a new instance of BeginCompoundEdit.
235          *
236          * @param delegate the original undoable edit.
237          */

238         public BeginCompoundEdit(UndoableEdit JavaDoc delegate) {
239             super();
240             this.delegate = delegate;
241         }
242
243         @Override JavaDoc
244         public boolean isSignificant() {
245             if (delegate != null) {
246                 return compoundInProgress || delegate.isSignificant();
247             } else {
248                 return compoundInProgress;
249             }
250         }
251
252         @Override JavaDoc
253         public void redo() throws CannotRedoException JavaDoc {
254             super.redo();
255             undoRedoCompound = true;
256             openCompound = true;
257         }
258
259         @Override JavaDoc
260         public void undo() throws CannotUndoException JavaDoc {
261             super.undo();
262             undoRedoComplete = true;
263             openCompound = false;
264         }
265     }
266
267     /**
268      * Sentinel edit that marks the end of a compound set of edits.
269      * This is not a filter edit since the fix for issue 8692 prevents
270      * us from replacing a document edit. Rather, we are simply added
271      * to the end of the queue and indicate to our manager that the
272      * entire compound set needs to be undone (or redo is finished).
273      */

274     private class EndCompoundEdit extends AbstractUndoableEdit JavaDoc {
275         /** silence compiler warnings */
276         private static final long serialVersionUID = 1L;
277
278         @Override JavaDoc
279         public boolean isSignificant() {
280             // We are significant during a compound undo/redo, so that
281
// the boolean flags are set appropriately. Outside of the
282
// undo/redo, we do not have to be significant, and in fact
283
// that is desirable. We may get replaced if the set is left
284
// open, but the undo manager will compensate later.
285
return compoundInProgress;
286         }
287
288         @Override JavaDoc
289         public void redo() throws CannotUndoException JavaDoc {
290             super.redo();
291             undoRedoComplete = true;
292             openCompound = false;
293         }
294
295         @Override JavaDoc
296         public void undo() throws CannotUndoException JavaDoc {
297             super.undo();
298             undoRedoCompound = true;
299             openCompound = true;
300         }
301     }
302 }
303
Popular Tags