1 /*
   2  * Copyright (c) 1997, 2014, 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 @SuppressWarnings("serial") // Same-version serialization only
  36 public class CompoundEdit extends AbstractUndoableEdit {
  37     /**
  38      * True if this edit has never received <code>end</code>.
  39      */
  40     boolean inProgress;
  41 
  42     /**
  43      * The collection of <code>UndoableEdit</code>s
  44      * undone/redone en masse by this <code>CompoundEdit</code>.
  45      */
  46     protected Vector<UndoableEdit> edits;
  47 
  48     public CompoundEdit() {
  49         super();
  50         inProgress = true;
  51         edits = new Vector<UndoableEdit>();
  52     }
  53 
  54     /**
  55      * Sends <code>undo</code> to all contained
  56      * <code>UndoableEdits</code> in the reverse of
  57      * the order in which they were added.
  58      */
  59     public void undo() throws CannotUndoException {
  60         super.undo();
  61         int i = edits.size();
  62         while (i-- > 0) {
  63             UndoableEdit e = edits.elementAt(i);
  64             e.undo();
  65         }
  66     }
  67 
  68     /**
  69      * Sends <code>redo</code> to all contained
  70      * <code>UndoableEdit</code>s in the order in
  71      * which they were added.
  72      */
  73     public void redo() throws CannotRedoException {
  74         super.redo();
  75         Enumeration<UndoableEdit> cursor = edits.elements();
  76         while (cursor.hasMoreElements()) {
  77             cursor.nextElement().redo();
  78         }
  79     }
  80 
  81     /**
  82      * Returns the last <code>UndoableEdit</code> in
  83      * <code>edits</code>, or <code>null</code>
  84      * if <code>edits</code> is empty.
  85      *
  86      * @return the last {@code UndoableEdit} in {@code edits},
  87      *         or {@code null} if {@code edits} is empty.
  88      */
  89     protected UndoableEdit lastEdit() {
  90         int count = edits.size();
  91         if (count > 0)
  92             return edits.elementAt(count-1);
  93         else
  94             return null;
  95     }
  96 
  97     /**
  98      * Sends <code>die</code> to each subedit,
  99      * in the reverse of the order that they were added.
 100      */
 101     public void die() {
 102         int size = edits.size();
 103         for (int i = size-1; i >= 0; i--)
 104         {
 105             UndoableEdit e = edits.elementAt(i);
 106 //          System.out.println("CompoundEdit(" + i + "): Discarding " +
 107 //                             e.getUndoPresentationName());
 108             e.die();
 109         }
 110         super.die();
 111     }
 112 
 113     /**
 114      * If this edit is <code>inProgress</code>,
 115      * accepts <code>anEdit</code> and returns true.
 116      *
 117      * <p>The last edit added to this <code>CompoundEdit</code>
 118      * is given a chance to <code>addEdit(anEdit)</code>.
 119      * If it refuses (returns false), <code>anEdit</code> is
 120      * given a chance to <code>replaceEdit</code> the last edit.
 121      * If <code>anEdit</code> returns false here,
 122      * it is added to <code>edits</code>.
 123      *
 124      * @param anEdit the edit to be added
 125      * @return true if the edit is <code>inProgress</code>;
 126      *  otherwise returns false
 127      */
 128     public boolean addEdit(UndoableEdit anEdit) {
 129         if (!inProgress) {
 130             return false;
 131         } else {
 132             UndoableEdit last = lastEdit();
 133 
 134             // If this is the first subedit received, just add it.
 135             // Otherwise, give the last one a chance to absorb the new
 136             // one.  If it won't, give the new one a chance to absorb
 137             // the last one.
 138 
 139             if (last == null) {
 140                 edits.addElement(anEdit);
 141             }
 142             else if (!last.addEdit(anEdit)) {
 143                 if (anEdit.replaceEdit(last)) {
 144                     edits.removeElementAt(edits.size()-1);
 145                 }
 146                 edits.addElement(anEdit);
 147             }
 148 
 149             return true;
 150         }
 151     }
 152 
 153     /**
 154      * Sets <code>inProgress</code> to false.
 155      *
 156      * @see #canUndo
 157      * @see #canRedo
 158      */
 159     public void end() {
 160         inProgress = false;
 161     }
 162 
 163     /**
 164      * Returns false if <code>isInProgress</code> or if super
 165      * returns false.
 166      *
 167      * @see     #isInProgress
 168      */
 169     public boolean canUndo() {
 170         return !isInProgress() && super.canUndo();
 171     }
 172 
 173     /**
 174      * Returns false if <code>isInProgress</code> or if super
 175      * returns false.
 176      *
 177      * @see     #isInProgress
 178      */
 179     public boolean canRedo() {
 180         return !isInProgress() && super.canRedo();
 181     }
 182 
 183     /**
 184      * Returns true if this edit is in progress--that is, it has not
 185      * received end. This generally means that edits are still being
 186      * added to it.
 187      *
 188      * @return  whether this edit is in progress
 189      * @see     #end
 190      */
 191     public boolean isInProgress() {
 192         return inProgress;
 193     }
 194 
 195     /**
 196      * Returns true if any of the <code>UndoableEdit</code>s
 197      * in <code>edits</code> do.
 198      * Returns false if they all return false.
 199      */
 200     public boolean  isSignificant() {
 201         Enumeration<UndoableEdit> cursor = edits.elements();
 202         while (cursor.hasMoreElements()) {
 203             if (cursor.nextElement().isSignificant()) {
 204                 return true;
 205             }
 206         }
 207         return false;
 208     }
 209 
 210     /**
 211      * Returns <code>getPresentationName</code> from the
 212      * last <code>UndoableEdit</code> added to
 213      * <code>edits</code>. If <code>edits</code> is empty,
 214      * calls super.
 215      */
 216     public String getPresentationName() {
 217         UndoableEdit last = lastEdit();
 218         if (last != null) {
 219             return last.getPresentationName();
 220         } else {
 221             return super.getPresentationName();
 222         }
 223     }
 224 
 225     /**
 226      * Returns <code>getUndoPresentationName</code>
 227      * from the last <code>UndoableEdit</code>
 228      * added to <code>edits</code>.
 229      * If <code>edits</code> is empty, calls super.
 230      */
 231     public String getUndoPresentationName() {
 232         UndoableEdit last = lastEdit();
 233         if (last != null) {
 234             return last.getUndoPresentationName();
 235         } else {
 236             return super.getUndoPresentationName();
 237         }
 238     }
 239 
 240     /**
 241      * Returns <code>getRedoPresentationName</code>
 242      * from the last <code>UndoableEdit</code>
 243      * added to <code>edits</code>.
 244      * If <code>edits</code> is empty, calls super.
 245      */
 246     public String getRedoPresentationName() {
 247         UndoableEdit last = lastEdit();
 248         if (last != null) {
 249             return last.getRedoPresentationName();
 250         } else {
 251             return super.getRedoPresentationName();
 252         }
 253     }
 254 
 255     /**
 256      * Returns a string that displays and identifies this
 257      * object's properties.
 258      *
 259      * @return a String representation of this object
 260      */
 261     public String toString()
 262     {
 263         return super.toString()
 264             + " inProgress: " + inProgress
 265             + " edits: " + edits;
 266     }
 267 }