/* * File : FillInBlanks.java * Created : 01-jun-2001 8:57 * 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.*; import edu.xtec.jclic.media.EventSounds; import edu.xtec.jclic.project.JClicProject; import edu.xtec.util.Actions; import edu.xtec.util.JDomUtility; import java.awt.event.*; import javax.swing.*; import javax.swing.text.*; /** * * @author Francesc Busquets (fbusquets@xtec.cat) * @version 13.09.16 */ public class FillInBlanks extends TextActivityBase { //boolean evalOnTheFly; protected boolean autoJump; protected boolean forceOkToAdvance; protected Evaluator ev; /** Creates new FillInBlanks */ public FillInBlanks(JClicProject project) { super(project); //evalOnTheFly=true; autoJump=false; forceOkToAdvance=false; ev=new ComplexEvaluator(project); } public static final String AUTO_JUMP="autoJump", FORCE_OK_TO_ADVANCE="forceOkToAdvance"; @Override public org.jdom.Element getJDomElement(){ org.jdom.Element e=super.getJDomElement(); if(autoJump) e.setAttribute(AUTO_JUMP, JDomUtility.boolString(autoJump)); if(forceOkToAdvance) e.setAttribute(FORCE_OK_TO_ADVANCE, JDomUtility.boolString(forceOkToAdvance)); e.addContent(ev.getJDomElement()); return e; } @Override public void setProperties(org.jdom.Element e, Object aux) throws Exception{ super.setProperties(e, aux); ev=Evaluator.getEvaluator(e.getChild(Evaluator.ELEMENT_NAME), project); //evalOnTheFly=!hasCheckButton; autoJump=JDomUtility.getBoolAttr(e, AUTO_JUMP, autoJump); forceOkToAdvance=JDomUtility.getBoolAttr(e, FORCE_OK_TO_ADVANCE, forceOkToAdvance); } @Override public void setProperties(edu.xtec.jclic.clic3.Clic3Activity c3a) throws Exception{ super.setProperties(c3a); ((ComplexEvaluator)ev).setProperties(c3a); forceOkToAdvance=c3a.okToNext; autoJump=!c3a.avNoSalta; hasCheckButton=!c3a.avCont; //evalOnTheFly=c3a.avCont; //if(evalOnTheFly) // hasCheckButton=false; } @Override public boolean needsKeyboard(){ return true; } @Override public Activity.Panel getActivityPanel(PlayStation ps) { return new Panel(ps); } class Panel extends TextActivityBase.Panel { boolean locked; TextActivityDocument playDoc=null; protected Panel(PlayStation ps){ super(ps); locked=true; } @Override protected void initDocument() throws Exception{ if(tad!=null){ playing=false; tad.tmb.setCurrentTarget(null, this); tad.tmb.reset(); playDoc=new TextActivityDocument(styleContext); tad.cloneDoc(playDoc, false, true, false); pane.setStyledDocument(playDoc); playDoc.attachTo(pane, FillInBlanks.Panel.this); tad.tmb.setParentPane(pane); pane.setEnabled(true); if(playDoc.tmb.size()>0){ pane.setEditable(true); pane.requestFocus(); pane.getCaret().setVisible(true); setCaretPos(0); locked=false; } else{ locked=true; pane.setEditable(false); pane.getCaret().setVisible(false); } } } @Override protected TextActivityPane buildPane(){ //FillInBlanksPane fp=new FillInBlanksPane(this); FillInBlanksPane fp=new FillInBlanksPane(); fp.setActions(); return fp; } class FillInBlanksPane extends TextActivityPane{ //public FillInBlanksPane(FillInBlanks.Panel fbp){ // super(fbp); //} protected FillInBlanksPane(){ super(FillInBlanks.Panel.this); } @Override public boolean processMouse(MouseEvent e){ if(super.processMouse(e) && e.getID()==MouseEvent.MOUSE_PRESSED && playing && !locked){ int p=this.viewToModel(e.getPoint()); if(p>=0) setCaretPos(p); } return false; } protected void removeChar(int offset) throws BadLocationException{ TargetMarker tm=playDoc.tmb.getCurrentTarget(); if(tm==null || !tm.contains(offset, false)){ playEvent(EventSounds.ACTION_ERROR); return; } boolean lastChar=(tm.getLength()==1); playDoc.remove(offset, 1); tm.target.setModified(true); if(lastChar){ playDoc.insertString(tm.begOffset, tm.target.getFillString(1), playDoc.getFillAttributeSet()); getCaret().setDot(tm.begOffset); } else{ tm.endOffset--; getCaret().setDot(offset); } tm.setPositions(); playDoc.tmb.updateOffsets(); } protected void removeCharCatch(int offset){ try{ removeChar(offset); } catch(BadLocationException ex){ System.err.println("Text activity error:\n"+ex); } } protected void insertChar(int offset, char ch) throws BadLocationException{ TargetMarker tm=playDoc.tmb.getCurrentTarget(); if(tm==null || !tm.contains(offset, true) || ch<0x20) return; int firstFill=getFirstFillChar(tm); if(firstFill>=0){ playDoc.remove(firstFill, 1); if(offset>firstFill) offset--; } else if(tm.getLength()>=tm.target.maxLenResp){ playEvent(EventSounds.ACTION_ERROR); return; } playDoc.insertString(offset, new String(new char[]{ch}), playDoc.getTargetAttributeSet()); tm.target.setModified(true); if(firstFill<0) tm.endOffset++; tm.setPositions(); playDoc.tmb.updateOffsets(); //if(autoJump && tm.getLength()==tm.target.maxLenResp){ if(autoJump && tm.getCurrentText(TextActivityDocument.FILL).length()>=tm.target.maxLenResp){ getCaret().setDot(goToTarget(playDoc.tmb.getNextTarget(tm), -1)); } else{ getCaret().setDot(offset+1); } } protected void insertCharCatch(int offset, char ch){ try{ insertChar(offset, ch); } catch(BadLocationException ex){ System.err.println("Text activity error:\n"+ex); } } protected int getFirstFillChar(TargetMarker tm){ for(int i=tm.begOffset; i<tm.endOffset; i++) if(playDoc.checkBooleanAttribute(i, TextActivityDocument.FILL)) return i; return -1; } boolean readyForActions(){ return playing && !locked && isEditable() && isEnabled(); } // Actions AbstractAction defaultKeyTypedAction=new AbstractAction(DefaultEditorKit.defaultKeyTypedAction){ public void actionPerformed(ActionEvent e){ if(readyForActions()){ String content = e.getActionCommand(); int mod = e.getModifiers(); if ((content != null) && (content.length() > 0) && ((mod & ActionEvent.ALT_MASK) == (mod & ActionEvent.CTRL_MASK))){ char c = content.charAt(0); if ((c >= 0x20) && (c != 0x7F)) { insertCharCatch(getCaret().getDot(), c); } } } } }; AbstractAction forwardAction=new AbstractAction(DefaultEditorKit.forwardAction){ public void actionPerformed(ActionEvent e){ if(readyForActions()){ int k=getCaret().getDot(); setCaretPos(k+1, k, true); } } }; AbstractAction backwardAction=new AbstractAction(DefaultEditorKit.backwardAction){ public void actionPerformed(ActionEvent e){ if(readyForActions()){ int k=getCaret().getDot(); setCaretPos(k-1, k, false); } } }; AbstractAction nextTargetAction=new AbstractAction("next-target"){ public void actionPerformed(ActionEvent e){ if(readyForActions()){ getCaret().setDot(goToTarget(playDoc.tmb.getNextTarget(null), -1)); } } }; AbstractAction prevTargetAction=new AbstractAction("prev-target"){ public void actionPerformed(ActionEvent e){ if(readyForActions()){ getCaret().setDot(goToTarget(playDoc.tmb.getPrevTarget(null), -1)); } } }; AbstractAction insertBreakAction=new AbstractAction(DefaultEditorKit.insertBreakAction){ public void actionPerformed(ActionEvent e){ insertTabAction.actionPerformed(e); } }; AbstractAction insertTabAction=new AbstractAction(DefaultEditorKit.insertTabAction){ public void actionPerformed(ActionEvent e){ if((e.getModifiers() & ActionEvent.SHIFT_MASK)!=0) prevTargetAction.actionPerformed(e); else nextTargetAction.actionPerformed(e); } }; AbstractAction deletePrevCharAction=new AbstractAction(DefaultEditorKit.deletePrevCharAction){ public void actionPerformed(ActionEvent e){ if(readyForActions()){ removeCharCatch(getCaret().getDot()-1); } } }; AbstractAction deleteNextCharAction=new AbstractAction(DefaultEditorKit.deleteNextCharAction){ public void actionPerformed(ActionEvent e){ if(readyForActions()){ removeCharCatch(getCaret().getDot()); } } }; AbstractAction beginWordAction=new AbstractAction(DefaultEditorKit.beginWordAction){ public void actionPerformed(ActionEvent e){ if(readyForActions()){ TargetMarker tm=playDoc.tmb.getCurrentTarget(); if(tm!=null) setCaretPos(tm.begOffset); } } }; AbstractAction endWordAction=new AbstractAction(DefaultEditorKit.endWordAction){ public void actionPerformed(ActionEvent e){ if(readyForActions()){ TargetMarker tm=playDoc.tmb.getCurrentTarget(); if(tm!=null) setCaretPos(tm.endOffset); } } }; AbstractAction beginAction=new AbstractAction(DefaultEditorKit.beginAction){ public void actionPerformed(ActionEvent e){ if(readyForActions()){ if((e.getModifiers() & ActionEvent.CTRL_MASK)!=0) setCaretPos(goToTarget(playDoc.tmb.getElement(0), -1)); else beginWordAction.actionPerformed(e); } } }; AbstractAction endAction=new AbstractAction(DefaultEditorKit.endAction){ public void actionPerformed(ActionEvent e){ if(readyForActions()){ if((e.getModifiers() & ActionEvent.CTRL_MASK)!=0) setCaretPos(goToTarget(playDoc.tmb.getElement(playDoc.tmb.size()-1), -1)); else endWordAction.actionPerformed(e); } } }; AbstractAction showHelpAction=new AbstractAction("show-help"){ public void actionPerformed(ActionEvent e){ if(readyForActions()){ TargetMarker tm=playDoc.tmb.getCurrentTarget(); if(tm!=null && tm.target!=null && tm.target.popupContent!=null && tm.target.infoMode==TextTarget.INFO_ON_DEMAND) tm.target.checkPopup(Panel.this, tm, true); } } }; Action kitUpAction=null; AbstractAction upAction=new AbstractAction(DefaultEditorKit.upAction){ public void actionPerformed(ActionEvent e){ if(kitUpAction!=null && readyForActions()){ int bkPos=getCaret().getDot(); kitUpAction.actionPerformed(e); int newPos=getCaret().getDot(); getCaret().setDot(bkPos); setCaretPos(newPos, bkPos, false); } } }; Action kitDownAction=null; AbstractAction downAction=new AbstractAction(DefaultEditorKit.downAction){ public void actionPerformed(ActionEvent e){ if(kitDownAction!=null && readyForActions()){ int bkPos=getCaret().getDot(); kitDownAction.actionPerformed(e); int newPos=getCaret().getDot(); getCaret().setDot(bkPos); setCaretPos(newPos, bkPos, true); } } }; protected void setActions(){ kitUpAction=getActionMap().get(DefaultEditorKit.upAction); kitDownAction=getActionMap().get(DefaultEditorKit.downAction); java.util.Map<String, Object[]> actionKeys=Actions.getActionKeys(this); //Actions.dumpActionKeys(actionKeys, getActionMap()); ActionMap am=new ActionMap(); am.setParent(getActionMap()); setActionMap(am); //Originals: Actions.mapTraceAction(this, actionKeys, DefaultEditorKit.beepAction); //Actions.mapTraceAction(this, actionKeys, DefaultEditorKit.readOnlyAction); //Actions.mapTraceAction(this, actionKeys, DefaultEditorKit.writableAction); Actions.mapTraceAction(this, actionKeys, "requestFocus"); Actions.mapTraceAction(this, actionKeys, "toggle-componentOrientation"); //Derivats: Actions.mapAction(this, actionKeys, defaultKeyTypedAction); Actions.mapAction(this, actionKeys, insertBreakAction); Actions.mapAction(this, actionKeys, insertTabAction); Actions.mapAction(this, actionKeys, deletePrevCharAction); Actions.mapAction(this, actionKeys, deleteNextCharAction); Actions.mapAction(this, actionKeys, forwardAction); Actions.mapAction(this, actionKeys, backwardAction); Actions.mapAction(this, actionKeys, beginWordAction); Actions.mapAction(this, actionKeys, endWordAction); Actions.mapAction(this, actionKeys, beginAction); Actions.mapAction(this, actionKeys, endAction); Actions.mapAction(this, actionKeys, upAction); Actions.mapAction(this, actionKeys, downAction); Actions.mapAction(this, actionKeys, showHelpAction); Actions.mapAction(this, actionKeys, nextTargetAction); Actions.mapAction(this, actionKeys, prevTargetAction); Actions.mapAction(this, actionKeys, nextTargetAction, DefaultEditorKit.nextWordAction); Actions.mapAction(this, actionKeys, prevTargetAction, DefaultEditorKit.previousWordAction); Actions.mapAction(this, actionKeys, nextTargetAction, DefaultEditorKit.endParagraphAction); Actions.mapAction(this, actionKeys, prevTargetAction, DefaultEditorKit.beginParagraphAction); Actions.mapAction(this, actionKeys, nextTargetAction, DefaultEditorKit.beginLineAction); Actions.mapAction(this, actionKeys, prevTargetAction, DefaultEditorKit.endLineAction); Actions.mapAction(this, actionKeys, nextTargetAction, DefaultEditorKit.pageDownAction); Actions.mapAction(this, actionKeys, prevTargetAction, DefaultEditorKit.pageUpAction); am.setParent(null); getInputMap().put(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_TAB, java.awt.Event.SHIFT_MASK), insertTabAction.getValue(Action.NAME)); getInputMap().put(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F1, 0), showHelpAction.getValue(Action.NAME)); } @Override protected void targetChanged(TextTarget tt){ if(readyForActions()){ TargetMarker tm=playDoc.tmb.getElement(tt); if(tm!=null){ TargetMarker currentTm=playDoc.tmb.getCurrentTarget(); goToTarget(tm, -1); //if(currentTm!=tm && evalOnTheFly){ if(currentTm!=tm && !hasCheckButton){ tm.target.setModified(true); goToTarget(tm, -1); } } } } } protected boolean evalTarget(TargetMarker tm){ if(tm.target==null) return false; String src=tm.getCurrentText(TextActivityDocument.FILL); if(src==null || src.length()<1) return false; byte[] result=ev.evalText(src, tm.target.answer); boolean ok=Evaluator.isOk(result); tm.target.targetStatus = ok ? TextTarget.SOLVED : TextTarget.WITH_ERROR; markTarget(tm, result); return ok; } protected void markTarget(TargetMarker tm, byte[] attributes){ if(tm.target.comboList!=null){ tm.target.comboList.checkColors(); tm.target.comboList.repaint(); return; } int bgTarget=tm.begPos.getOffset(); int endTarget=tm.endPos.getOffset(); int p=0; for(int i=bgTarget; i<endTarget && p<attributes.length; i++){ if(!playDoc.checkBooleanAttribute(i, TextActivityDocument.FILL)){ playDoc.setCharacterAttributes(i, 1, styleContext.getStyle(attributes[p]==Evaluator.FLAG_OK ? TextActivityDocument.TARGET : TextActivityDocument.TARGET_ERROR), false); p++; } } } protected void setCaretPos(int offset){ setCaretPos(offset, pane.getCaret().getDot(), true); } protected void setCaretPos(int offset, int currentOffset, boolean searchForward){ int check=checkCaretPos(offset, searchForward); if(check==-1) pane.setEditable(false); else if(check!=currentOffset) pane.setCaretPosition(check); } protected int checkCaretPos(int offset, boolean searchForward){ TargetMarkerBag tmb=playDoc.tmb; TargetMarker tm=tmb.getElementByOffset(offset, true); int p=offset; if(tm==null){ tm=tmb.getNearestElement(offset, searchForward); if(tm==null) tm=tmb.getNearestElement(offset, !searchForward); if(tm==null){ pane.setEditable(false); locked=true; return -1; } else{ if(searchForward) p=tm.begPos.getOffset(); else p=tm.endPos.getOffset(); } } if(tmb.getCurrentTarget()!=tm || tm.target.comboList!=null) p=goToTarget(tm, p); return p; } protected int goToTarget(TargetMarker tm, int offset){ int p=offset; TargetMarkerBag tmb=playDoc.tmb; //if(tmb.getCurrentTarget()!=null && evalOnTheFly){ if(tmb.getCurrentTarget()!=null && !hasCheckButton){ TargetMarker currentTm=tmb.getCurrentTarget(); if(!hasCheckButton && currentTm!=null && (currentTm.target.isModified() || forceOkToAdvance)){ //if(evalOnTheFly && currentTm!=null && (currentTm.target.isModified() || forceOkToAdvance)){ boolean ok=evalTarget(currentTm); if(!ok) currentTm.target.checkPopup(this, currentTm, false); int solvedTargets=tmb.countSolvedTargets(); ps.reportNewAction(getActivity(), ACTION_WRITE, currentTm.getCurrentText(TextActivityDocument.FILL), currentTm.target.getAnswers(), ok, solvedTargets); if(ok && solvedTargets==tmb.size()){ finishActivity(true); } else{ playEvent(ok ? EventSounds.ACTION_OK : EventSounds.ACTION_ERROR); } if(forceOkToAdvance && !ok){ tm=currentTm; tmb.setCurrentTarget(tm, this); return tm.begPos.getOffset(); } } } if(p<0) p=tm.begPos.getOffset(); if(!tm.contains(p, true)) p=tm.begOffset; tmb.setCurrentTarget(tm, this); return p; } @Override protected void doCheck(boolean fromButton){ if(playDoc==null || locked) return; TargetMarkerBag tmb=playDoc.tmb; //if(tmb.getCurrentTarget()!=null && evalOnTheFly==true){ if(tmb.getCurrentTarget()!=null && !hasCheckButton){ goToTarget(tmb.getCurrentTarget(), -1); return; } int solvedTargets=0; for(int i=0; i<tmb.size(); i++){ TargetMarker tm=tmb.getElement(i); if(tm!=null && tm.target.targetStatus!=TextTarget.NOT_EDITED){ boolean ok=evalTarget(tm); solvedTargets=tmb.countSolvedTargets(); ps.reportNewAction(getActivity(), ACTION_WRITE, tm.getCurrentText(TextActivityDocument.FILL), tm.target.getAnswers(), ok, solvedTargets); } } if(solvedTargets!=tmb.size()){ if(fromButton){ playEvent(EventSounds.FINISHED_ERROR); if(tmb.getCurrentTarget()!=null) goToTarget(tmb.getCurrentTarget(), -1); } } else finishActivity(true); } @Override public void finishActivity(boolean result){ pane.setEditable(false); pane.setEnabled(false); if(playDoc!=null && playDoc.tmb.getCurrentTarget()!=null); playDoc.tmb.getCurrentTarget().lostFocus(this); super.finishActivity(result); } } }