import java.awt.*; import java.awt.event.*; import java.lang.*; import java.util.Vector; import java.applet.*; /** Copyright (c) 2001 by rowland. Permission is granted to use and to distribute according to the terms of the GNU Lesser GPL, the text of which may be found here: http://www.gnu.org/copyleft/lesser.txt http://www.angelfire.com/ego/rowland/lesser.txt **/ /** This class encapsulates a test question, together with its possible answers, and the card and other AWT controls needed to display the question. **/ class mcquestion { String qtext; String atext[]; boolean acorrect[]; int ca, maxa; Checkbox cb[]; Panel card; /* for the answer checkboxes */ boolean singlechoice; mcquestion(String _qtext, int _maxa, boolean _singlechoice) { qtext= _qtext; atext= new String[_maxa]; acorrect= new boolean[_maxa]; singlechoice= _singlechoice; ca= 0; maxa= _maxa; card= new Panel(); } /** Add an answer **/ public void answer(String _atext, boolean _acorrect) { if (ca>= maxa) return; atext[ca]= _atext; acorrect[ca]= _acorrect; ca++; } /** set component to a decent font, one way or another **/ public static void setMyFont(Component c) { Font of= c.getFont(); if (of!= null) { String fontfam= of.getFamily(); c.setFont(new Font(fontfam, Font.BOLD, 12)); return; } Font nf= new Font("Helvetica", Font.BOLD, 12); if (nf!= null) c.setFont(nf); } /** fill out the AWT controls in the card **/ void populate() { CheckboxGroup cbgrp= null; if (singlechoice) cbgrp= new CheckboxGroup(); // I wasn't having much luck with TextArea, so... int clines= 0; Vector vlines= new Vector(); String stmp= new String(qtext); while (clines< 100) { // divvy qtext up into lines. int i= stmp.indexOf('\n'); if (i>= 0) { String sline= stmp.substring(0, i); vlines.addElement(sline); clines++; stmp= stmp.substring(i+1); } else { // last line vlines.addElement(stmp); clines++; break; } } int gridh= clines+ maxa+1; if (gridh< 8) gridh= 8; card.setLayout(new GridLayout(gridh, 1)); String sline; int cdlines= clines; while (cdlines-- > 0) { sline= (String)vlines.firstElement(); if (sline== null) break; Label l= new Label(sline); setMyFont(l); card.add(l); vlines.removeElementAt(0); } // checkboxes for the answers... cb = new Checkbox[maxa]; for (int i= 0; i< maxa; i++) { if (singlechoice) cb[i]= new Checkbox(atext[i], cbgrp, false); else cb[i]= new Checkbox(atext[i]); setMyFont(cb[i]); card.add(cb[i]); } } } /** This is the base class for a multiple-choice test. To use this, create a derived class that overrides the loadtest() method. For an example, see http://www.angelfire.com/ego/rowland/mytest.java **/ class multchoice extends Applet implements ActionListener { Panel pcb; Button be, bb; mcquestion q[]; int numq= 0; int curq= 0; TextArea tscore; // where the results are displayed. final static int MAXQUESTIONS= 100; void reset() { for (int i=0; i< numq; i++) { // go through the questions mcquestion _q= q[i]; if (_q== null) continue; for (int ia= 0; ia< _q.maxa; ia++) { // go through the answers Checkbox cb= _q.cb[ia]; if (cb== null) continue; cb.setState(false); } } curq= 0; CardLayout cl= (CardLayout)pcb.getLayout(); cl.first(pcb); } /** create and display a card that shows the results of the test **/ void score() { int citems= 0, ccorrect= 0; String sscore= "Results:\n\n"; for (int i=0; i< numq; i++) { // go through the questions mcquestion _q= q[i]; if (_q== null) continue; boolean answered= false; for (int ia= 0; ia< _q.maxa; ia++) { // go through the answers Checkbox cb= _q.cb[ia]; if (cb== null) continue; boolean ga= cb.getState(); boolean ca= _q.acorrect[ia]; if (_q.singlechoice) { // only one correct answer if (ga) { // the user selected this one answered= true; if (ca!= ga) { // incorrect answer String scold= new String("Wrong: "); scold+= _q.qtext+ ":\n "; scold+= _q.atext[ia]+ "\n"; sscore+= scold; scold= null; } else ccorrect++; break; } } else { // multiple correct answers citems++; if (ca!= ga) { // incorrect answer String scold= new String("Wrong: "); scold+= _q.qtext+ ":\n "; scold+= _q.atext[ia]+ "\n"; sscore+= scold; scold= null; } else ccorrect++; } } if (_q.singlechoice) { citems++; if (!answered) { String scold= new String("Skipped: "); scold+= _q.qtext+ ":\n"; sscore+= scold; scold= null; } } } String sscore1= new String("Score: "+ ccorrect); sscore1+= " / "+ citems; if (citems> 0) { sscore1+= " "; float fpercent= 100.0f* ccorrect; fpercent/= citems; Float Fpercent= new Float(fpercent); sscore1+= Fpercent.toString(); sscore1+= "%\n\n"; } if (tscore== null) { tscore= new TextArea(); mcquestion.setMyFont(tscore); } tscore.setText(sscore1+ sscore); CardLayout cl= (CardLayout)pcb.getLayout(); pcb.add("score", tscore); cl.last(pcb); bb.setLabel("Reset"); pcb.validate(); // needed to refresh screen } /** Utility function for use by the loadtest() implementation **/ public void addquestion(mcquestion _q) { if (numq>= MAXQUESTIONS) return; _q.populate(); pcb.add(_q.qtext, _q.card); q[numq]= _q; numq++; } public void loadtest() { } public void init() { setLayout(new BorderLayout()); setBackground(new Color(1.0f, 1.0f, 0.8f)); pcb= new Panel(); pcb.setLayout(new CardLayout()); add(pcb, BorderLayout.CENTER); Panel buttons= new Panel(); buttons.setLayout(new GridLayout(1, 2)); bb= new Button("Back"); bb.addActionListener(this); buttons.add(bb); be= new Button("Enter"); be.addActionListener(this); buttons.add(be); add(buttons, BorderLayout.SOUTH); CardLayout cl= (CardLayout)pcb.getLayout(); cl.first(pcb); pcb.validate(); // needed to refresh screen loadtest(); validate(); } /** constructor **/ public multchoice() { } public void actionPerformed(ActionEvent evt) { Object source= evt.getSource(); CardLayout cl= (CardLayout)pcb.getLayout(); if (source== be) { curq++; if (curq> numq) System.exit(0); if (curq== numq) score(); else { cl.next(pcb); pcb.validate(); // needed to refresh screen } } else if (source== bb) { if (curq>= numq) { // already finished. //return; reset(); } if (curq> 0) { cl.previous(pcb); pcb.validate(); // needed to refresh screen curq--; } } } }