001    /* CompoundEdit.java -- Combines multiple UndoableEdits.
002       Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.swing.undo;
040    
041    import java.util.Vector;
042    
043    /**
044     * An editing action that consists of multiple
045     * <code>UndoableEdits</code>.
046     *
047     * <p>The use of a <code>CompoundEdit</code> is divided in two separate
048     * phases.</p>
049     *
050     * <ol>
051     * <li>In the first phase, the <code>CompoundEdit</code> is
052     *     initialized.  After a new instance of <code>CompoundEdit</code> has
053     *     been created, {@link #addEdit(UndoableEdit)} is called for each
054     *     element of the compound.  To terminate the initialization phase,
055     *     call {@link #end()}.</li>
056     * <li>In the second phase, the the <code>CompoundEdit</code> can be
057     *     used, typically by invoking {@link #undo()} and
058     *     {@link #redo()}.</li>
059     * </ol>
060     *
061     * @author Andrew Selkirk (aselkirk@sympatico.ca)
062     * @author Sascha Brawer (brawer@dandelis.ch)
063     */
064    public class CompoundEdit
065      extends AbstractUndoableEdit
066    {
067      /**
068       * The identifier of this class in object serialization. Determined
069       * using the serialver tool of Sun J2SE 1.4.1_01.
070       */
071      private static final long serialVersionUID = -6512679249930119683L;
072    
073    
074      /**
075       * The <code>UndoableEdit</code>s being combined into a compound
076       * editing action.
077       */
078      protected Vector<UndoableEdit> edits;
079    
080    
081      /**
082       * Indicates whether the creation of this CompoundEdit is still in
083       * progress. Initially, the value of this flag is
084       * <code>true</code>. The {@link #end()} method changes the flag to
085       * <code>false</code>. 
086       */
087      private boolean inProgress;
088    
089    
090      /**
091       * Constructs a new CompoundEdit.
092       */
093      public CompoundEdit()
094      {
095        edits = new Vector<UndoableEdit>();
096        inProgress = true;
097      }
098      
099    
100      /**
101       * Undoes all edits that are part of of this
102       * <code>CompoundEdit</code>. The compound elements will receive the
103       * <code>undo</code> message in the reverse order of addition.
104       *
105       * @throws CannotUndoException if {@link #canUndo()} returns
106       * <code>false</code>. This can happen if {@link #end()} has not
107       * been called on this <code>CompoundEdit</code>, or if this edit
108       * has already been undone.
109       *
110       * @see #canUndo()
111       * @see #redo()
112       */
113      public void undo()
114        throws CannotUndoException
115      {
116        // AbstractUndoableEdit.undo() will throw a CannotUndoException if
117        // canUndo returns false.
118        super.undo();
119    
120        for (int i = edits.size() - 1; i >= 0; i--)
121          edits.elementAt(i).undo();
122      }
123    
124    
125      /**
126       * Redoes all edits that are part of of this
127       * <code>CompoundEdit</code>. The compound elements will receive the
128       * <code>undo</code> message in the same order as they were added.
129       *
130       * @throws CannotRedoException if {@link #canRedo()} returns
131       * <code>false</code>. This can happen if {@link #end()} has not
132       * been called on this <code>CompoundEdit</code>, or if this edit
133       * has already been redone.
134       *
135       * @see #canRedo()
136       * @see #undo()
137       */
138      public void redo()
139        throws CannotRedoException
140      {
141        // AbstractUndoableEdit.redo() will throw a CannotRedoException if
142        // canRedo returns false.
143        super.redo();
144    
145        for (int i = 0; i < edits.size(); i++)
146          edits.elementAt(i).redo();
147      }
148    
149      
150      /**
151       * Returns the the <code>UndoableEdit</code> that was last added to
152       * this compound.
153       */
154      protected UndoableEdit lastEdit()
155      {
156        if (edits.size() == 0)
157          return null;
158        else
159          return edits.elementAt(edits.size() - 1);
160      }
161    
162    
163      /**
164       * Informs this edit action, and all compound edits, that they will
165       * no longer be used. Some actions might use this information to
166       * release resources such as open files.  Called by {@link
167       * UndoManager} before this action is removed from the edit queue.
168       *
169       * <p>The compound elements will receive the
170       * <code>die</code> message in the reverse order of addition.
171       */
172      public void die()
173      {
174        for (int i = edits.size() - 1; i >= 0; i--)
175          edits.elementAt(i).die();
176    
177        super.die();
178      }
179    
180    
181      /**
182       * Incorporates another editing action into this one, thus forming a
183       * combined edit.
184       *
185       * <p>If this edit&#x2019;s {@link #end()} method has been called
186       * before, <code>false</code> is returned immediately. Otherwise,
187       * the {@linkplain #lastEdit() last added edit} is given the
188       * opportunity to {@linkplain UndoableEdit#addEdit(UndoableEdit)
189       * incorporate} <code>edit</code>.  If this fails, <code>edit</code>
190       * is given the opportunity to {@linkplain
191       * UndoableEdit#replaceEdit(UndoableEdit) replace} the last added
192       * edit.  If this fails as well, <code>edit</code> gets added as a
193       * new compound to {@link #edits}.
194       * 
195       * @param edit the editing action being added.
196       *
197       * @return <code>true</code> if <code>edit</code> could somehow be
198       * incorporated; <code>false</code> if <code>edit</code> has not
199       * been incorporated because {@link #end()} was called before.
200       */
201      public boolean addEdit(UndoableEdit edit)
202      {
203        UndoableEdit last;
204    
205        // If end has been called before, do nothing.
206        if (!inProgress)
207          return false;
208    
209        last = lastEdit();
210    
211        // If edit is the very first edit, just add it to the list.
212        if (last == null)
213        {
214          edits.add(edit);
215          return true;
216        }
217    
218        // Try to incorporate edit into last.
219        if (last.addEdit(edit))
220          return true;
221    
222        // Try to replace last by edit.
223        if (edit.replaceEdit(last))
224        {
225          edits.set(edits.size() - 1, edit);
226          return true;
227        }
228    
229        // If everything else has failed, add edit to the list of compound
230        // edits.
231        edits.add(edit);
232        return true;
233      }
234    
235    
236      /**
237       * Informs this <code>CompoundEdit</code> that its construction
238       * phase has been completed. After this method has been called,
239       * {@link #undo()} and {@link #redo()} may be called, {@link
240       * #isInProgress()} will return <code>false</code>, and all attempts
241       * to {@linkplain #addEdit(UndoableEdit) add further edits} will
242       * fail.
243       */
244      public void end()
245      {
246        inProgress = false;
247      }
248    
249    
250      /**
251       * Determines whether it would be possible to undo this editing
252       * action. The result will be <code>true</code> if {@link #end()}
253       * has been called on this <code>CompoundEdit</code>, {@link #die()}
254       * has not yet been called, and the edit has not been undone
255       * already.
256       *
257       * @return <code>true</code> to indicate that this action can be
258       * undone; <code>false</code> otherwise.
259       *
260       * @see #undo()
261       * @see #canRedo()
262       */
263      public boolean canUndo()
264      {
265        return !inProgress && super.canUndo();
266      }
267    
268    
269      /**
270       * Determines whether it would be possible to redo this editing
271       * action. The result will be <code>true</code> if {@link #end()}
272       * has been called on this <code>CompoundEdit</code>, {@link #die()}
273       * has not yet been called, and the edit has not been redone
274       * already.
275       *
276       * @return <code>true</code> to indicate that this action can be
277       * redone; <code>false</code> otherwise.
278       *
279       * @see #redo()
280       * @see #canUndo()
281       */
282      public boolean canRedo()
283      {
284        return !inProgress && super.canRedo();
285      }
286    
287    
288      /**
289       * Determines whether the initial construction phase of this
290       * <code>CompoundEdit</code> is still in progress.  During this
291       * phase, edits {@linkplain #addEdit(UndoableEdit) may be
292       * added}. After initialization has been terminated by calling
293       * {@link #end()}, {@link #undo()} and {@link #redo()} can be used.
294       *
295       * @return <code>true</code> if the initialization phase is still in
296       * progress; <code>false</code> if {@link #end()} has been called.
297       *
298       * @see #end()
299       */
300      public boolean isInProgress()
301      {
302        return inProgress;
303      }
304    
305    
306      /**
307       * Determines whether this editing action is significant enough for
308       * being seperately undoable by the user. A typical significant
309       * action would be the resizing of an object. However, changing the
310       * selection in a text document would usually not be considered
311       * significant.
312       *
313       * <p>A <code>CompoundEdit</code> is significant if any of its
314       * elements are significant.
315       */
316      public boolean isSignificant()
317      {
318        for (int i = edits.size() - 1; i >= 0; i--)
319          if (edits.elementAt(i).isSignificant())
320            return true;
321    
322        return false;
323      }
324      
325    
326      /**
327       * Returns a human-readable, localized name that describes this
328       * editing action and can be displayed to the user.
329       *
330       * <p>The implementation delegates the call to the {@linkplain
331       * #lastEdit() last added edit action}. If no edit has been added
332       * yet, the inherited implementation will be invoked, which always
333       * returns an empty string.
334       */
335      public String getPresentationName()
336      {
337        UndoableEdit last;
338    
339        last = lastEdit();
340        if (last == null)
341          return super.getPresentationName();
342        else
343          return last.getPresentationName();
344      }
345    
346    
347      /**
348       * Calculates a localized message text for presenting the undo
349       * action to the user.
350       *
351       * <p>The implementation delegates the call to the {@linkplain
352       * #lastEdit() last added edit action}. If no edit has been added
353       * yet, the {@linkplain
354       * AbstractUndoableEdit#getUndoPresentationName() inherited
355       * implementation} will be invoked.
356       */
357      public String getUndoPresentationName()
358      {
359        UndoableEdit last;
360    
361        last = lastEdit();
362        if (last == null)
363          return super.getUndoPresentationName();
364        else
365          return last.getUndoPresentationName();
366      }
367    
368    
369      /**
370       * Calculates a localized message text for presenting the redo
371       * action to the user.
372       *
373       * <p>The implementation delegates the call to the {@linkplain
374       * #lastEdit() last added edit action}. If no edit has been added
375       * yet, the {@linkplain
376       * AbstractUndoableEdit#getRedoPresentationName() inherited
377       * implementation} will be invoked.
378       */
379      public String getRedoPresentationName()
380      {
381        UndoableEdit last;
382    
383        last = lastEdit();
384        if (last == null)
385          return super.getRedoPresentationName();
386        else
387          return last.getRedoPresentationName();
388      }
389      
390      
391      /**
392       * Calculates a string that may be useful for debugging.
393       */
394      public String toString()
395      {
396        return super.toString()
397          + " inProgress: " + inProgress
398          + " edits: " + edits;
399      }
400    }