/*
* File : PlayerHistory.java
* Created : 11-jan-2002 15:31
* By : fbusquets
*
* JClic - Authoring and playing system for educational activities
*
* Copyright (C) 2000 - 2005 Francesc Busquets & Departament
* d'Educacio de la Generalitat de Catalunya
*
* 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 (see the LICENSE file).
*/
package edu.xtec.jclic;
import edu.xtec.jclic.bags.ActivitySequence;
import edu.xtec.jclic.bags.ActivitySequenceElement;
import edu.xtec.jclic.bags.JumpInfo;
import java.util.Stack;
/**
* PlayerHistory uses a {@link java.util.Stack} object to store the list of projects
* and activities done by the user. This class allows {@link edu.xtec.jclic.Player}
* objects to rewind a sequence or go back to a caller menu.
* @author Francesc Busquets (fbusquets@xtec.cat)
* @version 13.09.10
*/
public class PlayerHistory {
/**
* The <CODE>Player</CODE> this <CODE>PlayerHistory</CODE> belongs to
*/
protected Player player;
/**
* This is the main member of the class. <CODE>PlayerHistory</CODE> puts and retrieves
* on it information about the proects and activities done by the current user.
*/
protected Stack<HistoryElement> sequenceStack;
/**
* When in test mode (for instance, in the <CODE>Player</CODE> used by JClic author
* to preview activities), jumps are only simulated.
*/
protected boolean testMode;
/**
* Creates a new PlayerHistory
* @param player The <CODE>Player</CODE> this <CODE>PlayerHistory</CODE> belongs to
*/
public PlayerHistory(Player player) {
this.player=player;
sequenceStack=new Stack<HistoryElement>();
}
/**
* Counts the number of history elements stored in the stack
* @return The number of stored elements
*/
public int storedElementsCount(){
return sequenceStack.size();
}
/**
* Removes all the elements from the history stack
*/
public void clearHistory(){
sequenceStack.clear();
}
/**
* <CODE>PlayerHistory</CODE> uses this inner class to store history elements.
*/
protected class HistoryElement{
String projectPath;
String sequence;
int activity;
HistoryElement(String path, String seq, int act){
projectPath=path;
sequence=seq;
activity=act;
}
}
/**
* Adds the current <CODE>Player</CODE>'s project and activity to the top of the
* history stack.
*/
public void push(){
if(player.project!=null && player.project.getFullPath()!=null){
ActivitySequence ase=player.project.activitySequence;
int act=ase.getCurrentActNum();
if(act>=0){
if(!sequenceStack.isEmpty()){
HistoryElement last=sequenceStack.peek();
if(last.projectPath.equals(player.project.getFullPath()) && last.activity==act)
return;
}
sequenceStack.push(new HistoryElement(player.project.getFullPath(), ase.getSequenceForElement(act), act));
}
}
}
/**
* Retrieves the history element placed at the top of the stack (if any) and makes
* the <CODE>Player</CODE> to load it. The obtained effect is to "rewind" or "go back",
* usually to a caller menu or activity.
* @return The current implementation of this method always returns <CODE>true</CODE>.
*/
public boolean pop(){
// todo: check return value
if(!sequenceStack.isEmpty()){
HistoryElement e=sequenceStack.pop();
if(e.projectPath.equals(player.project.getFullPath()))
player.load(null, Integer.toString(e.activity), null, null);
else{
if(testMode && e.projectPath!=null && e.projectPath.length()>0){
player.getMessages().showAlert(player, new String[]{
player.getMsg("test_alert_jump_to"), e.projectPath});
}
else{
player.load(e.projectPath, Integer.toString(e.activity), null, null);
}
}
}
return true;
}
/**
* Processes the provided <CODE>JumpInfo</CODE> object, instructing the <CODE>Player</CODE>
* to go back, stop or jump to another point in the sequence.
* @param ji The <CODE>JumpInfo</CODE> object to be processed
* @param allowReturn When this param is <CODE>true</CODE>, the jump instructed by the <CODE>JumpInfo</CODE>
* (if any) will be recorded, in order to make possible to go back to the current activity.
* @return <CODE>true</CODE> if the jump can be processed without errors. <CODE>false</CODE> otherwise.
*/
public boolean processJump(JumpInfo ji, boolean allowReturn){
boolean result=false;
if(ji!=null && player.project!=null){
switch(ji.action){
case JumpInfo.STOP:
break;
case JumpInfo.RETURN:
result=pop();
break;
case JumpInfo.EXIT:
if(testMode){
player.getMessages().showAlert(player, "test_alert_exit");
}
else
player.exit(ji.sequence);
break;
case JumpInfo.JUMP:
if(ji.sequence==null && ji.projectPath==null){
ActivitySequenceElement ase=player.project.activitySequence.getElement(ji.actNum, true);
if(ase!=null){
if(allowReturn)
push();
//jcp.activitySequence.setCurrentAct(ji.actNum);
//result=loadActivity(ase.getActivityName());
player.load(null, null, ase.getActivityName(), null);
result=true;
}
}
else{
if(testMode && ji.projectPath!=null && ji.projectPath.length()>0){
player.getMessages().showAlert(player, new String[]{
player.getMsg("test_alert_jump_to"),
ji.projectPath});
}
else
result=jumpToSequence(ji.sequence, ji.projectPath, allowReturn);
}
break;
}
}
return result;
}
private boolean jumpToSequence(String sequence, String path, boolean allowReturn){
if(sequence==null && path==null)
return false;
if(path==null)
path=player.project.getFullPath();
if(!sequenceStack.isEmpty()){
HistoryElement e=sequenceStack.peek();
if(sequence!=null && path.equals(e.projectPath)){
boolean same=sequence.equals(e.sequence);
if(path.equals(player.project.getFullPath())){
ActivitySequenceElement ase=player.project.activitySequence.getElement(e.activity, false);
same=(ase!=null && sequence.equals(ase.getTag()));
}
if(same)
return pop();
}
}
if(allowReturn)
push();
if(path.equals(player.project.getFullPath()))
player.load(null, sequence, null, null);
else
player.load(path, sequence, null, null);
return true;
}
/**
* Getter for property testMode.
* @return Value of property testMode.
*/
public boolean isTestMode() {
return testMode;
}
/**
* Setter for property testMode.
* @param testMode New value of property testMode.
*/
public void setTestMode(boolean testMode) {
this.testMode = testMode;
}
}