1 /*
   2  * Copyright (c) 1997, 2004, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package javax.swing.undo;
  26 
  27 import java.util.*;
  28 
  29 /**
  30  * A concrete subclass of AbstractUndoableEdit, used to assemble little
  31  * UndoableEdits into great big ones.
  32  *
  33  * @author Ray Ryan
  34  */
  35 public class CompoundEdit extends AbstractUndoableEdit {
  36     /**
  37      * True if this edit has never received <code>end</code>.
  38      */
  39     boolean inProgress;
  40 
  41     /**
  42      * The collection of <code>UndoableEdit</code>s
  43      * undone/redone en masse by this <code>CompoundEdit</code>.
  44      */
  45     protected Vector<UndoableEdit> edits;
  46 
  47     public CompoundEdit() {
  48         super();
  49         inProgress = true;
  50         edits = new Vector<UndoableEdit>();
  51     }
  52 
  53     /**
  54      * Sends <code>undo</code> to all contained
  55      * <code>UndoableEdits</code> in the reverse of
  56      * the order in which they were added.
  57      */
  58     public void undo() throws CannotUndoException {
  59         super.undo();
  60         int i = edits.size();
  61         while (i-- > 0) {
  62             UndoableEdit e = edits.elementAt(i);
  63             e.undo();
  64         }
  65     }
  66 
  67     /**
  68      * Sends <code>redo</code> to all contained
  69      * <code>UndoableEdit</code>s in the order in
  70      * which they were added.
  71      */
  72     public void redo() throws CannotRedoException {
  73         super.redo();
  74         Enumeration cursor = edits.elements();
  75         while (cursor.hasMoreElements()) {
  76             ((UndoableEdit)cursor.nextElement()).redo();
  77         }
  78     }
  79 
  80     /**
  81      * Returns the last <code>UndoableEdit</code> in
  82      * <code>edits</code>, or <code>null</code>
  83      * if <code>edits</code> is empty.
  84      */
  85     protected UndoableEdit lastEdit() {
  86         int count = edits.size();
  87         if (count > 0)
  88             return edits.elementAt(count-1);
  89         else
  90             return null;
  91     }
  92 
  93     /**
  94      * Sends <code>die</code> to each subedit,
  95      * in the reverse of the order that they were added.
  96      */
  97     public void die() {
  98         int size = edits.size();
  99         for (int i = size-1; i >= 0; i--)
 100         {
 101             UndoableEdit e = edits.elementAt(i);
 102 //          System.out.println("CompoundEdit(" + i + "): Discarding " +
 103 //                             e.getUndoPresentationName());
 104             e.die();
 105         }
 106         super.die();
 107     }
 108 
 109     /**
 110      * If this edit is <code>inProgress</code>,
 111      * accepts <code>anEdit</code> and returns true.
 112      *
 113      * <p>The last edit added to this <code>CompoundEdit</code>
 114      * is given a chance to <code>addEdit(anEdit)</code>.
 115      * If it refuses (returns false), <code>anEdit</code> is
 116      * given a chance to <code>replaceEdit</code> the last edit.
 117      * If <code>anEdit</code> returns false here,
 118      * it is added to <code>edits</code>.
 119      *
 120      * @param anEdit the edit to be added
 121      * @return true if the edit is <code>inProgress</code>;
 122      *  otherwise returns false
 123      */
 124     public boolean addEdit(UndoableEdit anEdit) {
 125         if (!inProgress) {
 126             return false;
 127         } else {
 128             UndoableEdit last = lastEdit();
 129 
 130             // If this is the first subedit received, just add it.
 131             // Otherwise, give the last one a chance to absorb the new
 132             // one.  If it won't, give the new one a chance to absorb
 133             // the last one.
 134 
 135             if (last == null) {
 136                 edits.addElement(anEdit);
 137             }
 138             else if (!last.addEdit(anEdit)) {
 139                 if (anEdit.replaceEdit(last)) {
 140                     edits.removeElementAt(edits.size()-1);
 141                 }
 142                 edits.addElement(anEdit);
 143             }
 144 
 145             return true;
 146         }
 147     }
 148 
 149     /**
 150      * Sets <code>inProgress</code> to false.
 151      *
 152      * @see #canUndo
 153      * @see #canRedo
 154      */
 155     public void end() {
 156         inProgress = false;
 157     }
 158 
 159     /**
 160      * Returns false if <code>isInProgress</code> or if super
 161      * returns false.
 162      *
 163      * @see     #isInProgress
 164      */
 165     public boolean canUndo() {
 166         return !isInProgress() && super.canUndo();
 167     }
 168 
 169     /**
 170      * Returns false if <code>isInProgress</code> or if super
 171      * returns false.
 172      *
 173      * @see     #isInProgress
 174      */
 175     public boolean canRedo() {
 176         return !isInProgress() && super.canRedo();
 177     }
 178 
 179     /**
 180      * Returns true if this edit is in progress--that is, it has not
 181      * received end. This generally means that edits are still being
 182      * added to it.
 183      *
 184      * @see     #end
 185      */
 186     public boolean isInProgress() {
 187         return inProgress;
 188     }
 189 
 190     /**
 191      * Returns true if any of the <code>UndoableEdit</code>s
 192      * in <code>edits</code> do.
 193      * Returns false if they all return false.
 194      */
 195     public boolean  isSignificant() {
 196         Enumeration cursor = edits.elements();
 197         while (cursor.hasMoreElements()) {
 198             if (((UndoableEdit)cursor.nextElement()).isSignificant()) {
 199                 return true;
 200             }
 201         }
 202         return false;
 203     }
 204 
 205     /**
 206      * Returns <code>getPresentationName</code> from the
 207      * last <code>UndoableEdit</code> added to
 208      * <code>edits</code>. If <code>edits</code> is empty,
 209      * calls super.
 210      */
 211     public String getPresentationName() {
 212         UndoableEdit last = lastEdit();
 213         if (last != null) {
 214             return last.getPresentationName();
 215         } else {
 216             return super.getPresentationName();
 217         }
 218     }
 219 
 220     /**
 221      * Returns <code>getUndoPresentationName</code>
 222      * from the last <code>UndoableEdit</code>
 223      * added to <code>edits</code>.
 224      * If <code>edits</code> is empty, calls super.
 225      */
 226     public String getUndoPresentationName() {
 227         UndoableEdit last = lastEdit();
 228         if (last != null) {
 229             return last.getUndoPresentationName();
 230         } else {
 231             return super.getUndoPresentationName();
 232         }
 233     }
 234 
 235     /**
 236      * Returns <code>getRedoPresentationName</code>
 237      * from the last <code>UndoableEdit</code>
 238      * added to <code>edits</code>.
 239      * If <code>edits</code> is empty, calls super.
 240      */
 241     public String getRedoPresentationName() {
 242         UndoableEdit last = lastEdit();
 243         if (last != null) {
 244             return last.getRedoPresentationName();
 245         } else {
 246             return super.getRedoPresentationName();
 247         }
 248     }
 249 
 250     /**
 251      * Returns a string that displays and identifies this
 252      * object's properties.
 253      *
 254      * @return a String representation of this object
 255      */
 256     public String toString()
 257     {
 258         return super.toString()
 259             + " inProgress: " + inProgress
 260             + " edits: " + edits;
 261     }
 262 }