/*
* File : TextTarget.java
* Created : 31-may-2001 16:55
* 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.activities.text;
import edu.xtec.jclic.bags.MediaBag;
import edu.xtec.jclic.boxes.*;
import edu.xtec.jclic.media.MediaContent;
import edu.xtec.util.JDomUtility;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
/**
*
* @author Francesc Busquets (fbusquets@xtec.cat)
* @version 13.09.10
*/
public class TextTarget extends java.lang.Object{
//enum TIPUS_INCOGNITA {ESCRITA, LLISTA};
public static final int NO_INFO=0, INFO_ALWAYS=1, INFO_ON_ERROR=2, INFO_ON_DEMAND=3;
public static final String[] infoTypes={"no_info", "always", "onError", "onDemand"};
public static final int NOT_EDITED=0, EDITED=1, SOLVED=2, WITH_ERROR=3;
public static final int INFO_LEFTALIGN=1, INFO_ONLYPLAY=2;
public boolean isList;
public int numIniChars;
public char iniChar;
public int maxLenResp;
public String[] answer;
public String[] options;
public String iniText;
public int infoMode;
public ActiveBoxContent popupContent;
public int popupDelay;
public int popupMaxTime;
public boolean onlyPlay;
// TRANSIENT PROPERTIES
protected TargetCombo comboList;
public int targetStatus;
private boolean flagModified;
TextActivityPane parentPane;
/** Creates new TextTarget */
public TextTarget() {
isList=false;
numIniChars=1;
iniChar='_';
maxLenResp=0;
answer=null;
options=null;
iniText=null;
infoMode=NO_INFO;
popupContent=null;
popupDelay=0;
popupMaxTime=0;
onlyPlay=false;
comboList=null;
targetStatus=NOT_EDITED;
flagModified=false;
}
public TextTarget(Document doc, int x0, int x1){
this();
if(x1<x0){
int v=x0;
x0=x1;
x1=v;
}
String s;
try{
s=doc.getText(x0, x1-x0);
} catch(BadLocationException ex){
// should not occur!
s="";
}
numIniChars=s.length();
answer=new String[]{s};
maxLenResp=numIniChars;
}
public void reset(){
targetStatus=NOT_EDITED;
flagModified=false;
if(comboList!=null)
comboList.checkColors();
}
public static final String ELEMENT_NAME="target";
public static final String ANSWER="answer",
OPTION_LIST="optionList", OPTION="option",
RESPONSE="response", FILL="fill", INI_LEN="length", MAX_LEN="maxLength", INI_TEXT="show",
INFO="info", MODE="mode", DELAY="delay", MAX_TIME="maxTime";
public org.jdom.Element getJDomElement(){
org.jdom.Element e=new org.jdom.Element(ELEMENT_NAME);
org.jdom.Element child;
if(answer!=null){
for(String ans : answer)
e.addContent((new org.jdom.Element(ANSWER)).addContent(ans));
}
if(isList){
child=new org.jdom.Element(OPTION_LIST);
if(options!=null){
for(String opt : options)
child.addContent((new org.jdom.Element(OPTION)).addContent(opt));
}
e.addContent(child);
} else{
child=new org.jdom.Element(RESPONSE);
child.setAttribute(FILL, new String(new char[]{iniChar}));
child.setAttribute(INI_LEN, Integer.toString(numIniChars));
child.setAttribute(MAX_LEN, Integer.toString(maxLenResp));
if(iniText!=null && iniText.length()>0) child.setAttribute(INI_TEXT, iniText);
e.addContent(child);
}
if(popupContent!=null && infoMode!=NO_INFO){
child=new org.jdom.Element(INFO);
if(infoMode!=INFO_ALWAYS) child.setAttribute(MODE, infoTypes[infoMode]);
if(popupDelay>0) child.setAttribute(DELAY, Integer.toString(popupDelay));
if(popupMaxTime>0) child.setAttribute(MAX_TIME, Integer.toString(popupMaxTime));
if(onlyPlay && popupContent.mediaContent!=null){
child.addContent(popupContent.mediaContent.getJDomElement());
} else{
child.addContent(popupContent.getJDomElement());
}
e.addContent(child);
}
return e;
}
public static TextTarget getTextTarget(org.jdom.Element e, MediaBag mediaBag) throws Exception{
TextTarget tt=new TextTarget();
tt.setProperties(e, mediaBag);
return tt;
}
public void setProperties(org.jdom.Element e, Object aux) throws Exception{
JDomUtility.checkName(e, ELEMENT_NAME);
MediaBag mediaBag=(MediaBag)aux;
org.jdom.Element child, child2;
java.util.List answerLst=e.getChildren(ANSWER);
if(!answerLst.isEmpty()){
answer=new String[answerLst.size()];
for(int i=0; i<answer.length; i++)
answer[i]=((org.jdom.Element)(answerLst.get(i))).getText();
}
if((child=e.getChild(OPTION_LIST))!=null){
java.util.List optionLst=child.getChildren(OPTION);
if(!optionLst.isEmpty()){
isList=true;
options=new String[optionLst.size()];
for(int i=0; i<options.length; i++)
options[i]=((org.jdom.Element)(optionLst.get(i))).getText();
}
}
if((child=e.getChild(RESPONSE))!=null){
String s=new String(new char[]{iniChar});
s=JDomUtility.getStringAttr(child, FILL, s, true);
if(s.length()>0) iniChar=s.charAt(0);
numIniChars=JDomUtility.getIntAttr(child, INI_LEN, numIniChars);
maxLenResp=JDomUtility.getIntAttr(child, MAX_LEN, maxLenResp);
iniText=child.getAttributeValue(INI_TEXT);
}
if((child=e.getChild(INFO))!=null){
infoMode=JDomUtility.getStrIndexAttr(child, MODE, infoTypes, INFO_ALWAYS);
popupDelay=JDomUtility.getIntAttr(child, DELAY, popupDelay);
popupMaxTime=JDomUtility.getIntAttr(child, MAX_TIME, popupMaxTime);
if((child2=child.getChild(MediaContent.ELEMENT_NAME))!=null){
onlyPlay=true;
popupContent=new ActiveBoxContent();
popupContent.mediaContent=MediaContent.getMediaContent(child2);
}
else if((child2=child.getChild(ActiveBoxContent.ELEMENT_NAME))!=null){
popupContent=ActiveBoxContent.getActiveBoxContent(child2, mediaBag);
}
}
}
protected void setParentPane(TextActivityPane pane){
parentPane=pane;
}
public void setAnswer(String text){
java.util.StringTokenizer st=new java.util.StringTokenizer(text, "|");
int numItems=st.countTokens();
if(numItems<=0){
answer=null;
} else{
answer=new String[numItems];
for(int i=0; i<numItems; i++){
answer[i]=st.nextToken();
}
}
}
public String getAnswers(){
StringBuilder sb=new StringBuilder();
if(answer!=null)
for(int i=0; i<answer.length; i++){
if(i>0)
sb.append("|");
sb.append(answer[i]);
}
return sb.substring(0);
}
public boolean checkText(String txt, Evaluator ev){
if(answer==null || txt==null)
return false;
boolean ok=ev.checkText(txt, answer);
targetStatus=ok ? SOLVED : WITH_ERROR;
return ok;
}
public JComboBox buildCombo(AttributeSet targetAttr, AttributeSet errorAttr){
if(!isList || options==null) return null;
comboList=new TargetCombo(options, targetAttr, errorAttr);
if(iniText!=null)
comboList.setSelectedItem(iniText);
return comboList;
}
public String getFillString(){
return getFillString(numIniChars);
}
public String getFillString(int length){
StringBuilder s=new StringBuilder();
for(int i=0; i<length; i++){
s.append(iniChar);
}
return s.substring(0);
}
public void requestFocus(TextActivityBase.Panel tabp, TargetMarker tm){
if(comboList!=null){
SwingUtilities.invokeLater(new Runnable(){
public void run(){
comboList.requestFocus();
//comboList.setPopupVisible(true);
}
});
//if(tab instanceof FillInBlanks)
// parent=(FillInBlanks.FillInBlanksPane)tab.pane;
}
else
tabp.requestFocus();
if(popupContent!=null && (infoMode==INFO_ALWAYS || (infoMode==INFO_ON_ERROR && targetStatus==WITH_ERROR)))
tabp.popupTimer.setUp(this, tm);
}
public void checkPopup(TextActivityBase.Panel tabp, TargetMarker tm, boolean f1Key){
if(popupContent!=null){
boolean show=false;
if(infoMode==INFO_ON_ERROR)
show=(targetStatus==WITH_ERROR);
else if(infoMode==INFO_ON_DEMAND)
show=f1Key;
if(show)
tabp.popupTimer.setUp(this, tm);
else
tabp.popupTimer.stopPopup();
}
}
protected Point getPopupLocation(TextActivityBase.Panel tabp, TargetMarker tm){
if(popupContent==null || onlyPlay)
return null;
Rectangle r=tm.getBegRect(tabp.pane);
if(r==null)
return null;
Point pt=r.getLocation();
pt.y-=popupContent.dimension.height;
if(pt.y<0)
pt.y=r.y+r.height;
if(pt.x+popupContent.dimension.width > tabp.pane.getWidth()){
pt.x-=(tabp.pane.getWidth()-pt.x+popupContent.dimension.width);
if(pt.x<0)
pt.x=0;
}
return pt;
}
public void adjustPopupLocation(TextActivityBase.Panel tabp, TargetMarker tm){
Point pt=getPopupLocation(tabp, tm);
if(pt!=null)
tabp.pane.bx.setLocation(pt);
}
public void lostFocus(TextActivityBase.Panel tabp, TargetMarker tm){
if(comboList!=null && parentPane!=null){
parentPane.repaint();
}
if(popupContent!=null)
tabp.popupTimer.stopPopup();
}
public class TargetCombo extends JComboBox{
Color bgColor, foreColor;
Color errBgColor, errForeColor;
Font rFont;
TargetComboCellRenderer cellRenderer;
JTextField txEditor;
AbstractAction showListAction, fwdAction, bkAction;
public TargetCombo(String[] items, AttributeSet targetAttr, AttributeSet errorAttr){
super(items);
setSelectedIndex(-1);
showListAction=new AbstractAction("showList"){
public void actionPerformed(ActionEvent e){
setPopupVisible(true);
}
};
fwdAction=new AbstractAction("forward"){
public void actionPerformed(ActionEvent e){
if(parentPane!=null){
Action act=parentPane.getActionMap().get("next-target");
if(act!=null)
act.actionPerformed(e);
}
}
};
bkAction=new AbstractAction("backwards"){
public void actionPerformed(ActionEvent e){
if(parentPane!=null){
Action act=parentPane.getActionMap().get("prev-target");
if(act!=null)
act.actionPerformed(e);
}
}
};
rFont=TextActivityDocument.attributesToFont(targetAttr);
setFont(rFont);
bgColor=StyleConstants.getBackground(targetAttr);
foreColor=StyleConstants.getForeground(targetAttr);
errBgColor=StyleConstants.getBackground(errorAttr);
errForeColor=StyleConstants.getForeground(errorAttr);
setCursor(Cursor.getDefaultCursor());
cellRenderer=new edu.xtec.jclic.activities.text.TextTarget.TargetCombo.TargetComboCellRenderer();
setRenderer(cellRenderer);
Dimension d=getPreferredSize();
setPreferredSize(new Dimension((int)d.getWidth()+rFont.getSize()/2, (int)d.getHeight()));
setMaximumSize(getPreferredSize());
setEditable(true);
Component c=getEditor().getEditorComponent();
if(c instanceof JTextField)
txEditor=(JTextField)c;
else
txEditor=new JTextField();
txEditor.setEnabled(false);
checkColors();
setAlignmentY(0.8f);
setKActions();
}
protected void setKActions(){
registerKeyboardAction(showListAction, KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), JComponent.WHEN_FOCUSED);
registerKeyboardAction(showListAction, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), JComponent.WHEN_FOCUSED);
txEditor.registerKeyboardAction(showListAction, KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), JComponent.WHEN_FOCUSED);
txEditor.registerKeyboardAction(showListAction, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), JComponent.WHEN_FOCUSED);
registerKeyboardAction(fwdAction, KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
registerKeyboardAction(fwdAction, KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
registerKeyboardAction(fwdAction, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
registerKeyboardAction(bkAction, KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
registerKeyboardAction(bkAction, KeyStroke.getKeyStroke(KeyEvent.VK_TAB, Event.SHIFT_MASK), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
}
public void checkColors(){
if(targetStatus==WITH_ERROR){
txEditor.setBackground(errBgColor);
txEditor.setForeground(errForeColor);
txEditor.setDisabledTextColor(errForeColor);
}
else{
txEditor.setBackground(bgColor);
txEditor.setForeground(foreColor);
txEditor.setDisabledTextColor(foreColor);
}
}
class TargetComboCellRenderer extends JLabel implements ListCellRenderer {
public TargetComboCellRenderer() {
setOpaque(true);
setFont(rFont);
}
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus){
setBackground(isSelected ? foreColor : bgColor);
setForeground(isSelected ? bgColor : foreColor);
setText(value==null ? "" : value.toString());
return this;
}
}
@Override
public void setSelectedItem(Object o){
super.setSelectedItem(o);
if(o!=null && parentPane!=null){
setModified(true);
SwingUtilities.invokeLater(new Runnable(){
public void run(){
if(!isPopupVisible()){
parentPane.targetChanged(TextTarget.this);
}
}
});
}
}
}
public void setModified(boolean value){
flagModified=value;
if(value==true && targetStatus==NOT_EDITED)
targetStatus=EDITED;
}
public boolean isModified(){
return flagModified;
}
public static class PopupTimer extends javax.swing.Timer{
TextTarget target;
boolean action;
TextActivityBase.Panel tabp;
TargetMarker tm;
public PopupTimer(TextActivityBase.Panel tabp){
super(1000, null);
this.tabp=tabp;
setRepeats(false);
target=null;
}
public void setUp(TextTarget setTarget, TargetMarker setTm){
stop();
target=setTarget;
tm=setTm;
action=true;
if(target!=null && target.popupContent!=null){
SwingUtilities.invokeLater(new Runnable(){
public void run(){
if(target.popupDelay>0){
//setDelay(target.popupDelay*1000);
setInitialDelay(target.popupDelay*1000);
setRepeats(false);
start();
}
else
startPopup();
}});
}
}
public void startPopup(){
stop();
if(target!=null && target.popupContent!=null){
if(!target.onlyPlay){
Point pt=target.getPopupLocation(tabp, tm);
if(pt!=null)
tabp.pane.enableActiveBox(target.popupContent, pt);
}
if(target.popupContent.mediaContent!=null)
tabp.ps.playMedia(target.popupContent.mediaContent, tabp.pane.bx);
if(target.popupMaxTime>0){
action=false;
//setDelay(target.popupMaxTime*1000);
setInitialDelay(target.popupMaxTime*1000);
setRepeats(false);
start();
}
}
}
public void stopPopup(){
stop();
if(target!=null && target.popupContent!=null){
tabp.ps.stopMedia(1);
tabp.pane.disableActiveBox();
}
}
@Override
protected void fireActionPerformed(java.awt.event.ActionEvent e){
if(action==true)
startPopup();
else
stopPopup();
}
}
}