KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > rice > cs > drjava > model > definitions > CompoundUndoManager


1 /*BEGIN_COPYRIGHT_BLOCK
2  *
3  * This file is part of DrJava. Download the current version of this project from http://www.drjava.org/
4  * or http://sourceforge.net/projects/drjava/
5  *
6  * DrJava Open Source License
7  *
8  * Copyright (C) 2001-2005 JavaPLT group at Rice University (javaplt@rice.edu). All rights reserved.
9  *
10  * Developed by: Java Programming Languages Team, Rice University, http://www.cs.rice.edu/~javaplt/
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
13  * documentation files (the "Software"), to deal with the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
15  * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
16  *
17  * - Redistributions of source code must retain the above copyright notice, this list of conditions and the
18  * following disclaimers.
19  * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
20  * following disclaimers in the documentation and/or other materials provided with the distribution.
21  * - Neither the names of DrJava, the JavaPLT, Rice University, nor the names of its contributors may be used to
22  * endorse or promote products derived from this Software without specific prior written permission.
23  * - Products derived from this software may not be called "DrJava" nor use the term "DrJava" as part of their
24  * names without prior written permission from the JavaPLT group. For permission, write to javaplt@rice.edu.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
27  * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28  * CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
29  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
30  * WITH THE SOFTWARE.
31  *
32  END_COPYRIGHT_BLOCK*/

33
34 package edu.rice.cs.drjava.model.definitions;
35
36 import javax.swing.undo.*;
37 import javax.swing.SwingUtilities JavaDoc;
38 import java.util.LinkedList JavaDoc;
39
40 import edu.rice.cs.drjava.model.GlobalEventNotifier;
41 import edu.rice.cs.util.swing.Utilities;
42
43 /** Extended UndoManager with increased functionality. Can handle aggregating multiple edits into one for the purposes
44   * of undoing and redoing. It exposes editToBeUndone and editToBeRedone (under new names); they are protected methods
45   * in UndoManager. The public methods that involve composite state are synchronized, so this manager can be accessed
46   * outside of the event thread. The internal data structures _compoundEdits and _keys are not thread safe but they
47   * only accessed only by synchronized methods. The synchronization scheme (locking on this) follows UndoManager.
48   * @version $Id: CompoundUndoManager.java 4033 2006-11-16 20:16:51Z rcartwright $
49   */

