/* * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI * for visualizing and manipulating spatial features with geometry and attributes. * * Copyright (C) 2003 Vivid Solutions * * This program 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 * of the License, or (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * For more information, contact: * * Vivid Solutions * Suite #1A * 2328 Government Street * Victoria BC V8T 5G5 * Canada * * (250)385-6040 * www.vividsolutions.com */ package com.vividsolutions.jump.workbench.model; import java.util.ArrayList; import java.util.Iterator; import javax.swing.undo.UndoManager; import javax.swing.undo.UndoableEdit; import com.vividsolutions.jts.util.Assert; /** * Receives UndoableEdits from PlugIns and CursorTools. Also provides access to * a Task's UndoManager. * <P> * In the documentation, the "receiving phase" refers to the time between the * calls to #start and #stop. * <P> * If there is an exception that leaves this UndoableCommand execution * partially complete and non-unexecutable, be sure to call * #reportIrreversibleChange() */ public class UndoableEditReceiver { private UndoManager undoManager = new UndoManager(); private ArrayList newUndoableEdits = new ArrayList(); /** Handle nested calls to UndoableEditReceiver */ private int transactions = 0; private boolean nothingToUndoReported = false; private boolean irreversibleChangeReported = false; private boolean undoManagerCouldUndoAtStart = false; private ArrayList listeners = new ArrayList(); public UndoableEditReceiver() { } public void startReceiving() { transactions++; setNothingToUndoReported(false); irreversibleChangeReported = false; undoManagerCouldUndoAtStart = undoManager.canUndo(); } /** * Specifies that the undo history should not be modified at the end of * the current receiving phase, if neither #receive nor #reportIrreversibleChange * is called. If none of the three methods are called during the receiving * phase, an irreversible change is assumed to have occurred, and the * undo history will be truncated. */ public void reportNothingToUndoYet() { Assert.isTrue(isReceiving()); setNothingToUndoReported(true); } /** * Notifies this UndoableEditReceiver that something non-undoable has * happened. Be sure to call this if an Exception occurs during the execution * of an UndoableCommand, leaving it partially complete and non-unexecutable. */ public void reportIrreversibleChange() { Assert.isTrue(isReceiving()); irreversibleChangeReported = true; } public void stopReceiving() { transactions--; try { if ((newUndoableEdits.isEmpty() && !wasNothingToUndoReported()) || irreversibleChangeReported) { undoManager.discardAllEdits(); return; } for (Iterator i = newUndoableEdits.iterator(); i.hasNext();) { UndoableEdit undoableEdit = (UndoableEdit) i.next(); undoManager.addEdit(undoableEdit); } newUndoableEdits.clear(); } finally { fireUndoHistoryChanged(); if (undoManagerCouldUndoAtStart && !undoManager.canUndo()) { fireUndoHistoryTruncated(); } } } private void fireUndoHistoryTruncated() { for (Iterator i = listeners.iterator(); i.hasNext();) { Listener listener = (Listener) i.next(); listener.undoHistoryTruncated(); } } private void fireUndoHistoryChanged() { for (Iterator i = listeners.iterator(); i.hasNext();) { Listener listener = (Listener) i.next(); listener.undoHistoryChanged(); } } public void add(Listener listener) { listeners.add(listener); } /** * If the currently executing PlugIn or AbstractCursorTool is not undoable, * it should simply not call this method; the undo history will be cleared. */ public void receive(UndoableEdit undoableEdit) { Assert.isTrue(isReceiving()); //Don't add the UndoableEdit to the UndoManager right away; the caller may //call #clearNewUndoableEdits. [Jon Aquino] newUndoableEdits.add(undoableEdit); } public UndoManager getUndoManager() { return undoManager; } private void setNothingToUndoReported(boolean nothingToUndoReported) { this.nothingToUndoReported = nothingToUndoReported; } private boolean wasNothingToUndoReported() { return nothingToUndoReported; } public static interface Listener { public void undoHistoryChanged(); public void undoHistoryTruncated(); } public boolean isReceiving() { return transactions > 0; } }