UndoableActionManager.java
/**
* VStar: a statistical analysis tool for variable star data.
* Copyright (C) 2010 AAVSO (http://www.aavso.org/)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.aavso.tools.vstar.ui.undo;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import org.aavso.tools.vstar.data.ValidObservation;
import org.aavso.tools.vstar.ui.mediator.Mediator;
import org.aavso.tools.vstar.ui.mediator.message.MultipleObservationSelectionMessage;
import org.aavso.tools.vstar.ui.mediator.message.NewStarMessage;
import org.aavso.tools.vstar.ui.mediator.message.ObservationSelectionMessage;
import org.aavso.tools.vstar.ui.mediator.message.ProgressInfo;
import org.aavso.tools.vstar.ui.mediator.message.UndoActionMessage;
import org.aavso.tools.vstar.ui.mediator.message.UndoableActionType;
import org.aavso.tools.vstar.ui.task.UndoableActionTask;
import org.aavso.tools.vstar.util.notification.Listener;
/**
* This class manages actions on behalf of other components.
*/
public class UndoableActionManager {
private Set<ValidObservation> selectedObs;
private Stack<IUndoableAction> undoStack;
private Stack<IUndoableAction> redoStack;
/**
* Constructor.
*/
public UndoableActionManager() {
selectedObs = new HashSet<ValidObservation>();
undoStack = new Stack<IUndoableAction>();
redoStack = new Stack<IUndoableAction>();
}
/**
* Add an action to the undo or redo stack and notify listeners of this.
*
* @param action
* The undoable action.
* @param type
* The type of action (undo/redo).
*/
public void addAction(IUndoableAction action, UndoableActionType type) {
if (type == UndoableActionType.UNDO) {
undoStack.add(action);
} else if (type == UndoableActionType.REDO) {
redoStack.add(action);
}
UndoActionMessage msg = new UndoActionMessage(this, action, type);
Mediator.getInstance().getUndoActionNotifier().notifyListeners(msg);
}
/**
* Clear pending undo/redo actions for actions with the specified display
* string.
*
* @param displayString
* The action's display string.
*/
public void clearPendingAction(String displayString) {
if (!isUndoStackEmpty()) {
if (undoStack.peek().getDisplayString().equals(displayString)) {
undoStack.pop();
}
}
if (!isRedoStackEmpty()) {
if (redoStack.peek().getDisplayString().equals(displayString)) {
redoStack.pop();
}
}
}
/**
* Is the undo stack empty?
*
* @return Whether or not the undo stack is empty.
*/
public boolean isUndoStackEmpty() {
return undoStack.isEmpty();
}
/**
* Is the redo stack empty?
*
* @return Whether or not the redo stack is empty.
*/
public boolean isRedoStackEmpty() {
return redoStack.isEmpty();
}
/**
* Execute an undo action if one exists.
*/
public void executeUndoAction() {
if (!undoStack.isEmpty()) {
performUndoableAction(undoStack.pop(), UndoableActionType.UNDO);
}
}
/**
* Execute a redo action if one exists.
*/
public void executeRedoAction() {
if (!redoStack.isEmpty()) {
performUndoableAction(redoStack.pop(), UndoableActionType.REDO);
}
}
/**
* Start an asynchronous task to execute an undoable action.
*
* @param action
* The undoable action.
* @param type
* The undoable action type (undo/redo).
*/
public UndoableActionTask performUndoableAction(IUndoableAction action,
UndoableActionType type) {
UndoableActionTask task = new UndoableActionTask(action, type);
Mediator.getInstance().getProgressNotifier()
.notifyListeners(ProgressInfo.START_PROGRESS);
Mediator.getInstance().getProgressNotifier()
.notifyListeners(ProgressInfo.BUSY_PROGRESS);
task.execute();
return task;
}
// ** Specific actions and related methods. **
/**
* Exclude the currently selected observation(s).
*/
public void excludeCurrentSelection() {
if (!selectedObs.isEmpty()) {
// Create an undoable exclusion action.
List<ValidObservation> undoObs = new ArrayList<ValidObservation>();
undoObs.addAll(selectedObs);
ObservationExclusionAction action = new ObservationExclusionAction(
undoObs, true);
// Perform the exclusion action, then add its opposite to the undo
// stack.
action.execute(UndoableActionType.DO);
addAction(action, UndoableActionType.UNDO);
// Now that we have excluded the selected observations,
// clear the collection.
selectedObs.clear();
}
}
// Returns a new star listener.
public Listener<NewStarMessage> createNewStarListener() {
return new Listener<NewStarMessage>() {
@Override
public void update(NewStarMessage info) {
selectedObs.clear();
undoStack.clear();
redoStack.clear();
}
@Override
public boolean canBeRemoved() {
return false;
}
};
}
// Returns a multiple observation selection listener that collects
// observations that have been selected.
public Listener<MultipleObservationSelectionMessage> createMultipleObservationSelectionListener() {
return new Listener<MultipleObservationSelectionMessage>() {
@Override
public void update(MultipleObservationSelectionMessage info) {
selectedObs.clear();
selectedObs.addAll(info.getObservations());
}
@Override
public boolean canBeRemoved() {
return false;
}
};
}
// Returns an observation selection listener that collects
// the single observation that has been selected.
public Listener<ObservationSelectionMessage> createObservationSelectionListener() {
return new Listener<ObservationSelectionMessage>() {
@Override
public void update(ObservationSelectionMessage info) {
selectedObs.clear();
selectedObs.add(info.getObservation());
}
@Override
public boolean canBeRemoved() {
return false;
}
};
}
}