50 public class CompoundUndoManager extends UndoManager {
51   
52   private static volatile int counter = 0;
53   
54   private final int id;
55   
56   /** The compound edits we are storing. Not thread safe! */
57   private final LinkedList JavaDoc<CompoundEdit> _compoundEdits;
58   
59   /** The keys for the CompoundEdits we are storing. */
60   private final LinkedList JavaDoc<Integer JavaDoc> _keys;
61   
62   /** The next key to use for nested CompoundEdits. */
63   private volatile int _nextKey;
64   
65   /** The last edit that was performed before the last save. */
66   private volatile UndoableEdit _savePoint;
67   
68   /** Keeps track of the listeners to this undo manager. */
69   private final GlobalEventNotifier _notifier;
70   
71   /** Standard constructor. */
72   public CompoundUndoManager(GlobalEventNotifier notifier) {
73     super();
74     counter++;
75     id = counter;
76     _compoundEdits = new LinkedList JavaDoc<CompoundEdit>();
77     _keys = new LinkedList JavaDoc<Integer JavaDoc>();
78     _nextKey = 0;
79     _savePoint = null;
80     _notifier = notifier;
81   }
82   
83   /** Starts a compound edit.
84    * @return the key for the compound edit
85    */

86   public synchronized int startCompoundEdit() {
87     _compoundEdits.add(0, new CompoundEdit());
88     _keys.add(0, new Integer JavaDoc(_nextKey));
89     if (_nextKey < Integer.MAX_VALUE) _nextKey++;
90     else _nextKey = Integer.MIN_VALUE;
91     return _keys.get(0).intValue();
92   }
93   
94   /** Ends the last compound edit that was created.
95    * Used when a compound edit is created by the _undoListener in DefinitionsPane and the key is not known in DefinitionsDocument.
96    */

97   public synchronized void endLastCompoundEdit() {
98     if (_keys.size() == 0) return;
99     // NOTE: The preceding can happen if for example uncomment lines does not modify any text.
100
endCompoundEdit(_keys.get(0).intValue());
101   }
102   
103   /** Ends a compound edit.
104    * @param key the key that was returned by startCompoundEdit()
105    */

106   public synchronized void endCompoundEdit(int key) {
107     if (_keys.size() == 0) return;
108     
109     if (_keys.get(0) == key) {
110       _keys.remove(0);
111       final CompoundEdit ce = _compoundEdits.remove(0);
112  
113       ce.end();
114       if (ce.canUndo()) {
115         if (! _compoundEditInProgress()) {
116           super.addEdit(ce);
117           _notifyUndoHappened();
118         }
119         else _compoundEdits.get(0).addEdit(ce);
120       }
121     }
122     else throw new IllegalStateException JavaDoc("Improperly nested compound edits.");
123   }
124   
125   /** We are getting the last Compound Edit entered into the list.
126    * This is for making a Compound edit for granular undo.
127    */

128   public synchronized CompoundEdit getLastCompoundEdit() { return _compoundEdits.get(0); }
129   
130   /** Gets the next undo.
131    * @return the next undo
132    */

133   public UndoableEdit getNextUndo() { return editToBeUndone(); }
134   
135   /** Gets the next redo.
136    * @return the next redo
137    */

138   public UndoableEdit getNextRedo() { return editToBeRedone(); }
139   
140   /** Adds an edit. Checks whether or not the current edit is a compound edit.
141    * @param e the edit to be added
142    * @return true if the add is successful, false otherwise
143    */

144   public synchronized boolean addEdit(UndoableEdit e) {
145     if (_compoundEditInProgress()) {
146       // _notifyUndoHappened(); // added this for granular undo
147
return _compoundEdits.get(0).addEdit(e);
148     }
149     else {
150       boolean result = super.addEdit(e);
151       _notifyUndoHappened();
152       return result;
153     }
154   }
155   
156   /** Returns whether or not a compound edit is in progress.
157    * @return true iff in progress
158    */

159   public synchronized boolean _compoundEditInProgress() { return ! _compoundEdits.isEmpty(); }
160   
161   /** Returns true when a compound edit is in progress, or when there are valid stored undoable edits
162    * @return true iff undoing is possible
163    */

164   public synchronized boolean canUndo() { return _compoundEditInProgress() || super.canUndo(); }
165   
166   /** Returns the presentation name for this undo, or delegates to super if none is available
167     * @return the undo's presentation name
168     */

169   public synchronized String JavaDoc getUndoPresentationName() {
170     if (_compoundEditInProgress()) return "Undo Previous Command";
171     return super.getUndoPresentationName();
172   }
173   
174   /** Undoes the last undoable edit, or compound edit created by the user. */
175   public synchronized void undo() {
176     endCompoundEdit();
177     super.undo();
178   }
179   
180   /** Overload for undo which allows the initiator of a CompoundEdit to abandon it.
181     * WARNING: this has been used to date and has not been properly tested and very possibly may not work.
182     * @param key the key returned by the last call to startCompoundEdit
183     * @throws IllegalArgumentException if the key is incorrect
184     */

185   public synchronized void undo(int key) {
186     if (_keys.get(0) == key) {
187       final CompoundEdit ce = _compoundEdits.get(0);
188       _compoundEdits.remove(0);
189       _keys.remove(0);
190       
191       
192       SwingUtilities.invokeLater(new Runnable JavaDoc() {
193         public void run() {
194           ce.end();
195           ce.undo();
196           ce.die();
197         }
198       }); // unsafe methods inherited from CompoundEdit
199
}
200     else throw new IllegalArgumentException JavaDoc("Bad undo key " + key + "!");
201   }
202   
203   /** Overrides redo so that any compound edit in progress is ended before the redo is performed. */
204   public synchronized void redo() {
205     endCompoundEdit(); // How can there be a compound edit in progress if redo is available?
206
super.redo();
207   }
208   
209   /** Helper method to notify the view that an undoable edit has occured. Note that lock on this is not held by
210    * the event thread (even if called from event thread) when notification happens. */

211   private void _notifyUndoHappened() {
212     // Use SwingUtilities.invokeLater so that notification is deferred when running in the event thread.
213
SwingUtilities.invokeLater(new Runnable JavaDoc() { public void run() { _notifier.undoableEditHappened(); } });
214   }
215   
216   /** Ends the compoundEdit in progress if any. Used by undo(), redo(), documentSaved(). */
217   private synchronized void endCompoundEdit() {
218     Integer JavaDoc[] keys = _keys.toArray(new Integer JavaDoc[_keys.size()]); // unit testing ran into a concurrent modification exception without this copying operation
219
if (_compoundEditInProgress()) {
220       for (int key: keys) endCompoundEdit(key);
221     }
222   }
223   
224   /** Informs this undo manager that the document has been saved. */
225   public synchronized void documentSaved() {
226     endCompoundEdit();
227     _savePoint = editToBeUndone();
228 // Utilities.showDebug("_savePoint := " + _savePoint);
229
}
230   
231   /** Determines if the document is in the same undo state as it was when it was last saved.
232    * @return true iff all changes have been undone since the last save
233    */

234   public synchronized boolean isModified() {
235 // Utilities.showDebug("_savePoint = " + _savePoint + " editToBeUndone() = " + editToBeUndone());
236
return editToBeUndone() != _savePoint;
237   }
238   
239   public String JavaDoc toString() { return "(CompoundUndoManager: " + id + ")"; }
240   
241   /** Used to help track down memory leaks. */
242   // protected void finalize() throws Throwable{
243
// super.finalize();
244
// }
245
}
246
Popular Tags