1 |
/* AbstractTableModel.java -- |
/* CompoundEdit.java -- Combines multiple UndoableEdits. |
2 |
Copyright (C) 2002 Free Software Foundation, Inc. |
Copyright (C) 2002, 2003 Free Software Foundation, Inc. |
3 |
|
|
4 |
This file is part of GNU Classpath. |
This file is part of GNU Classpath. |
5 |
|
|
41 |
import java.util.Vector; |
import java.util.Vector; |
42 |
|
|
43 |
/** |
/** |
44 |
* CompoundEdit |
* An editing action that consists of multiple |
45 |
* @author Andrew Selkirk |
* <code>UndoableEdits</code>. |
46 |
|
* |
47 |
|
* <p>The use of a <code>CompoundEdit</code> is divided in two separate |
48 |
|
* phases. |
49 |
|
* |
50 |
|
* <ol><li>In the first phase, the <code>CompoundEdit</code> is |
51 |
|
* initialized. After a new instance of <code>CompoundEdit</code> has |
52 |
|
* been created, {@link #addEdit(UndoableEdit)} is called for each |
53 |
|
* element of the compound. To terminate the initialization phase, |
54 |
|
* call {@link #end()}.</li> |
55 |
|
* |
56 |
|
* <li>In the second phase, the the <code>CompoundEdit</code> can be |
57 |
|
* used, typically by invoking {@link #undo()} and {@link |
58 |
|
* #redo()}.</li></ol> |
59 |
|
* |
60 |
|
* @author Andrew Selkirk (aselkirk@sympatico.ca) |
61 |
|
* @author Sascha Brawer (brawer@dandelis.ch) |
62 |
*/ |
*/ |
63 |
public class CompoundEdit extends AbstractUndoableEdit { |
public class CompoundEdit |
64 |
|
extends AbstractUndoableEdit |
65 |
//------------------------------------------------------------- |
{ |
66 |
// Variables -------------------------------------------------- |
/** |
67 |
//------------------------------------------------------------- |
* The <code>UndoableEdit</code>s being combined into a compound |
68 |
|
* editing action. |
69 |
/** |
*/ |
70 |
* The collection of UndoableEdits undone/redone en |
protected Vector edits; |
71 |
* masse by this CompoundEdit |
|
72 |
*/ |
|
73 |
protected Vector edits = new Vector(); |
/** |
74 |
|
* Indicates whether the creation of this CompoundEdit is still in |
75 |
/** |
* progress. Initially, the value of this flag is |
76 |
* TODO |
* <code>true</code>. The {@link #end()} method changes the flag to |
77 |
*/ |
* <code>false</code>. |
78 |
private boolean inProgress = false; |
*/ |
79 |
|
private boolean inProgress; |
80 |
|
|
81 |
//------------------------------------------------------------- |
|
82 |
// Initialization --------------------------------------------- |
/** |
83 |
//------------------------------------------------------------- |
* Constructs a new CompoundEdit. |
84 |
|
*/ |
85 |
/** |
public CompoundEdit() |
86 |
* Create new Compound Edit |
{ |
87 |
*/ |
edits = new Vector(); |
88 |
public CompoundEdit() { |
inProgress = true; |
89 |
} // CompoundEdit() |
} |
90 |
|
|
91 |
|
|
92 |
//------------------------------------------------------------- |
/** |
93 |
// Interface: UndoableEdit ------------------------------------ |
* Undoes all edits that are part of of this |
94 |
//------------------------------------------------------------- |
* <code>CompoundEdit</code>. The compound elements will receive the |
95 |
|
* <code>undo</code> message in the reverse order of addition. |
96 |
/** |
* |
97 |
* addEdit |
* @throws CannotUndoException if {@link #canUndo()} returns |
98 |
* @param anEdit TODO |
* <code>false</code>. This can happen if {@link #end()} has not |
99 |
* @returns TODO |
* been called on this <code>CompoundEdit</code>, or if this edit |
100 |
*/ |
* has already been undone. |
101 |
public boolean addEdit(UndoableEdit anEdit) { |
* |
102 |
|
* @see #canUndo() |
103 |
// Variables |
* @see #redo() |
104 |
UndoableEdit lastEdit; |
*/ |
105 |
|
public void undo() |
106 |
if (inProgress == true) { |
throws CannotUndoException |
107 |
|
{ |
108 |
// Get Last Edit |
// AbstractUndoableEdit.undo() will throw a CannotUndoException if |
109 |
lastEdit = lastEdit(); |
// canUndo returns false. |
110 |
|
super.undo(); |
111 |
// Check for null |
|
112 |
if (lastEdit != null) { |
for (int i = edits.size() - 1; i >= 0; i--) |
113 |
|
((UndoableEdit) edits.elementAt(i)).undo(); |
114 |
if (lastEdit.addEdit(anEdit) == false) { |
} |
115 |
if (lastEdit.replaceEdit(anEdit) == false) { |
|
116 |
edits.add(anEdit); |
|
117 |
} |
/** |
118 |
} |
* Redoes all edits that are part of of this |
119 |
|
* <code>CompoundEdit</code>. The compound elements will receive the |
120 |
} // if: lastEdit |
* <code>undo</code> message in the same order as they were added. |
121 |
|
* |
122 |
return true; |
* @throws CannotRedoException if {@link #canRedo()} returns |
123 |
|
* <code>false</code>. This can happen if {@link #end()} has not |
124 |
} else { |
* been called on this <code>CompoundEdit</code>, or if this edit |
125 |
return false; |
* has already been redone. |
126 |
} |
* |
127 |
} // addEdit() |
* @see #canRedo() |
128 |
|
* @see #undo() |
129 |
/** |
*/ |
130 |
* canRedo |
public void redo() |
131 |
* @returns TODO |
throws CannotRedoException |
132 |
*/ |
{ |
133 |
public boolean canRedo() { |
// AbstractUndoableEdit.redo() will throw a CannotRedoException if |
134 |
if (isInProgress() == true || super.canRedo() == false) { |
// canRedo returns false. |
135 |
return false; |
super.redo(); |
136 |
} |
|
137 |
return true; |
for (int i = 0; i < edits.size(); i++) |
138 |
} // canRedo() |
((UndoableEdit) edits.elementAt(i)).redo(); |
139 |
|
} |
140 |
/** |
|
141 |
* canUndo |
|
142 |
* @returns TODO |
/** |
143 |
*/ |
* Returns the the <code>UndoableEdit</code> that was last added to |
144 |
public boolean canUndo() { |
* this compound. |
145 |
if (isInProgress() == true || super.canUndo() == false) { |
*/ |
146 |
return false; |
protected UndoableEdit lastEdit() |
147 |
} |
{ |
148 |
return true; |
if (edits.size() == 0) |
149 |
} // canUndo() |
return null; |
150 |
|
else |
151 |
/** |
return (UndoableEdit) edits.elementAt(edits.size() - 1); |
152 |
* die |
} |
153 |
*/ |
|
154 |
public void die() { |
|
155 |
|
/** |
156 |
// Variables |
* Informs this edit action, and all compound edits, that they will |
157 |
int index; |
* no longer be used. Some actions might use this information to |
158 |
UndoableEdit current; |
* release resources such as open files. Called by {@link |
159 |
|
* UndoManager} before this action is removed from the edit queue. |
160 |
// Loop through all contained UndoableEdits |
* |
161 |
for (index = edits.size() - 1; index >= 0; index--) { |
* <p>The compound elements will receive the |
162 |
current = (UndoableEdit) edits.elementAt(index); |
* <code>die</code> message in the reverse order of addition. |
163 |
current.die(); |
*/ |
164 |
} // for: index |
public void die() |
165 |
|
{ |
166 |
} // die() |
for (int i = edits.size() - 1; i >= 0; i--) |
167 |
|
((UndoableEdit) edits.elementAt(i)).die(); |
168 |
/** |
|
169 |
* end |
super.die(); |
170 |
*/ |
} |
171 |
public void end() { |
|
172 |
inProgress = false; |
|
173 |
} // end() |
/** |
174 |
|
* Incorporates another editing action into this one, thus forming a |
175 |
/** |
* combined edit. |
176 |
* getPresentationName |
* |
177 |
* @returns TODO |
* <p>If this edit’s {@link #end()} method has been called |
178 |
*/ |
* before, <code>false</code> is returned immediately. Otherwise, |
179 |
public String getPresentationName() { |
* the {@linkplain #lastEdit() last added edit} is given the |
180 |
if (edits.size() == 0) { |
* opportunity to {@linkplain UndoableEdit#addEdit(UndoableEdit) |
181 |
return super.getPresentationName(); |
* incorporate} <code>edit</code>. If this fails, <code>edit</code> |
182 |
} else { |
* is given the opportunity to {@linkplain |
183 |
return lastEdit().getPresentationName(); |
* UndoableEdit#replaceEdit(UndoableEdit) replace} the last added |
184 |
} |
* edit. If this fails as well, <code>edit</code> gets added as a |
185 |
} // getPresentationName() |
* new compound to {@link #edits}. |
186 |
|
* |
187 |
/** |
* @param edit the editing action being added. |
188 |
* getRedoPresentationName |
* |
189 |
* @returns TODO |
* @return <code>true</code> if <code>edit</code> could somehow be |
190 |
*/ |
* incorporated; <code>false</code> if <code>edit</code> has not |
191 |
public String getRedoPresentationName() { |
* been incorporated because {@link #end()} was called before. |
192 |
if (edits.size() == 0) { |
*/ |
193 |
return super.getRedoPresentationName(); |
public boolean addEdit(UndoableEdit edit) |
194 |
} else { |
{ |
195 |
return lastEdit().getRedoPresentationName(); |
UndoableEdit last; |
196 |
} |
|
197 |
} // getRedoPresentationName() |
// If end has been called before, do nothing. |
198 |
|
if (!inProgress) |
199 |
/** |
return false; |
200 |
* getUndoPresentationName |
|
201 |
* @returns TODO |
last = lastEdit(); |
202 |
*/ |
|
203 |
public String getUndoPresentationName() { |
// If edit is the very first edit, just add it to the list. |
204 |
if (edits.size() == 0) { |
if (last == null) |
205 |
return super.getUndoPresentationName(); |
{ |
206 |
} else { |
edits.add(edit); |
207 |
return lastEdit().getUndoPresentationName(); |
return true; |
208 |
} |
} |
209 |
} // getUndoPresentationName() |
|
210 |
|
// Try to incorporate edit into last. |
211 |
/** |
if (last.addEdit(edit)) |
212 |
* isInProgress |
return true; |
213 |
* @returns TODO |
|
214 |
*/ |
// Try to replace last by edit. |
215 |
public boolean isInProgress() { |
if (edit.replaceEdit(last)) |
216 |
return inProgress; |
{ |
217 |
} // isInProgress() |
edits.set(edits.size() - 1, edit); |
218 |
|
return true; |
219 |
|
} |
220 |
/** |
|
221 |
* isSignigicant |
// If everything else has failed, add edit to the list of compound |
222 |
* @returns TODO |
// edits. |
223 |
*/ |
edits.add(edit); |
224 |
public boolean isSignificant() { |
return true; |
225 |
|
} |
226 |
// Variables |
|
227 |
int index; |
|
228 |
UndoableEdit current; |
/** |
229 |
|
* Informs this <code>CompoundEdit</code> that its construction |
230 |
// Check each edit |
* phase has been completed. After this method has been called, |
231 |
for (index = 0; index < edits.size(); index++) { |
* {@link #undo()} and {@link #redo()} may be called, {@link |
232 |
current = (UndoableEdit) edits.elementAt(index); |
* #isInProgress()} will return <code>false</code>, and all attempts |
233 |
if (current.isSignificant() == true) { |
* to {@linkplain #addEdit(UndoableEdit) add further edits} will |
234 |
return true; |
* fail. |
235 |
} |
*/ |
236 |
} // for: index |
public void end() |
237 |
|
{ |
238 |
return false; |
inProgress = false; |
239 |
|
} |
240 |
} // isSignificant() |
|
241 |
|
|
242 |
/** |
/** |
243 |
* lastEdit |
* Determines whether it would be possible to undo this editing |
244 |
* @returns TODO |
* action. The result will be <code>true</code> if {@link #end()} |
245 |
*/ |
* has been called on this <code>CompoundEdit</code>, {@link #die()} |
246 |
protected UndoableEdit lastEdit() { |
* has not yet been called, and the edit has not been undone |
247 |
if (edits.size() == 0) { |
* already. |
248 |
return null; |
* |
249 |
} |
* @return <code>true</code> to indicate that this action can be |
250 |
return (UndoableEdit) edits.elementAt(edits.size() - 1); |
* undone; <code>false</code> otherwise. |
251 |
} // lastEdit() |
* |
252 |
|
* @see #undo() |
253 |
/** |
* @see #canRedo() |
254 |
* redo |
*/ |
255 |
* @throws CannotRedoException TODO |
public boolean canUndo() |
256 |
*/ |
{ |
257 |
public void redo() throws CannotRedoException { |
return !inProgress && super.canUndo(); |
258 |
|
} |
259 |
// Variables |
|
260 |
int index; |
|
261 |
UndoableEdit current; |
/** |
262 |
|
* Determines whether it would be possible to redo this editing |
263 |
// Loop through all contained UndoableEdits |
* action. The result will be <code>true</code> if {@link #end()} |
264 |
for (index = 0; index < edits.size(); index++) { |
* has been called on this <code>CompoundEdit</code>, {@link #die()} |
265 |
current = (UndoableEdit) edits.elementAt(index); |
* has not yet been called, and the edit has not been redone |
266 |
current.redo(); |
* already. |
267 |
} // for: index |
* |
268 |
|
* @return <code>true</code> to indicate that this action can be |
269 |
} // redo() |
* redone; <code>false</code> otherwise. |
270 |
|
* |
271 |
/** |
* @see #redo() |
272 |
* String representation |
* @see #canUndo() |
273 |
* @returns String representation |
*/ |
274 |
*/ |
public boolean canRedo() |
275 |
public String toString() { |
{ |
276 |
return null; // TODO |
return !inProgress && super.canRedo(); |
277 |
} // toString() |
} |
278 |
|
|
279 |
/** |
|
280 |
* undo |
/** |
281 |
* @throws CannotUndoException TODO |
* Determines whether the initial construction phase of this |
282 |
*/ |
* <code>CompoundEdit</code> is still in progress. During this |
283 |
public void undo() throws CannotUndoException { |
* phase, edits {@linkplain #addEdit(UndoableEdit) may be |
284 |
|
* added}. After initialization has been terminated by calling |
285 |
// Variables |
* {@link #end()}, {@link #undo()} and {@link #redo()} can be used. |
286 |
int index; |
* |
287 |
UndoableEdit current; |
* @return <code>true</code> if the initialization phase is still in |
288 |
|
* progress; <code>false</code> if {@link #end()} has been called. |
289 |
// Loop through all contained UndoableEdits |
* |
290 |
for (index = edits.size() - 1; index >= 0; index--) { |
* @see #end() |
291 |
current = (UndoableEdit) edits.elementAt(index); |
*/ |
292 |
current.undo(); |
public boolean isInProgress() |
293 |
} // for: index |
{ |
294 |
|
return inProgress; |
295 |
} // undo() |
} |
296 |
|
|
297 |
|
|
298 |
} // CompoundEdit |
/** |
299 |
|
* Determines whether this editing action is significant enough for |
300 |
|
* being seperately undoable by the user. A typical significant |
301 |
|
* action would be the resizing of an object. However, changing the |
302 |
|
* selection in a text document would usually not be considered |
303 |
|
* significant. |
304 |
|
* |
305 |
|
* <p>A <code>CompoundEdit</code> is significant if any of its |
306 |
|
* elements are significant. |
307 |
|
*/ |
308 |
|
public boolean isSignificant() |
309 |
|
{ |
310 |
|
for (int i = edits.size() - 1; i >= 0; i--) |
311 |
|
if (((UndoableEdit) edits.elementAt(i)).isSignificant()) |
312 |
|
return true; |
313 |
|
|
314 |
|
return false; |
315 |
|
} |
316 |
|
|
317 |
|
|
318 |
|
/** |
319 |
|
* Returns a human-readable, localized name that describes this |
320 |
|
* editing action and can be displayed to the user. |
321 |
|
* |
322 |
|
* <p>The implementation delegates the call to the {@linkplain |
323 |
|
* #lastEdit() last added edit action}. If no edit has been added |
324 |
|
* yet, the inherited implementation will be invoked, which always |
325 |
|
* returns an empty string. |
326 |
|
*/ |
327 |
|
public String getPresentationName() |
328 |
|
{ |
329 |
|
UndoableEdit last; |
330 |
|
|
331 |
|
last = lastEdit(); |
332 |
|
if (last == null) |
333 |
|
return super.getPresentationName(); |
334 |
|
else |
335 |
|
return last.getPresentationName(); |
336 |
|
} |
337 |
|
|
338 |
|
|
339 |
|
/** |
340 |
|
* Calculates a localized message text for presenting the undo |
341 |
|
* action to the user. |
342 |
|
* |
343 |
|
* <p>The implementation delegates the call to the {@linkplain |
344 |
|
* #lastEdit() last added edit action}. If no edit has been added |
345 |
|
* yet, the {@linkplain |
346 |
|
* AbstractUndoableEdit#getUndoPresentationName() inherited |
347 |
|
* implementation} will be invoked. |
348 |
|
*/ |
349 |
|
public String getUndoPresentationName() |
350 |
|
{ |
351 |
|
UndoableEdit last; |
352 |
|
|
353 |
|
last = lastEdit(); |
354 |
|
if (last == null) |
355 |
|
return super.getUndoPresentationName(); |
356 |
|
else |
357 |
|
return last.getUndoPresentationName(); |
358 |
|
} |
359 |
|
|
360 |
|
|
361 |
|
/** |
362 |
|
* Calculates a localized message text for presenting the redo |
363 |
|
* action to the user. |
364 |
|
* |
365 |
|
* <p>The implementation delegates the call to the {@linkplain |
366 |
|
* #lastEdit() last added edit action}. If no edit has been added |
367 |
|
* yet, the {@linkplain |
368 |
|
* AbstractUndoableEdit#getRedoPresentationName() inherited |
369 |
|
* implementation} will be invoked. |
370 |
|
*/ |
371 |
|
public String getRedoPresentationName() |
372 |
|
{ |
373 |
|
UndoableEdit last; |
374 |
|
|
375 |
|
last = lastEdit(); |
376 |
|
if (last == null) |
377 |
|
return super.getRedoPresentationName(); |
378 |
|
else |
379 |
|
return last.getRedoPresentationName(); |
380 |
|
} |
381 |
|
|
382 |
|
|
383 |
|
/** |
384 |
|
* Calculates a string that may be useful for debugging. |
385 |
|
*/ |
386 |
|
public String toString() |
387 |
|
{ |
388 |
|
return super.toString() |
389 |
|
+ " inProgress: " + inProgress |
390 |
|
+ " edits: " + edits; |
391 |
|
} |
392 |
|
} |