/*
* JAME 6.2.1
* http://jame.sourceforge.net
*
* Copyright 2001, 2016 Andrea Medeghini
*
* This file is part of JAME.
*
* JAME is an application for creating fractals and other graphics artifacts.
*
* JAME 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 3 of the License, or
* (at your option) any later version.
*
* JAME 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 JAME. If not, see <http://www.gnu.org/licenses/>.
*
*/
package net.sf.jame.twister;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sf.jame.core.DefaultTree;
import net.sf.jame.core.tree.DefaultNodeSession;
import net.sf.jame.core.tree.Node;
import net.sf.jame.core.tree.NodeAction;
import net.sf.jame.core.tree.NodePath;
import net.sf.jame.core.tree.Tree;
import net.sf.jame.core.util.RenderContext;
/**
* @author Andrea Medeghini
*/
public abstract class AbstractTwisterController implements TwisterController {
private static final Logger logger = Logger.getLogger(AbstractTwisterController.class.getName());
private final List<ControllerListener> listeners = new ArrayList<ControllerListener>();
protected List<ControllerCommand> commands = new ArrayList<ControllerCommand>();
protected List<ControllerAction> actions = new ArrayList<ControllerAction>();
private final Tree tree = new DefaultTree();
private TwisterConfig config;
protected int commandIndex = -1;
protected long clipDuration = 0;
protected long clipTimestamp = 0;
protected boolean redone = true;
protected boolean undone = false;
protected boolean needRefresh;
private RenderContext context;
/**
*
*/
public AbstractTwisterController() {
}
/**
* @see net.sf.jame.twister.TwisterController#setRenderContext(net.sf.jame.core.util.RenderContext)
*/
public void setRenderContext(final RenderContext context) {
this.context = context;
}
/**
* @param config
*/
protected void updateConfig(final TwisterConfig config) {
if (this.config == null) {
this.config = config;
final TwisterConfigNodeBuilder builder = new TwisterConfigNodeBuilder(config);
tree.getRootNode().removeAllChildNodes();
builder.createNodes(tree.getRootNode());
tree.getRootNode().setContext(config.getContext());
tree.getRootNode().setSession(new DefaultNodeSession("controller"));
}
else {
this.config.setFrameConfigElement(config.getFrameConfigElement().clone());
this.config.setEffectConfigElement(config.getEffectConfigElement().clone());
this.config.setBackground(config.getBackground());
}
fireConfigChanged();
}
private boolean isEmpty() {
return commands.size() == 0;
}
private ControllerCommand getCommand() {
if ((commandIndex > -1) && (commandIndex < commands.size())) {
return commands.get(commandIndex);
}
return null;
}
private ControllerCommand getNextCommand() {
if ((commandIndex >= -1) && (commandIndex < commands.size() - 1)) {
return commands.get(commandIndex + 1);
}
return null;
}
private ControllerCommand getPrevCommand() {
if ((commandIndex > 0) && (commandIndex <= commands.size() - 1)) {
return commands.get(commandIndex - 1);
}
return null;
}
private boolean isFirstIndex() {
return commandIndex == 0;
}
private boolean isLastIndex() {
return commandIndex == commands.size() - 1;
}
private boolean nextCommand() {
final int index = commandIndex + 1;
if (index < commands.size()) {
commandIndex = index;
redone = false;
undone = false;
return true;
}
return false;
}
private boolean prevCommand() {
final int index = commandIndex - 1;
if (index > -1) {
commandIndex = index;
redone = false;
undone = false;
return true;
}
return false;
}
private void redoCommand() {
if ((commandIndex > -1) && (commandIndex < commands.size())) {
commands.get(commandIndex).redo();
}
redone = true;
undone = false;
}
private void undoCommand() {
if ((commandIndex > -1) && (commandIndex < commands.size())) {
commands.get(commandIndex).undo();
}
undone = true;
redone = false;
}
private void redoAction(final NodeAction action) {
actions.add(new ControllerAction(false, action));
if (action.isRefreshRequired()) {
needRefresh = true;
}
}
private void undoAction(final NodeAction action) {
actions.add(new ControllerAction(true, action));
if (action.isRefreshRequired()) {
needRefresh = true;
}
}
private void execute() {
if (needRefresh && (context != null)) {
context.stopRenderers();
}
for (final ControllerAction action : actions) {
action.execute();
}
actions.clear();
doAccept();
if (needRefresh && (context != null)) {
context.startRenderers();
}
needRefresh = false;
}
private void executeRedoAction(final NodeAction action) {
final NodePath path = action.getActionTarget();
final Integer[] pe = path.getPathElements();
Node node = tree.getRootNode();
try {
for (final Integer element : pe) {
node = node.getChildNode(element);
}
if (AbstractTwisterController.logger.isLoggable(Level.FINER)) {
AbstractTwisterController.logger.finer(action.toString());
}
action.redo(node.getNodeEditor());
}
catch (Exception e) {
logger.log(Level.WARNING, path.toString(), e);
}
}
private void executeUndoAction(final NodeAction action) {
final NodePath path = action.getActionTarget();
final Integer[] pe = path.getPathElements();
Node node = tree.getRootNode();
try {
for (final Integer element : pe) {
node = node.getChildNode(element);
}
if (AbstractTwisterController.logger.isLoggable(Level.FINER)) {
AbstractTwisterController.logger.finer(action.toString());
}
action.undo(node.getNodeEditor());
}
catch (Exception e) {
logger.log(Level.WARNING, path.toString(), e);
}
}
/**
* @see net.sf.jame.twister.TwisterController#addControllerListener(net.sf.jame.twister.ControllerListener)
*/
public void addControllerListener(final ControllerListener listener) {
listeners.add(listener);
}
/**
* @see net.sf.jame.twister.TwisterController#removeControllerListener(net.sf.jame.twister.ControllerListener)
*/
public void removeControllerListener(final ControllerListener listener) {
listeners.remove(listener);
}
/**
*
*/
protected void fireConfigChanged() {
for (final ControllerListener listener : listeners) {
listener.configChanged();
}
}
/**
* @param action
*/
protected void fireActionUndone(final NodeAction action) {
for (final ControllerListener listener : listeners) {
listener.actionUndone(action);
}
}
/**
* @param action
*/
protected void fireActionRedone(final NodeAction action) {
for (final ControllerListener listener : listeners) {
listener.actionRedone(action);
}
}
/**
* @return
*/
protected int lastCommandIndex() {
return commands.size() - 1;
}
/**
*
*/
protected void doAccept() {
tree.getRootNode().accept();
}
/**
* @see net.sf.jame.twister.TwisterController#redoAction(boolean)
*/
public boolean redoAction(final boolean sameTimestamp) {
if (redo(sameTimestamp)) {
execute();
return true;
}
return false;
}
private boolean redo(final boolean sameTimestamp) {
// if (logger.isDebugEnabled()) {
// logger.debug(getStatus());
// }
if (isEmpty() || (isLastIndex() && redone)) {
return false;
}
if (!redone) {
redoCommand();
if (sameTimestamp) {
ControllerCommand action = getCommand();
if (action != null) {
final long timestamp = action.getTimestamp();
while ((action = getNextCommand()) != null) {
if (action.getTimestamp() != timestamp) {
break;
}
nextCommand();
redoCommand();
}
}
}
}
else {
if (nextCommand()) {
redoCommand();
if (sameTimestamp) {
ControllerCommand action = getCommand();
if (action != null) {
final long timestamp = action.getTimestamp();
while ((action = getNextCommand()) != null) {
if (action.getTimestamp() != timestamp) {
break;
}
nextCommand();
redoCommand();
}
}
}
}
}
if (isLastIndex() && redone) {
clipTimestamp = clipDuration;
}
else {
clipTimestamp = getCommand().getTimestamp();
}
return true;
}
/**
* @see net.sf.jame.twister.TwisterController#undoAction(boolean)
*/
public boolean undoAction(final boolean sameTimestamp) {
if (undo(sameTimestamp)) {
execute();
return true;
}
return false;
}
private boolean undo(final boolean sameTimestamp) {
// if (logger.isDebugEnabled()) {
// logger.debug(getStatus());
// }
if (isEmpty() || (isFirstIndex() && undone)) {
return false;
}
if (!undone) {
undoCommand();
if (sameTimestamp) {
ControllerCommand action = getCommand();
if (action != null) {
final long timestamp = action.getTimestamp();
while ((action = getPrevCommand()) != null) {
if (action.getTimestamp() != timestamp) {
break;
}
prevCommand();
undoCommand();
}
}
}
}
else {
if (prevCommand()) {
undoCommand();
if (sameTimestamp) {
ControllerCommand action = getCommand();
if (action != null) {
final long timestamp = action.getTimestamp();
while ((action = getPrevCommand()) != null) {
if (action.getTimestamp() != timestamp) {
break;
}
prevCommand();
undoCommand();
}
}
}
}
}
if (isFirstIndex() && undone) {
clipTimestamp = 0;
}
else {
clipTimestamp = getCommand().getTimestamp();
}
return true;
}
/**
* @see net.sf.jame.twister.TwisterController#redoAction(long, boolean)
*/
public boolean redoAction(final long timestamp, final boolean relative) {
// if (logger.isDebugEnabled()) {
// logger.debug(getStatus());
// }
if (isEmpty() || (isLastIndex() && redone)) {
return false;
}
if (relative) {
if (!redone) {
ControllerCommand action = getCommand();
if (action != null) {
if (timestamp >= action.getTimestamp() - clipTimestamp) {
redoCommand();
}
action = getNextCommand();
if (action != null) {
if (timestamp >= action.getTimestamp() - clipTimestamp) {
nextCommand();
redoCommand();
while ((action = getNextCommand()) != null) {
if (timestamp < action.getTimestamp() - clipTimestamp) {
break;
}
nextCommand();
redoCommand();
}
}
}
}
}
else {
ControllerCommand action = getNextCommand();
if (action != null) {
if (timestamp >= action.getTimestamp() - clipTimestamp) {
nextCommand();
redoCommand();
while ((action = getNextCommand()) != null) {
if (timestamp < action.getTimestamp() - clipTimestamp) {
break;
}
nextCommand();
redoCommand();
}
}
}
}
}
else {
if (!redone) {
ControllerCommand action = getCommand();
if (action != null) {
if (timestamp >= action.getTimestamp()) {
redoCommand();
}
action = getNextCommand();
if (action != null) {
if (timestamp >= action.getTimestamp()) {
nextCommand();
redoCommand();
while ((action = getNextCommand()) != null) {
if (timestamp < action.getTimestamp()) {
break;
}
nextCommand();
redoCommand();
}
}
}
}
}
else {
ControllerCommand action = getNextCommand();
if (action != null) {
if (timestamp >= action.getTimestamp()) {
nextCommand();
redoCommand();
while ((action = getNextCommand()) != null) {
if (timestamp < action.getTimestamp()) {
break;
}
nextCommand();
redoCommand();
}
}
}
}
}
if (isLastIndex() && redone) {
clipTimestamp = clipDuration;
}
else {
if (relative) {
clipTimestamp += timestamp;
}
else {
clipTimestamp = timestamp;
}
}
execute();
return true;
}
/**
* @see net.sf.jame.twister.TwisterController#undoAction(long, boolean)
*/
public boolean undoAction(final long timestamp, final boolean relative) {
// if (logger.isDebugEnabled()) {
// logger.debug(getStatus());
// }
if (isEmpty() || (isFirstIndex() && undone)) {
return false;
}
if (relative) {
if (!undone) {
ControllerCommand action = getCommand();
if (action != null) {
if (timestamp >= clipTimestamp - action.getTimestamp()) {
undoCommand();
}
action = getPrevCommand();
if (action != null) {
if (timestamp >= clipTimestamp - action.getTimestamp()) {
prevCommand();
undoCommand();
while ((action = getPrevCommand()) != null) {
if (timestamp < clipTimestamp - action.getTimestamp()) {
break;
}
prevCommand();
undoCommand();
}
}
}
}
}
else {
ControllerCommand action = getPrevCommand();
if (action != null) {
if (timestamp >= clipTimestamp - action.getTimestamp()) {
prevCommand();
undoCommand();
while ((action = getPrevCommand()) != null) {
if (timestamp < clipTimestamp - action.getTimestamp()) {
break;
}
prevCommand();
undoCommand();
}
}
}
}
}
else {
if (!undone) {
ControllerCommand action = getCommand();
if (action != null) {
if (timestamp <= action.getTimestamp()) {
undoCommand();
}
action = getPrevCommand();
if (action != null) {
if (timestamp <= action.getTimestamp()) {
prevCommand();
undoCommand();
while ((action = getPrevCommand()) != null) {
if (timestamp > action.getTimestamp()) {
break;
}
prevCommand();
undoCommand();
}
}
}
}
}
else {
ControllerCommand action = getPrevCommand();
if (action != null) {
if (timestamp <= action.getTimestamp()) {
prevCommand();
undoCommand();
while ((action = getPrevCommand()) != null) {
if (timestamp > action.getTimestamp()) {
break;
}
prevCommand();
undoCommand();
}
}
}
}
}
if (isFirstIndex() && undone) {
clipTimestamp = 0;
}
else {
if (relative) {
clipTimestamp -= timestamp;
}
else {
clipTimestamp = timestamp;
}
}
execute();
return true;
}
/**
* @see net.sf.jame.twister.TwisterController#redoActionAndSleep()
*/
public boolean redoActionAndSleep() {
// if (logger.isDebugEnabled()) {
// logger.debug(getStatus());
// }
if (isEmpty() || (isLastIndex() && redone)) {
return false;
}
long sleep = 0;
if (!redone) {
ControllerCommand action = getCommand();
if (action != null) {
long timestamp = action.getTimestamp();
if (timestamp > clipTimestamp) {
try {
Thread.sleep(timestamp - clipTimestamp);
}
catch (final InterruptedException e) {
Thread.currentThread().interrupt();
}
}
redoCommand();
clipTimestamp = timestamp;
while ((action = getNextCommand()) != null) {
if (action.getTimestamp() != clipTimestamp) {
break;
}
timestamp = action.getTimestamp();
if (timestamp > clipTimestamp) {
sleep += timestamp - clipTimestamp;
}
nextCommand();
redoCommand();
clipTimestamp = getCommand().getTimestamp();
}
}
}
else {
ControllerCommand action = getNextCommand();
if (action != null) {
long timestamp = action.getTimestamp();
if (timestamp > clipTimestamp) {
try {
Thread.sleep(timestamp - clipTimestamp);
}
catch (final InterruptedException e) {
Thread.currentThread().interrupt();
}
}
nextCommand();
redoCommand();
clipTimestamp = timestamp;
while ((action = getNextCommand()) != null) {
if (action.getTimestamp() != clipTimestamp) {
break;
}
timestamp = action.getTimestamp();
if (timestamp > clipTimestamp) {
sleep += timestamp - clipTimestamp;
}
nextCommand();
redoCommand();
clipTimestamp = getCommand().getTimestamp();
}
}
}
execute();
if (sleep > 0) {
try {
Thread.sleep(sleep);
}
catch (final InterruptedException e) {
Thread.currentThread().interrupt();
}
}
if (isLastIndex() && redone) {
try {
Thread.sleep(clipDuration - clipTimestamp);
}
catch (final InterruptedException e) {
Thread.currentThread().interrupt();
}
clipTimestamp = clipDuration;
}
return true;
}
/**
* @see net.sf.jame.twister.TwisterController#undoActionAndSleep()
*/
public boolean undoActionAndSleep() {
// if (logger.isDebugEnabled()) {
// logger.debug(getStatus());
// }
if (isEmpty() || (isFirstIndex() && undone)) {
return false;
}
long sleep = 0;
if (!undone) {
ControllerCommand action = getCommand();
if (action != null) {
long timestamp = action.getTimestamp();
if (timestamp < clipTimestamp) {
try {
Thread.sleep(clipTimestamp - timestamp);
}
catch (final InterruptedException e) {
Thread.currentThread().interrupt();
}
}
undoCommand();
clipTimestamp = timestamp;
while ((action = getPrevCommand()) != null) {
if (action.getTimestamp() != clipTimestamp) {
break;
}
timestamp = action.getTimestamp();
if (timestamp > clipTimestamp) {
sleep += clipTimestamp - timestamp;
}
prevCommand();
undoCommand();
clipTimestamp = getCommand().getTimestamp();
}
}
}
else {
ControllerCommand action = getPrevCommand();
if (action != null) {
long timestamp = action.getTimestamp();
if (timestamp < clipTimestamp) {
try {
Thread.sleep(clipTimestamp - timestamp);
}
catch (final InterruptedException e) {
Thread.currentThread().interrupt();
}
}
prevCommand();
undoCommand();
clipTimestamp = timestamp;
while ((action = getPrevCommand()) != null) {
if (action.getTimestamp() != clipTimestamp) {
break;
}
timestamp = action.getTimestamp();
if (timestamp < clipTimestamp) {
sleep += clipTimestamp - timestamp;
}
prevCommand();
undoCommand();
clipTimestamp = getCommand().getTimestamp();
}
}
}
execute();
if (sleep > 0) {
try {
Thread.sleep(sleep);
}
catch (final InterruptedException e) {
Thread.currentThread().interrupt();
}
}
if (isFirstIndex() && undone) {
try {
Thread.sleep(clipTimestamp);
}
catch (final InterruptedException e) {
Thread.currentThread().interrupt();
}
clipTimestamp = 0;
}
return true;
}
/**
* @see net.sf.jame.twister.TwisterController#undoAll()
*/
public boolean undoAll() {
while (undo(false)) {
}
execute();
clipTimestamp = 0;
return true;
}
/**
* @see net.sf.jame.twister.TwisterController#redoAll()
*/
public boolean redoAll() {
while (redo(false)) {
}
execute();
clipTimestamp = clipDuration;
return true;
}
/**
* @return the config
*/
public TwisterConfig getConfig() {
return config;
}
/**
* @see net.sf.jame.twister.TwisterController#getDuration()
*/
public long getDuration() {
return clipDuration;
}
/**
* @see net.sf.jame.twister.TwisterController#getTime()
*/
public long getTime() {
return clipTimestamp;
}
protected interface ControllerCommand {
public void undo();
public void redo();
public long getTimestamp();
}
protected class ActionCommand implements ControllerCommand {
private final NodeAction action;
public ActionCommand(final NodeAction action) {
this.action = action;
}
public void undo() {
undoAction(action);
fireActionUndone(action);
}
public void redo() {
redoAction(action);
fireActionRedone(action);
}
public long getTimestamp() {
return action.getTimestamp();
}
public NodeAction getAction() {
return action;
}
}
protected class LoadCommand implements ControllerCommand {
protected TwisterConfig config;
protected long timestamp;
public LoadCommand(final TwisterConfig config, final long timestamp) {
this.config = config;
this.timestamp = timestamp;
}
public void undo() {
}
public void redo() {
}
public long getTimestamp() {
return timestamp;
}
}
protected class NullCommand implements ControllerCommand {
protected long timestamp;
public NullCommand(final long timestamp) {
this.timestamp = timestamp;
}
public void undo() {
}
public void redo() {
}
public long getTimestamp() {
return timestamp;
}
}
private class ControllerAction {
private final boolean isUndo;
private final NodeAction action;
/**
* @param isUndo
* @param action
*/
protected ControllerAction(final boolean isUndo, final NodeAction action) {
this.isUndo = isUndo;
this.action = action;
}
/**
*
*/
public void execute() {
if (isUndo) {
executeUndoAction(action);
}
else {
executeRedoAction(action);
}
}
}
}