1 /*
2  * Copyright (c) 1997, 2015, 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      */
CompoundEdit()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      */
undo()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      */
redo()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      */
lastEdit()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      */
die()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      */
addEdit(UndoableEdit anEdit)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      */
end()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      */
canUndo()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      */
canRedo()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      */
isInProgress()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      */
isSignificant()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      */
getPresentationName()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      */
getUndoPresentationName()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      */
getRedoPresentationName()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      */
toString()264     public String toString()
265     {
266         return super.toString()
267             + " inProgress: " + inProgress
268             + " edits: " + edits;
269     }
270 }
271