Frames | No Frames |
1: /* CompoundEdit.java -- Combines multiple UndoableEdits. 2: Copyright (C) 2002, 2003, 2004, 2005 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<UndoableEdit> 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: */ 93: public CompoundEdit() 94: { 95: edits = new Vector<UndoableEdit>(); 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: */ 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: 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: */ 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: edits.elementAt(i).redo(); 147: } 148: 149: 150: /** 151: * Returns the the <code>UndoableEdit</code> that was last added to 152: * this compound. 153: */ 154: protected UndoableEdit lastEdit() 155: { 156: if (edits.size() == 0) 157: return null; 158: else 159: return 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: */ 172: public void die() 173: { 174: for (int i = edits.size() - 1; i >= 0; i--) 175: 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 316: public boolean isSignificant() 317: { 318: for (int i = edits.size() - 1; i >= 0; i--) 319: if (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: */ 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: */ 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: */ 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: */ 394: public String toString() 395: { 396: return super.toString() 397: + " inProgress: " + inProgress 398: + " edits: " + edits; 399: } 400: }