| /* UndoableEditSupport.java -- |
| Copyright (C) 2002, 2003, 2004, 2005, 2006, Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| |
| package javax.swing.undo; |
| |
| import java.util.Iterator; |
| import java.util.Vector; |
| |
| import javax.swing.event.UndoableEditEvent; |
| import javax.swing.event.UndoableEditListener; |
| |
| /** |
| * A helper class for supporting {@link |
| * javax.swing.event.UndoableEditListener}. |
| * |
| * @author Andrew Selkirk (aselkirk@sympatico.ca) |
| * @author Sascha Brawer (brawer@dandelis.ch) |
| */ |
| public class UndoableEditSupport |
| { |
| /** |
| * The number of times that {@link #beginUpdate()} has been called |
| * without a matching call to {@link #endUpdate()}. |
| */ |
| protected int updateLevel; |
| |
| |
| /** |
| * compoundEdit |
| */ |
| protected CompoundEdit compoundEdit; |
| |
| |
| /** |
| * The currently registered listeners. |
| */ |
| protected Vector listeners = new Vector(); |
| |
| |
| /** |
| * The source of the broadcast UndoableEditEvents. |
| */ |
| protected Object realSource; |
| |
| |
| /** |
| * Constructs a new helper for broadcasting UndoableEditEvents. The |
| * events will indicate the newly constructed |
| * <code>UndoableEditSupport</code> instance as their source. |
| * |
| * @see #UndoableEditSupport(java.lang.Object) |
| */ |
| public UndoableEditSupport() |
| { |
| realSource = this; |
| } |
| |
| |
| /** |
| * Constructs a new helper for broadcasting UndoableEditEvents. |
| * |
| * @param realSource the source of the UndoableEditEvents that will |
| * be broadcast by this helper. If <code>realSource</code> is |
| * <code>null</code>, the events will indicate the newly constructed |
| * <code>UndoableEditSupport</code> instance as their source. |
| */ |
| public UndoableEditSupport(Object realSource) |
| { |
| if (realSource == null) |
| realSource = this; |
| this.realSource = realSource; |
| } |
| |
| |
| /** |
| * Returns a string representation of this object that may be useful |
| * for debugging. |
| */ |
| public String toString() |
| { |
| // Note that often, this.realSource == this. Therefore, dumping |
| // realSource without additional checks may lead to infinite |
| // recursion. See Classpath bug #7119. |
| return super.toString() + " updateLevel: " + updateLevel |
| + " listeners: " + listeners + " compoundEdit: " + compoundEdit; |
| } |
| |
| |
| /** |
| * Registers a listener. |
| * |
| * @param val the listener to be added. |
| */ |
| public synchronized void addUndoableEditListener(UndoableEditListener val) |
| { |
| listeners.add(val); |
| } |
| |
| |
| /** |
| * Unregisters a listener. |
| * @param val the listener to be removed. |
| */ |
| public synchronized void removeUndoableEditListener(UndoableEditListener val) |
| { |
| listeners.removeElement(val); |
| } |
| |
| |
| /** |
| * Returns an array containing the currently registered listeners. |
| */ |
| public synchronized UndoableEditListener[] getUndoableEditListeners() |
| { |
| UndoableEditListener[] result = new UndoableEditListener[listeners.size()]; |
| return (UndoableEditListener[]) listeners.toArray(result); |
| } |
| |
| |
| /** |
| * Notifies all registered listeners that an {@link |
| * UndoableEditEvent} has occured. |
| * |
| * <p><b>Lack of Thread Safety:</b> It is <em>not</em> safe to call |
| * this method from concurrent threads, unless the call is protected |
| * by a synchronization on this <code>UndoableEditSupport</code> |
| * instance. |
| * |
| * @param edit the edit action to be posted. |
| */ |
| protected void _postEdit(UndoableEdit edit) |
| { |
| UndoableEditEvent event; |
| Iterator iter; |
| |
| // Do nothing if we have no listeners. |
| if (listeners.isEmpty()) |
| return; |
| |
| event = new UndoableEditEvent(realSource, edit); |
| |
| // We clone the vector because this allows listeners to register |
| // or unregister listeners in their undoableEditHappened method. |
| // Otherwise, this would throw exceptions (in the case of |
| // Iterator, a java.util.ConcurrentModificationException; in the |
| // case of a direct loop over the Vector elements, some |
| // index-out-of-bounds exception). |
| iter = ((Vector) listeners.clone()).iterator(); |
| while (iter.hasNext()) |
| ((UndoableEditListener) iter.next()).undoableEditHappened(event); |
| } |
| |
| |
| /** |
| * If {@link #beginUpdate} has been called (so that the current |
| * update level is greater than zero), adds the specified edit |
| * to {@link #compoundEdit}. Otherwise, notify listeners of the |
| * edit by calling {@link #_postEdit(UndoableEdit)}. |
| * |
| * <p><b>Thread Safety:</b> It is safe to call this method from any |
| * thread without external synchronization. |
| * |
| * @param edit the edit action to be posted. |
| */ |
| public synchronized void postEdit(UndoableEdit edit) |
| { |
| if (compoundEdit != null) |
| compoundEdit.addEdit(edit); |
| else |
| _postEdit(edit); |
| } |
| |
| |
| /** |
| * Returns the current update level. |
| */ |
| public int getUpdateLevel() |
| { |
| return updateLevel; |
| } |
| |
| |
| /** |
| * Starts a (possibly nested) update session. If the current update |
| * level is zero, {@link #compoundEdit} is set to the result of the |
| * {@link #createCompoundEdit} method. In any case, the update level |
| * is increased by one. |
| * |
| * <p><b>Thread Safety:</b> It is safe to call this method from any |
| * thread without external synchronization. |
| */ |
| public synchronized void beginUpdate() |
| { |
| if (compoundEdit == null) |
| compoundEdit = createCompoundEdit(); |
| ++updateLevel; |
| } |
| |
| |
| /** |
| * Creates a new instance of {@link CompoundEdit}. Called by {@link |
| * #beginUpdate}. If a subclass wants {@link #beginUpdate} to work |
| * on a specific {@link #compoundEdit}, it should override this |
| * method. |
| * |
| * @return a newly created instance of {@link CompoundEdit}. |
| */ |
| protected CompoundEdit createCompoundEdit() |
| { |
| return new CompoundEdit(); |
| } |
| |
| |
| /** |
| * Ends an update session. If the terminated session was the |
| * outermost session, {@link #compoundEdit} will receive an |
| * <code>end</code> message, and {@link #_postEdit} gets called in |
| * order to notify any listeners. Finally, the |
| * <code>compoundEdit</code> is discarded. |
| * |
| * <p><b>Thread Safety:</b> It is safe to call this method from any |
| * thread without external synchronization. |
| */ |
| public synchronized void endUpdate() |
| { |
| if (updateLevel == 0) |
| throw new IllegalStateException(); |
| |
| if (--updateLevel > 0) |
| return; |
| |
| compoundEdit.end(); |
| _postEdit(compoundEdit); |
| compoundEdit = null; |
| } |
| } |