/** @author Scott Marshall @author University of Glasgow, MSc IT Project 2001 @author Building an OnLine Course in Computing Fundamentals */ package ukacgla_BinaryConverter; import java.util.*; import javax.swing.*; import javax.swing.text.*; import javax.swing.border.*; import javax.swing.table.*; import javax.swing.table.TableColumn; import javax.swing.table.AbstractTableModel; import java.awt.*; import java.awt.event.*; import javax.swing.event.*; import javax.swing.event.TableModelEvent; /** ConverterFrame holds the GUI components. */ public class ConverterFrame extends JFrame{ private Container contentPane; private JButton clear,reveal, submit, hint, nextNumber; private JTextArea instruction; private JPanel master; //contains all GUI components private int noOfCols; //number of columns /** Holds the table column headers */ private String[] columnNames; /** Holds the students solution to the problem. */ private MyTableModel answerModel; /** Holds data used by the student for rough working.*/ private MyTableModel workingModel; /** Holds the data presented by the program.*/ private MyTableModel outputModel; /** Number to be converted. */ private int question; /** Base used for calculation */ private int base; /** Power used for calculation */ private int power; /** Checks student answer vs correct answer */ private Checker checker; /** Random number generator */ private Random rand; boolean pressed; /** Constructor notes : Parameters for the test are set as : noOfCols (number of columns) ie n columns for powers, plus one for labelling */ public ConverterFrame(int bases, int powers, JApplet applet){ //set the paramaters for the test. base = bases; noOfCols = powers+1; power = powers-1; contentPane = applet.getContentPane(); columnNames = new String[noOfCols]; //Must begin j at n-1 (ie an 8 bit number will go from 2 power 7 down to 2 power 0. int j = noOfCols-1; for (int i = 0 ; i < noOfCols ; i++ ){ if(i==0) columnNames[i] = new String("Term"); else columnNames[i] = new String (base +" power "+j) ; j--; } rand = new Random(); contentPane.add(mkUI()); contentPane.validate(); } /** Resets all table models to clear. Generates a new random number and sets this number as the exercise to the student. The range of the random number is [0... (base x power)-1] */ protected void nextNumber() { pressed = false; answerModel.tableReset(answerModel); workingModel.tableReset(workingModel); outputModel.tableReset(outputModel); int max = (checker.calcPower(base, power+1))-1; question = rand.nextInt(max); instruction.setText("Convert " + question + " to base " + base); } /** Builds User Interface on a Container Returns Container to be placed onto the required contentPane. */ public Container mkUI() { //set to Windows look and feel try { UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); } catch (Exception e) { } master = new JPanel(new BorderLayout(20,20)); pressed = false; //----------- NUMBER TO BE DISPLAYED --------------------- JPanel mid1 = new JPanel(new BorderLayout(10,10)); mid1.setBorder(new BevelBorder(BevelBorder.LOWERED)); instruction = mkTextArea(2, 40, false); question = rand.nextInt(256); instruction.setText("Convert " + question + " to base " + base); JLabel lab1 = mkLabel("Number to be converted", Font.PLAIN, 10); mid1.add("North", lab1); mid1.add("Center", instruction); master.add("North", mid1); //-----------makeTables --------------------- JPanel masterTable = new JPanel(new BorderLayout(10,10)); JPanel tablePanel1 = new JPanel(new BorderLayout(10,10)); JPanel labelPanel = new JPanel(new BorderLayout(0,0)); JLabel instruction1 = mkLabel("For rough working only. Use this table to calculate powers.", Font.PLAIN, 10); JLabel instruction2 = mkLabel("Entries in this table will not be used to check any part of answer. ", Font.PLAIN, 10); labelPanel.add("North", instruction1); labelPanel.add("Center", instruction2); JScrollPane jsp1 = mkTable(false, workingModel = new MyTableModel(columnNames,1,noOfCols, true), "Use table for rough working only"); workingModel.setValueAt(new String("Workings only: "),0,0); tablePanel1.add("North",labelPanel); tablePanel1.add("Center",jsp1); JPanel tablePanel2 = new JPanel(new BorderLayout(10,10)); JLabel lab3 = mkLabel("Enter answers into this table row. ", Font.PLAIN, 10); JScrollPane jsp2 = mkTable(true, answerModel = new MyTableModel(columnNames,1,noOfCols, true), "Enter answers here"); answerModel.setValueAt(new String ("Enter here: "),0,0); //tableListener = new MyTMListener(); //answerModel.addTableModelListener(new MyTMListener()); tablePanel2.add("North",lab3); tablePanel2.add("Center",jsp2); JPanel tablePanel3 = new JPanel(new BorderLayout(10,10)); JLabel lab4 = mkLabel("Program output will appear in this table ", Font.PLAIN, 10); JScrollPane jsp3 = mkTable(false, outputModel = new MyTableModel(columnNames,2,noOfCols, false), "Program output will appear here"); outputModel.setValueAt(new String ("Output: "),0,0); outputModel.setValueAt(new String ("Output: "),1,0); tablePanel3.add("North",lab4); tablePanel3.add("Center",jsp3); masterTable.add("North",tablePanel1); masterTable.add("Center",tablePanel2); masterTable.add("South",tablePanel3); master.add("Center", masterTable); //make panel, make JButtons and add to frame JPanel mid4 = new JPanel(new BorderLayout(20,20)); JPanel buttonPanel = new JPanel( new FlowLayout() ); buttonPanel.setBorder(new BevelBorder(BevelBorder.LOWERED)); clear = mkButton("Reset", buttonPanel); clear.setToolTipText("Clears entries in all tables"); hint = mkButton("Hint", buttonPanel); hint.setToolTipText("Displays information to help solve problem"); reveal = mkButton("Reveal", buttonPanel); reveal.setToolTipText("Displays correct answer"); submit = mkButton("Submit", buttonPanel); submit.setToolTipText("Submit answer for checking"); nextNumber = mkButton("Next Number", buttonPanel); nextNumber.setToolTipText("Generates new random number"); mid4.add("Center", buttonPanel); mid4.add("South", new JLabel("")); //filler to allow room for tool tips to be displayed master.add("South", mid4); //generate marker and calculate and store correct answer into a string. checker = new Checker(this); return master; } //------------------------- TABLE BUILDING METHODS ---------------------------- /** Generates a new JTable, and places it inside a JScrollPane, before returning the JScrollPane. The model must be constructed separately, and supplied as an argument. */ private JScrollPane mkTable(boolean integerEditor, MyTableModel model, String ttp) { JTable table = new JTable(model); table.setPreferredScrollableViewportSize(new Dimension(700, 50)); table.setCellSelectionEnabled(true); table.setRowSelectionAllowed(false); table.setColumnSelectionAllowed(false); table.setRowHeight(25); table.setToolTipText(ttp); if (integerEditor == true) setUpIntegerEditor(table); TableColumn column = null; for (int i = 0; i < noOfCols; i++) { column = table.getColumnModel().getColumn(i); if (i==0) column.setMinWidth(80); else column.setPreferredWidth(40); column.setResizable(false); } JScrollPane scrollPane = new JScrollPane(table); return scrollPane; } /** Code modified from Sun Microsystems website. Ensures only String 0's or 1's are entered into table Strings must be used to allow the initial display for certain cells to be blank. Use of Integers requires an initial value of 0 to be displayed */ protected void setUpIntegerEditor(JTable table) { //Set up the editor for the table cells. final WholeNumberField tableField = new WholeNumberField(new String("0"), noOfCols); tableField.setHorizontalAlignment(WholeNumberField.LEFT); DefaultCellEditor integerEditor = new DefaultCellEditor(tableField) { public Object getCellEditorValue() { String answer = ""; try { answer = tableField.getValue(); } catch (InvalidEntryException x) { makeDialog("Input Error",x.getString(),JOptionPane.ERROR_MESSAGE,JOptionPane.DEFAULT_OPTION); } return answer; } }; table.setDefaultEditor(String.class, integerEditor); } //------------------------- OTHER GUI BUILDING METHODS ---------------------------- /** Returns JTextArea of required size. */ private JTextArea mkTextArea(int rows, int cols, boolean editable){ JTextArea temp = new JTextArea(rows, cols); temp.setFont(new Font("arial", Font.PLAIN, 12)); temp.setLineWrap(true); temp.setEditable(editable); temp.setBackground(Color.white); Border border = new EtchedBorder(EtchedBorder.RAISED); temp.setBorder(border); return temp; } /** Returns JButton of standard size 120 x 25. */ private JButton mkButton(String nm,Container c) { JButton b = new JButton(nm); b.setPreferredSize(new Dimension(120,25)); b.addActionListener(new ButtonListener()); c.add(b); return b; } /** Returns JLabel using "arial" font. User can specify Font style and size. */ private JLabel mkLabel(String nm, int style, int size) { JLabel l = new JLabel(nm); l.setFont(new Font("arial",style , size)); l.setOpaque(true); return l; } /** Creates a JOptionPane with user specified message. */ protected void makeDialog(String title,String message,int messageType,int optionType){ JOptionPane optionPane = new JOptionPane(message,messageType,optionType); JDialog dialog = optionPane.createDialog(contentPane,title); dialog.show(); } //------------------------- TABLE MANIPULATION METHODS ---------------------------- /** Displays in the outPut model two rows to demonstrate the answer. The first row is the power calculations. The second row is the binary representation of the number. */ protected void displayAnswer(){ StringBuffer answer = new StringBuffer(checker.calcAnswer()); int i = 0; int tempPower = power; int tempBase = base; //must set the column to be i+1 to skip the label column while (i < answer.length()) { //set the first line to display the calculation int sum = checker.calcPower(tempBase,tempPower); outputModel.setValueAt(new String(sum + " "), 0, (i+1)); tempPower--; //set the 2nd line to display the correct binary output Character character = new Character(answer.charAt(i)); outputModel.setValueAt(character, 1, (i+1)); i++; } } /** Displays the power calculations in the first row of the outPut model as a hint. */ private void displayClue(){ int i = 0; int tempPower = power; int tempBase = base; while (i < noOfCols-1) { //set the first line to display the calculation int sum = checker.calcPower(tempBase,tempPower); outputModel.setValueAt(new String(sum + " "), 0, (i+1)); tempPower--; i++; } } /** Tests if all cells are complete. If complete, actions depend on the boolean returned by checker.verifyAnswer() Then displays JOption pane with feedback to user. */ private void submit(){ if (testCellsComplete(answerModel) == false){ if (checker.verifyAnswer()==true) makeDialog("Results checked.", "All answers correct", JOptionPane.INFORMATION_MESSAGE, JOptionPane.DEFAULT_OPTION); else makeDialog("Results checked.", "One or more errors found" , JOptionPane.ERROR_MESSAGE, JOptionPane.DEFAULT_OPTION); } } /** Checks each cell in table for cells that have not been completed. WholeNumberField prevents incorrect entries, only entries can be 0,1 or This method detects strings. */ private boolean testCellsComplete(MyTableModel m) { String value; boolean missing = false; //check first row only. try { for (int i=0; i<1 ; i++){ for(int j=1; j