001/* CompoundEdit.java -- Combines multiple UndoableEdits. 002 Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038 039package javax.swing.undo; 040 041import java.util.Vector; 042 043/** 044 * An editing action that consists of multiple 045 * <code>UndoableEdits</code>. 046 * 047 * <p>The use of a <code>CompoundEdit</code> is divided in two separate 048 * phases.</p> 049 * 050 * <ol> 051 * <li>In the first phase, the <code>CompoundEdit</code> is 052 * initialized. After a new instance of <code>CompoundEdit</code> has 053 * been created, {@link #addEdit(UndoableEdit)} is called for each 054 * element of the compound. To terminate the initialization phase, 055 * call {@link #end()}.</li> 056 * <li>In the second phase, the the <code>CompoundEdit</code> can be 057 * used, typically by invoking {@link #undo()} and 058 * {@link #redo()}.</li> 059 * </ol> 060 * 061 * @author Andrew Selkirk (aselkirk@sympatico.ca) 062 * @author Sascha Brawer (brawer@dandelis.ch) 063 */ 064public class CompoundEdit 065 extends AbstractUndoableEdit 066{ 067 /** 068 * The identifier of this class in object serialization. Determined 069 * using the serialver tool of Sun J2SE 1.4.1_01. 070 */ 071 private static final long serialVersionUID = -6512679249930119683L; 072 073 074 /** 075 * The <code>UndoableEdit</code>s being combined into a compound 076 * editing action. 077 */ 078 protected Vector<UndoableEdit> edits; 079 080 081 /** 082 * Indicates whether the creation of this CompoundEdit is still in 083 * progress. Initially, the value of this flag is 084 * <code>true</code>. The {@link #end()} method changes the flag to 085 * <code>false</code>. 086 */ 087 private boolean inProgress; 088 089 090 /** 091 * Constructs a new CompoundEdit. 092 */ 093 public CompoundEdit() 094 { 095 edits = new Vector<UndoableEdit>(); 096 inProgress = true; 097 } 098 099 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}