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