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