/**
 * Copyright 2010-2013 Jingjing Li.
 *
 * This file is part of jplot2d.
 *
 * jplot2d is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or any later version.
 *
 * jplot2d 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 Lesser Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with jplot2d. If not, see .
 */
package org.jplot2d.env;

/**
 * Maintains a queue of undo sets, a current set, and a redo sets.
 * 
 * @author Jingjing Li
 * 
 */
public class UndoManager {

	private static class Entry {
		E element;
		Entry next;
		Entry previous;

		Entry(E element, Entry next, Entry previous) {
			this.element = element;
			this.next = next;
			this.previous = previous;
		}
	}

	/**
	 * Change set SN.
	 */
	private int csn;

	private Entry header = new Entry(null, null, null);

	private int curIdx = -1;

	private Entry cur = header;

	/**
	 * The capacity of undo stack. Default is 0.
	 */
	private int capacity;

	/**
	 * Construct a ChangeHistory with capacity 0.
	 */
	public UndoManager() {
		header.next = header.previous = header;
	}

	/**
	 * Construct a ChangeHistory with the given capacity.
	 * 
	 * @param capacity
	 *            the capacity of the undo stack.
	 * @throws IllegalArgumentException
	 *             if the specified capacity is negative
	 */
	public UndoManager(int capacity) throws IllegalArgumentException {
		this();
		if (capacity < 0) {
			throw new IllegalArgumentException("Illegal Capacity: " + capacity);
		}
		this.capacity = capacity;
	}

	/**
	 * Returns the capacity of undo stack.
	 * 
	 * @return the capacity of undo stack
	 */
	public int getCapacity() {
		return capacity;
	}

	/**
	 * Sets the The capacity of undo stack. The default capacity is 0.
	 * 
	 * @param capacity
	 *            the capacity of undo stack
	 */
	public void setCapacity(int capacity) {
		if (capacity < 0) {
			throw new IllegalArgumentException("Illegal Capacity: " + capacity);
		}

		this.capacity = capacity;
	}

	/**
	 * Returns the possible undo steps. The size dose not count the current change set.
	 * 
	 * @return the possible undo steps
	 */
	public int getUndoSize() {
		return curIdx == -1 ? 0 : curIdx;
	}

	/**
	 * Return the current change set.
	 * 
	 * @return the current change set
	 */
	public E current() {
		return cur == header ? null : cur.element;
	}

	/**
	 * Returns the current CSN
	 * 
	 * @return the current CSN
	 */
	public int getCSN() {
		return csn;
	}

	/**
	 * Add the given change set as current, and push old change set into history.
	 * 
	 * @param cs
	 *            the change set
	 * @return the change SN
	 */
	public void add(E cs) {
		csn++;

		if (curIdx == 0 && capacity == 0) {
			cur.element = cs;
			return;
		}

		cur = new Entry(cs, header, cur);
		cur.previous.next = cur;
		cur.next.previous = cur;
		curIdx++;

		// remove the oldest change sets to satisfy capacity
		while (curIdx > capacity) {
			header.next = header.next.next;
			header.next.next.previous = header;
			curIdx--;
		}
	}

	/**
	 * Returns true if undo is possible.
	 * 
	 * @return true if undo is possible
	 */
	public boolean canUndo() {
		return cur != header && cur.previous != header;
	}

	/**
	 * Move the change set before the current set to current, and return it.
	 * 
	 * @return the previous change set
	 */
	public E undo() {
		if (canUndo()) {
			csn--;
			curIdx--;
			cur = cur.previous;
			return cur.element;
		}
		return null;
	}

	/**
	 * Returns true if redo is possible.
	 * 
	 * @return true if redo is possible
	 */
	public boolean canRedo() {
		return cur != null && cur.next != header;
	}

	/**
	 * Returns the next change set after the current change set. Returns null if there is no next change
	 * set.
	 * 
	 * @return the next change set
	 */
	public E redo() {
		if (canRedo()) {
			csn++;
			curIdx++;
			cur = cur.next;
			return cur.element;
		}
		return null;
	}

}

 

Ads help maintain this website.