/* ******************************************************************************
* Copyright (c) 2006-2012 XMind Ltd. and others.
*
* This file is a part of XMind 3. XMind releases 3 and
* above are dual-licensed under the Eclipse Public License (EPL),
* which is available at http://www.eclipse.org/legal/epl-v10.html
* and the GNU Lesser General Public License (LGPL),
* which is available at http://www.gnu.org/licenses/lgpl.html
* See http://www.xmind.net/license.html for details.
*
* Contributors:
* XMind Ltd. - initial API and implementation
*******************************************************************************/
package org.xmind.gef.command;
import static org.xmind.gef.GEF.CS_POST_EXECUTE;
import static org.xmind.gef.GEF.CS_POST_REDO;
import static org.xmind.gef.GEF.CS_POST_UNDO;
import static org.xmind.gef.GEF.CS_PRE_EXECUTE;
import static org.xmind.gef.GEF.CS_PRE_REDO;
import static org.xmind.gef.GEF.CS_PRE_UNDO;
import java.util.ArrayList;
import java.util.List;
import org.xmind.gef.GEF;
/**
* @author Brian Sun
*/
public class CommandStack extends CommandStackBase
implements ICommandStack2, ICommandStack3 {
private class DelegateListener implements ICommandStackListener {
public void handleCommandStackEvent(CommandStackEvent event) {
fireEvent(event);
}
}
protected List<Command> commandList;
private int currentLocation = -1;
private int saveLocation = -1;
private boolean alwaysDirty = false;
private ICommandStackDelegate delegate = null;
private DelegateListener delegateListener = null;
private boolean inCompoundCommand = false;
private Command compoundCommand = null;
public CommandStack() {
this(DEFAULT_UNDO_LIMIT);
}
/**
* @param undoLimit
*/
public CommandStack(int undoLimit) {
super(undoLimit);
commandList = new ArrayList<Command>(undoLimit);
}
public void startCompoundCommand() {
if (delegate instanceof ICommandStack3) {
((ICommandStack3) delegate).startCompoundCommand();
return;
}
inCompoundCommand = true;
}
public void endCompoundCommand() {
if (delegate instanceof ICommandStack3) {
((ICommandStack3) delegate).endCompoundCommand();
return;
}
inCompoundCommand = false;
if (compoundCommand != null) {
Command command = compoundCommand;
compoundCommand = null;
postExecute(command);
}
}
public void execute(Command command) {
if (delegate != null && delegate.canExecute(command)) {
delegate.execute(command);
return;
}
if (command == null || !command.canExecute())
return;
fireEvent(command, CS_PRE_EXECUTE);
beginTransaction();
command.execute();
if (inCompoundCommand) {
if (compoundCommand == null) {
compoundCommand = command;
return;
} else {
endCompoundCommand();
}
}
endTransaction(command, CS_PRE_EXECUTE);
postExecute(command);
}
private void postExecute(Command command) {
fireEvent(command, CS_POST_EXECUTE);
if (command.canUndo()) {
pushCommand(command);
}
fireEvent(null, GEF.CS_UPDATED);
}
private void pushCommand(Command cmd) {
discardRedoables();
commandList.add(cmd);
if (saveLocation > currentLocation)
saveLocation = -1;
currentLocation++;
fireEvent(cmd, GEF.CS_COMMAND_PUSHED);
if (getUndoLimit() > 0 && currentLocation >= getUndoLimit()) {
Command discarded = commandList.remove(0);
if (discarded != null) {
discarded.dispose();
}
currentLocation--;
if (saveLocation >= 0)
saveLocation--;
alwaysDirty = true;
}
}
private void discardRedoables() {
while (commandList.size() - 1 > currentLocation) {
Command discarded = commandList.remove(commandList.size() - 1);
if (discarded != null) {
discarded.dispose();
}
}
}
/**
* @return the commandList
*/
public List<Command> getCommandList() {
return commandList;
}
public boolean canUndo() {
if (delegate != null)
return delegate.canUndo();
return currentLocation >= 0;
}
public void undo() {
if (delegate != null && delegate.canUndo()) {
delegate.undo();
return;
}
Command undoCmd = commandList.get(currentLocation);
if (undoCmd.canUndo()) {
beginTransaction();
fireEvent(undoCmd, CS_PRE_UNDO);
undoCmd.undo();
endTransaction(undoCmd, CS_PRE_UNDO);
currentLocation--;
fireEvent(undoCmd, CS_POST_UNDO);
} else {
currentLocation--;
}
fireEvent(null, GEF.CS_UPDATED);
}
public void undo(boolean discard) {
undo();
if (discard) {
discardRedoables();
}
}
public boolean canRedo() {
if (delegate != null)
return delegate.canRedo();
return currentLocation < commandList.size() - 1;
}
public void redo() {
if (delegate != null && delegate.canRedo()) {
delegate.redo();
return;
}
Command redoCmd = commandList.get(currentLocation + 1);
if (redoCmd.canExecute()) {
fireEvent(redoCmd, CS_PRE_REDO);
beginTransaction();
redoCmd.redo();
endTransaction(redoCmd, CS_PRE_REDO);
currentLocation++;
fireEvent(redoCmd, CS_POST_REDO);
} else {
currentLocation++;
}
fireEvent(null, GEF.CS_UPDATED);
}
// /**
// * @deprecated
// */
// public String getRepeatLabel() {
// if (delegate != null)
// return delegate.getRepeatLabel();
// return super.getRepeatLabel();
// }
//
// /**
// * @deprecated
// */
// public void repeat() {
// if (delegate != null) {
// delegate.repeat();
// return;
// }
// super.repeat();
//// execute( commandList.get( currentLocation ).clone() );
// }
public boolean isDirty() {
if (delegate != null)
return delegate.isDirty();
return saveLocation != currentLocation || alwaysDirty;
}
public void markSaved() {
// if (delegate != null) {
// delegate.markSaved();
// return;
// }
saveLocation = currentLocation;
alwaysDirty = false;
}
public void clear() {
// if (delegate != null) {
// delegate.clear();
// return;
// }
currentLocation = -1;
saveLocation = -1;
for (Command c : commandList)
c.dispose();
commandList.clear();
fireEvent(null, GEF.CS_UPDATED);
}
// /**
// * @see org.xmind.gef.command.ICommandStack#canRepeat()
// * @deprecated
// */
// public boolean canRepeat() {
// if (delegate != null)
// return delegate.canRepeat();
// return super.canRepeat();
//// return currentLocation >= 0
//// && commandList.get( currentLocation ).canRepeat();
// }
/**
* @see org.xmind.gef.command.ICommandStack#getRedoLabel()
*/
public String getRedoLabel() {
if (delegate != null && delegate.canRedo())
return delegate.getRedoLabel();
return commandList.get(currentLocation + 1).getLabel();
}
/**
* @see org.xmind.gef.command.ICommandStack#getUndoLabel()
*/
public String getUndoLabel() {
if (delegate != null && delegate.canUndo())
return delegate.getUndoLabel();
return commandList.get(currentLocation).getLabel();
}
public void setUndoLimit(int undoLimit) {
// if (delegate != null) {
// delegate.setUndoLimit(undoLimit);
// return;
// }
super.setUndoLimit(undoLimit);
while (undoLimit > 0 && commandList.size() > undoLimit) {
deleteFirst();
}
fireEvent(null, GEF.CS_UPDATED);
}
private void deleteFirst() {
if (commandList.size() > 0) {
Command cmd = commandList.remove(0);
if (cmd != null) {
cmd.dispose();
if (saveLocation >= 0)
saveLocation--;
if (currentLocation >= 0)
currentLocation--;
alwaysDirty = true;
}
}
}
// public int getUndoLimit() {
// if (delegate != null)
// return delegate.getUndoLimit();
// return super.getUndoLimit();
// }
public void setDelegate(ICommandStackDelegate delegate) {
if (delegate == this.delegate)
return;
if (this.delegate != null) {
if (delegateListener != null) {
this.delegate.removeCSListener(delegateListener);
}
}
this.delegate = delegate;
if (delegate != null) {
if (delegateListener == null)
delegateListener = new DelegateListener();
delegate.addCSListener(delegateListener);
} else {
delegateListener = null;
}
fireEvent(null, GEF.CS_UPDATED);
}
public ICommandStackDelegate getDelegate() {
return delegate;
}
public void dispose() {
setDelegate(null);
super.dispose();
}
}