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