1 /* CompoundEdit.java -- Combines multiple UndoableEdits. 2 Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. 3 4 This file is part of GNU Classpath. 5 6 GNU Classpath is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 GNU Classpath is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU Classpath; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301 USA. 20 21 Linking this library statically or dynamically with other modules is 22 making a combined work based on this library. Thus, the terms and 23 conditions of the GNU General Public License cover the whole 24 combination. 25 26 As a special exception, the copyright holders of this library give you 27 permission to link this library with independent modules to produce an 28 executable, regardless of the license terms of these independent 29 modules, and to copy and distribute the resulting executable under 30 terms of your choice, provided that you also meet, for each linked 31 independent module, the terms and conditions of the license of that 32 module. An independent module is a module which is not derived from 33 or based on this library. If you modify this library, you may extend 34 this exception to your version of the library, but you are not 35 obligated to do so. If you do not wish to do so, delete this 36 exception statement from your version. */ 37 38 39 package javax.swing.undo; 40 41 import java.util.Vector; 42 43 /** 44 * An editing action that consists of multiple 45 * <code>UndoableEdits</code>. 46 * 47 * <p>The use of a <code>CompoundEdit</code> is divided in two separate 48 * phases.</p> 49 * 50 * <ol> 51 * <li>In the first phase, the <code>CompoundEdit</code> is 52 * initialized. After a new instance of <code>CompoundEdit</code> has 53 * been created, {@link #addEdit(UndoableEdit)} is called for each 54 * element of the compound. To terminate the initialization phase, 55 * call {@link #end()}.</li> 56 * <li>In the second phase, the the <code>CompoundEdit</code> can be 57 * used, typically by invoking {@link #undo()} and 58 * {@link #redo()}.</li> 59 * </ol> 60 * 61 * @author Andrew Selkirk (aselkirk@sympatico.ca) 62 * @author Sascha Brawer (brawer@dandelis.ch) 63 */ 64 public class CompoundEdit 65 extends AbstractUndoableEdit 66 { 67 /** 68 * The identifier of this class in object serialization. Determined 69 * using the serialver tool of Sun J2SE 1.4.1_01. 70 */ 71 private static final long serialVersionUID = -6512679249930119683L; 72 73 74 /** 75 * The <code>UndoableEdit</code>s being combined into a compound 76 * editing action. 77 */ 78 protected Vector edits; 79 80 81 /** 82 * Indicates whether the creation of this CompoundEdit is still in 83 * progress. Initially, the value of this flag is 84 * <code>true</code>. The {@link #end()} method changes the flag to 85 * <code>false</code>. 86 */ 87 private boolean inProgress; 88 89 90 /** 91 * Constructs a new CompoundEdit. 92 */ CompoundEdit()93 public CompoundEdit() 94 { 95 edits = new Vector(); 96 inProgress = true; 97 } 98 99 100 /** 101 * Undoes all edits that are part of of this 102 * <code>CompoundEdit</code>. The compound elements will receive the 103 * <code>undo</code> message in the reverse order of addition. 104 * 105 * @throws CannotUndoException if {@link #canUndo()} returns 106 * <code>false</code>. This can happen if {@link #end()} has not 107 * been called on this <code>CompoundEdit</code>, or if this edit 108 * has already been undone. 109 * 110 * @see #canUndo() 111 * @see #redo() 112 */ undo()113 public void undo() 114 throws CannotUndoException 115 { 116 // AbstractUndoableEdit.undo() will throw a CannotUndoException if 117 // canUndo returns false. 118 super.undo(); 119 120 for (int i = edits.size() - 1; i >= 0; i--) 121 ((UndoableEdit) edits.elementAt(i)).undo(); 122 } 123 124 125 /** 126 * Redoes all edits that are part of of this 127 * <code>CompoundEdit</code>. The compound elements will receive the 128 * <code>undo</code> message in the same order as they were added. 129 * 130 * @throws CannotRedoException if {@link #canRedo()} returns 131 * <code>false</code>. This can happen if {@link #end()} has not 132 * been called on this <code>CompoundEdit</code>, or if this edit 133 * has already been redone. 134 * 135 * @see #canRedo() 136 * @see #undo() 137 */ redo()138 public void redo() 139 throws CannotRedoException 140 { 141 // AbstractUndoableEdit.redo() will throw a CannotRedoException if 142 // canRedo returns false. 143 super.redo(); 144 145 for (int i = 0; i < edits.size(); i++) 146 ((UndoableEdit) edits.elementAt(i)).redo(); 147 } 148 149 150 /** 151 * Returns the the <code>UndoableEdit</code> that was last added to 152 * this compound. 153 */ lastEdit()154 protected UndoableEdit lastEdit() 155 { 156 if (edits.size() == 0) 157 return null; 158 else 159 return (UndoableEdit) edits.elementAt(edits.size() - 1); 160 } 161 162 163 /** 164 * Informs this edit action, and all compound edits, that they will 165 * no longer be used. Some actions might use this information to 166 * release resources such as open files. Called by {@link 167 * UndoManager} before this action is removed from the edit queue. 168 * 169 * <p>The compound elements will receive the 170 * <code>die</code> message in the reverse order of addition. 171 */ die()172 public void die() 173 { 174 for (int i = edits.size() - 1; i >= 0; i--) 175 ((UndoableEdit) edits.elementAt(i)).die(); 176 177 super.die(); 178 } 179 180 181 /** 182 * Incorporates another editing action into this one, thus forming a 183 * combined edit. 184 * 185 * <p>If this edit’s {@link #end()} method has been called 186 * before, <code>false</code> is returned immediately. Otherwise, 187 * the {@linkplain #lastEdit() last added edit} is given the 188 * opportunity to {@linkplain UndoableEdit#addEdit(UndoableEdit) 189 * incorporate} <code>edit</code>. If this fails, <code>edit</code> 190 * is given the opportunity to {@linkplain 191 * UndoableEdit#replaceEdit(UndoableEdit) replace} the last added 192 * edit. If this fails as well, <code>edit</code> gets added as a 193 * new compound to {@link #edits}. 194 * 195 * @param edit the editing action being added. 196 * 197 * @return <code>true</code> if <code>edit</code> could somehow be 198 * incorporated; <code>false</code> if <code>edit</code> has not 199 * been incorporated because {@link #end()} was called before. 200 */ addEdit(UndoableEdit edit)201 public boolean addEdit(UndoableEdit edit) 202 { 203 UndoableEdit last; 204 205 // If end has been called before, do nothing. 206 if (!inProgress) 207 return false; 208 209 last = lastEdit(); 210 211 // If edit is the very first edit, just add it to the list. 212 if (last == null) 213 { 214 edits.add(edit); 215 return true; 216 } 217 218 // Try to incorporate edit into last. 219 if (last.addEdit(edit)) 220 return true; 221 222 // Try to replace last by edit. 223 if (edit.replaceEdit(last)) 224 { 225 edits.set(edits.size() - 1, edit); 226 return true; 227 } 228 229 // If everything else has failed, add edit to the list of compound 230 // edits. 231 edits.add(edit); 232 return true; 233 } 234 235 236 /** 237 * Informs this <code>CompoundEdit</code> that its construction 238 * phase has been completed. After this method has been called, 239 * {@link #undo()} and {@link #redo()} may be called, {@link 240 * #isInProgress()} will return <code>false</code>, and all attempts 241 * to {@linkplain #addEdit(UndoableEdit) add further edits} will 242 * fail. 243 */ end()244 public void end() 245 { 246 inProgress = false; 247 } 248 249 250 /** 251 * Determines whether it would be possible to undo this editing 252 * action. The result will be <code>true</code> if {@link #end()} 253 * has been called on this <code>CompoundEdit</code>, {@link #die()} 254 * has not yet been called, and the edit has not been undone 255 * already. 256 * 257 * @return <code>true</code> to indicate that this action can be 258 * undone; <code>false</code> otherwise. 259 * 260 * @see #undo() 261 * @see #canRedo() 262 */ canUndo()263 public boolean canUndo() 264 { 265 return !inProgress && super.canUndo(); 266 } 267 268 269 /** 270 * Determines whether it would be possible to redo this editing 271 * action. The result will be <code>true</code> if {@link #end()} 272 * has been called on this <code>CompoundEdit</code>, {@link #die()} 273 * has not yet been called, and the edit has not been redone 274 * already. 275 * 276 * @return <code>true</code> to indicate that this action can be 277 * redone; <code>false</code> otherwise. 278 * 279 * @see #redo() 280 * @see #canUndo() 281 */ canRedo()282 public boolean canRedo() 283 { 284 return !inProgress && super.canRedo(); 285 } 286 287 288 /** 289 * Determines whether the initial construction phase of this 290 * <code>CompoundEdit</code> is still in progress. During this 291 * phase, edits {@linkplain #addEdit(UndoableEdit) may be 292 * added}. After initialization has been terminated by calling 293 * {@link #end()}, {@link #undo()} and {@link #redo()} can be used. 294 * 295 * @return <code>true</code> if the initialization phase is still in 296 * progress; <code>false</code> if {@link #end()} has been called. 297 * 298 * @see #end() 299 */ isInProgress()300 public boolean isInProgress() 301 { 302 return inProgress; 303 } 304 305 306 /** 307 * Determines whether this editing action is significant enough for 308 * being seperately undoable by the user. A typical significant 309 * action would be the resizing of an object. However, changing the 310 * selection in a text document would usually not be considered 311 * significant. 312 * 313 * <p>A <code>CompoundEdit</code> is significant if any of its 314 * elements are significant. 315 */ isSignificant()316 public boolean isSignificant() 317 { 318 for (int i = edits.size() - 1; i >= 0; i--) 319 if (((UndoableEdit) edits.elementAt(i)).isSignificant()) 320 return true; 321 322 return false; 323 } 324 325 326 /** 327 * Returns a human-readable, localized name that describes this 328 * editing action and can be displayed to the user. 329 * 330 * <p>The implementation delegates the call to the {@linkplain 331 * #lastEdit() last added edit action}. If no edit has been added 332 * yet, the inherited implementation will be invoked, which always 333 * returns an empty string. 334 */ getPresentationName()335 public String getPresentationName() 336 { 337 UndoableEdit last; 338 339 last = lastEdit(); 340 if (last == null) 341 return super.getPresentationName(); 342 else 343 return last.getPresentationName(); 344 } 345 346 347 /** 348 * Calculates a localized message text for presenting the undo 349 * action to the user. 350 * 351 * <p>The implementation delegates the call to the {@linkplain 352 * #lastEdit() last added edit action}. If no edit has been added 353 * yet, the {@linkplain 354 * AbstractUndoableEdit#getUndoPresentationName() inherited 355 * implementation} will be invoked. 356 */ getUndoPresentationName()357 public String getUndoPresentationName() 358 { 359 UndoableEdit last; 360 361 last = lastEdit(); 362 if (last == null) 363 return super.getUndoPresentationName(); 364 else 365 return last.getUndoPresentationName(); 366 } 367 368 369 /** 370 * Calculates a localized message text for presenting the redo 371 * action to the user. 372 * 373 * <p>The implementation delegates the call to the {@linkplain 374 * #lastEdit() last added edit action}. If no edit has been added 375 * yet, the {@linkplain 376 * AbstractUndoableEdit#getRedoPresentationName() inherited 377 * implementation} will be invoked. 378 */ getRedoPresentationName()379 public String getRedoPresentationName() 380 { 381 UndoableEdit last; 382 383 last = lastEdit(); 384 if (last == null) 385 return super.getRedoPresentationName(); 386 else 387 return last.getRedoPresentationName(); 388 } 389 390 391 /** 392 * Calculates a string that may be useful for debugging. 393 */ toString()394 public String toString() 395 { 396 return super.toString() 397 + " inProgress: " + inProgress 398 + " edits: " + edits; 399 } 400 } 401