/* * =========================================================================== * @(#)SpreadSheet.java 1.5 96/12/06 * * Copyright (c) 1994-1996 Sun Microsystems, Inc. All Rights Reserved. * * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use, * modify and redistribute this software in source and binary code form, * provided that i) this copyright notice and license appear on all copies of * the software; and ii) Licensee does not utilize the software in a manner * which is disparaging to Sun. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * * This software is not designed or intended for use in on-line control of * aircraft, air traffic, aircraft navigation or aircraft communications; or in * the design, construction, operation or maintenance of any nuclear * facility. Licensee represents and warrants that it will not use or * redistribute the Software for such purposes. * * Modified by David Chancogne (i.e., bugs fixed) 1996-1997 * =========================================================================== */ import java.applet.*; import java.awt.*; import java.io.*; import java.net.*; import java.util.*; public class spreadsheet extends Applet { Font inputFont; Color cellColor; Color inputColor; int cellWidth; int offset = 45; int cellHeight = 25; int rowLabelWidth = 20; int rows; int columns; int selectedRow = -1; int selectedColumn = -1; InputField inputArea; Cell cells[][]; Cell current = null; Button clear_sheet; Random gaus = new Random(); public synchronized void init() { String rs; cellColor = Color.white; inputColor = new Color(100, 100, 225); inputFont = new Font("Courier", Font.BOLD, 12); rs = getParameter("rows"); if (rs == null) rows = 9; else rows = Integer.parseInt(rs); if(rows < 1) rows = 9; if(99 < rows) rows = 99; rs = getParameter("cols"); if (rs == null) columns = 5; else columns = Integer.parseInt(rs); if(columns < 1) columns = 5; if(26 < columns) columns = 26; cellWidth = (size().width - rowLabelWidth) / columns; cells = new Cell[rows][columns]; char l[] = new char[1]; for(int i = 0; i < rows; i++) for(int j = 0; j < columns; j++) { cells[i][j] = new Cell(this, Color.lightGray, Color.black, cellColor, cellWidth - 2, cellHeight - 2); l[0] = (char)((int)'a' + j); rs = getParameter("" + new String(l) + (i+1)); if(rs != null) cells[i][j].setValue(rs); } inputArea = new InputField(null, this, columns * cellWidth + rowLabelWidth, cellHeight - 1, inputColor, Color.white); Button clear = new Button("Clear"); setLayout(new FlowLayout(FlowLayout.LEFT)); add(clear); resize(columns * cellWidth + rowLabelWidth, ((rows + 1) * cellHeight) + cellHeight + offset); } public void stop() { } public void start() { } public void destroy() { } public void setCurrentValue(String val) { if(selectedRow < 0 || rows <= selectedRow || selectedColumn < 0 || columns <= selectedColumn) return; cells[selectedRow][selectedColumn].setValue(val); repaint(); } public void update(Graphics g) { int i, j; int cx, cy; char l[] = new char[1]; g.setColor(inputColor); g.setFont(inputFont); g.fillRect(0, offset, columns * cellWidth + rowLabelWidth, cellHeight); for(i = 0; i < rows + 1; i++) { cy = (i + 2) * cellHeight + offset; g.setColor(getBackground()); g.draw3DRect(0, cy, columns * cellWidth + rowLabelWidth, 1, true); if(0 < i) { g.setColor(Color.red); g.drawString("" + i, 2, cy - 3); } } for(i = 0; i < columns + 1; i++) { cx = i * cellWidth; g.setColor(getBackground()); g.draw3DRect(cx + rowLabelWidth, cellHeight + offset, 1, (rows + 1) * cellHeight, true); if(i < columns) { g.setColor(Color.red); l[0] = (char)((int)'a' + i); g.drawString(new String(l), cx + rowLabelWidth + (cellWidth / 2), 2 * cellHeight - 3 + offset); } } for(i = 0; i < rows; i++) for(j = 0; j < columns; j++) { cx = (j * cellWidth) + 2 + rowLabelWidth; cy = ((i+2) * cellHeight) + 2 + offset; if(cells[i][j] != null && cells[i][j].needRedisplay) cells[i][j].paint(g, cx, cy); } if(inputArea != null) inputArea.paint(g, 1, 1 + offset); } public void recalculate() { for(int i = 0; i < rows; i++) for(int j = 0; j < columns; j++) if(cells[i][j] != null && cells[i][j].type == Cell.FORMULA) { cells[i][j].setRawValue(evaluateFormula(cells[i][j].parseRoot)); cells[i][j].needRedisplay = true; } repaint(); } public double evaluateFormula(Node n) { double val = 0.0; Node temp; if(n == null) return val; switch (n.type) { case Node.OP: case Node.PRE: val = evaluateFormula(n.left); switch (n.op) { case '+': val += evaluateFormula(n.right); break; case '*': val *= evaluateFormula(n.right); break; case '-': temp = n.right; while(temp != null && temp.type == Node.OP && temp.op == '-') { val -= evaluateFormula(temp.left); temp = temp.right; } if(temp != null && temp.type == Node.OP && temp.op == '+') { val -= evaluateFormula(temp.left); val += evaluateFormula(temp.right); } else val -= evaluateFormula(temp); break; case '/': temp = n.right; while(temp != null && temp.type == Node.OP && temp.op == '/') { val /= evaluateFormula(temp.left); temp = temp.right; } if(temp != null && temp.type == Node.OP && temp.op == '*') { val /= evaluateFormula(temp.left); val *= evaluateFormula(temp.right); } else val /= evaluateFormula(temp); break; } break; case Node.VALUE: return n.value; case Node.CELL: if(0 <= n.row && n.row < rows && 0 <= n.column && n.column < columns && cells[n.row][n.column] != null) return cells[n.row][n.column].value; case Node.FUNC: if(n.func.compareTo("Abs") == 0) if(1 <= n.args.size()) val = Math.abs(evaluateFormula((Node)n.args.elementAt(0))); if(n.func.compareTo("Acos") == 0) if(1 <= n.args.size()) val = Math.acos(evaluateFormula((Node)n.args.elementAt(0))); if(n.func.compareTo("Asin") == 0) if(1 <= n.args.size()) val = Math.asin(evaluateFormula((Node)n.args.elementAt(0))); if(n.func.compareTo("Atan") == 0) if(1 <= n.args.size()) val = Math.atan(evaluateFormula((Node)n.args.elementAt(0))); if(n.func.compareTo("Ceil") == 0) if(1 <= n.args.size()) val = Math.ceil(evaluateFormula((Node)n.args.elementAt(0))); if(n.func.compareTo("Cos") == 0) if(1 <= n.args.size()) val = Math.cos(evaluateFormula((Node)n.args.elementAt(0))); if(n.func.compareTo("Exp") == 0) if(1 <= n.args.size()) val = Math.exp(evaluateFormula((Node)n.args.elementAt(0))); if(n.func.compareTo("Floor") == 0) if(1 <= n.args.size()) val = Math.floor(evaluateFormula((Node)n.args.elementAt(0))); if(n.func.compareTo("Log") == 0) if(1 <= n.args.size()) val = Math.log(evaluateFormula((Node)n.args.elementAt(0))); if(n.func.compareTo("Max") == 0) if(2 <= n.args.size()) val = Math.max(evaluateFormula((Node)n.args.elementAt(0)), evaluateFormula((Node)n.args.elementAt(1))); if(n.func.compareTo("Min") == 0) if(2 <= n.args.size()) val = Math.min(evaluateFormula((Node)n.args.elementAt(0)), evaluateFormula((Node)n.args.elementAt(1))); if(n.func.compareTo("Pow") == 0) if(2 <= n.args.size()) val = Math.pow(evaluateFormula((Node)n.args.elementAt(0)), evaluateFormula((Node)n.args.elementAt(1))); if(n.func.compareTo("Random") == 0) val = Math.random(); if(n.func.compareTo("Gaussian") == 0) { val = gaus.nextGaussian(); } if(n.func.compareTo("Round") == 0) if(1 <= n.args.size()) val = Math.round(evaluateFormula((Node)n.args.elementAt(0))); if(n.func.compareTo("Sin") == 0) if(1 <= n.args.size()) val = Math.sin(evaluateFormula((Node)n.args.elementAt(0))); if(n.func.compareTo("Sqrt") == 0) if(1 <= n.args.size()) val = Math.sqrt(evaluateFormula((Node)n.args.elementAt(0))); if(n.func.compareTo("Tan") == 0) if(1 <= n.args.size()) val = Math.tan(evaluateFormula((Node)n.args.elementAt(0))); if(n.func.compareTo("Lt") == 0) if(2 <= n.args.size()) if(evaluateFormula((Node)n.args.elementAt(0)) < evaluateFormula((Node)n.args.elementAt(1))) val = 1.0; if(n.func.compareTo("Le") == 0) if(2 <= n.args.size()) if(evaluateFormula((Node)n.args.elementAt(0)) <= evaluateFormula((Node)n.args.elementAt(1))) val = 1.0; if(n.func.compareTo("Gt") == 0) if(2 <= n.args.size()) if(evaluateFormula((Node)n.args.elementAt(0)) > evaluateFormula((Node)n.args.elementAt(1))) val = 1.0; if(n.func.compareTo("Ge") == 0) if(2 <= n.args.size()) if(evaluateFormula((Node)n.args.elementAt(0)) >= evaluateFormula((Node)n.args.elementAt(1))) val = 1.0; if(n.func.compareTo("Eq") == 0) if(2 <= n.args.size()) if(evaluateFormula((Node)n.args.elementAt(0)) == evaluateFormula((Node)n.args.elementAt(1))) val = 1.0; if(n.func.compareTo("Ne") == 0) if(2 <= n.args.size()) if(evaluateFormula((Node)n.args.elementAt(0)) != evaluateFormula((Node)n.args.elementAt(1))) val = 1.0; if(n.func.compareTo("And") == 0) if(2 <= n.args.size()) if(evaluateFormula((Node)n.args.elementAt(0)) != 0.0 && evaluateFormula((Node)n.args.elementAt(1)) != 0.0) val = 1.0; if(n.func.compareTo("Or") == 0) if(2 <= n.args.size()) if(evaluateFormula((Node)n.args.elementAt(0)) != 0 || evaluateFormula((Node)n.args.elementAt(1)) != 0) val = 1.0; if(n.func.compareTo("Not") == 0) if(1 <= n.args.size()) if(evaluateFormula((Node)n.args.elementAt(0)) == 0) val = 1.0; if(n.func.compareTo("If") == 0) if(2 <= n.args.size()) if(evaluateFormula((Node)n.args.elementAt(0)) != 0) val = evaluateFormula((Node)n.args.elementAt(1)); else if(3 <= n.args.size()) val = evaluateFormula((Node)n.args.elementAt(2)); } return val; } public synchronized void paint(Graphics g) { int i, j; int cx, cy; char l[] = new char[1]; g.setColor(inputColor); g.setFont(inputFont); g.fillRect(0, offset, columns * cellWidth + rowLabelWidth, cellHeight); for(i = 0; i < rows + 1; i++) { cy = (i + 2) * cellHeight + offset; g.setColor(getBackground()); g.draw3DRect(0, cy, columns * cellWidth + rowLabelWidth, 1, true); if(0 < i) { g.setColor(Color.red); g.drawString("" + i, 2, cy - 3); } } for(i = 0; i < columns + 1; i++) { cx = i * cellWidth; g.setColor(getBackground()); g.draw3DRect(cx + rowLabelWidth, cellHeight + offset, 1, (rows + 1) * cellHeight, true); if(i < columns) { g.setColor(Color.red); l[0] = (char)((int)'a' + i); g.drawString(new String(l), cx + rowLabelWidth + (cellWidth / 2), 2 * cellHeight - 3 + offset); } } for(i = 0; i < rows; i++) for(j = 0; j < columns; j++) { cx = (j * cellWidth) + 2 + rowLabelWidth; cy = ((i+2) * cellHeight) + 2 + offset; if(cells[i][j] != null) cells[i][j].paint(g, cx, cy); } inputArea.paint(g, 1, 1 + offset); } public boolean mouseDown(Event evt, int x, int y) { int newselectedRow; int newselectedColumn; if(y < 2 * cellHeight + offset) newselectedRow = -1; else newselectedRow = ((y - 2 * cellHeight - offset) / cellHeight); if(x < rowLabelWidth) newselectedColumn = -1; else newselectedColumn = (x - rowLabelWidth) / cellWidth; if(0 <= newselectedRow && newselectedRow < rows && 0 <= newselectedColumn && newselectedColumn < columns) { selectedRow = newselectedRow; selectedColumn = newselectedColumn; if(current != null) current.deselect(); current = cells[selectedRow][selectedColumn]; inputArea.setText(new String(current.printString)); current.select(); requestFocus(); } repaint(); return true; } public boolean keyDown(Event evt, int key) { inputArea.keyDown(key); if(key == 10) { selectedRow = -1; selectedColumn = -1; if(current != null) current.deselect(); inputArea.setText(""); } return true; } public boolean action(Event evt, Object obj) { if (evt.target instanceof Button) { String label = (String)obj; if (label.equals("Clear")) { if(current != null) current.deselect(); for(int i = 0; i < rows; i++) for(int j = 0; j < columns; j++) cells[i][j].setValue(""); selectedRow = -1; selectedColumn = -1; inputArea.setText(""); repaint(); } return true; } if (evt.target instanceof TextField) { System.out.println("Name"); return true; } return false; } public String getAppletInfo() { return("Robert's Online SpreadSheet! by Dr. Robert Lum"); } } class Cell { public static final int LABEL = 0; public static final int FORMULA = 1; Node parseRoot; boolean needRedisplay; boolean selected = false; int type = Cell.LABEL; String valueString = ""; String printString = ""; double value = 0.0; Color bgColor; Color fgColor; Color highlightColor; int width; int height; spreadsheet app; public Cell(spreadsheet app, Color bgColor, Color fgColor, Color highlightColor, int width, int height) { this.app = app; this.bgColor = bgColor; this.fgColor = fgColor; this.highlightColor = highlightColor; this.width = width; this.height = height; needRedisplay = true; } public void setRawValue(double f) { valueString = new Double(f).toString(); value = f; } public String parseFormula(String formula, Node node) { char op; Node right; Node left; if(formula == null || formula.length() == 0) return formula; String restformula = new String(formula).trim(); restformula = parseAddend(restformula, node); if(restformula == null || restformula.length() == 0) return restformula; switch(op = restformula.charAt(0)) { case '+': case '-': restformula = parseFormula(restformula.substring(1), right = new Node()); left = new Node(node); node.type = Node.OP; node.op = op; node.left = left; node.right = right; break; default: break; } return restformula.trim(); } public String parseAddend(String formula, Node node) { char op; Node right; Node left; if(formula == null || formula.length() == 0) return formula; String restformula = new String(formula).trim(); restformula = parseMultiplicand(restformula, node); if(restformula == null || restformula.length() == 0) return restformula; switch(op = restformula.charAt(0)) { case '*': case '/': restformula = parseAddend(restformula.substring(1), right = new Node()); left = new Node(node); node.type = Node.OP; node.op = op; node.left = left; node.right = right; break; default: break; } return restformula.trim(); } public String parseMultiplicand(String formula, Node node) { if(formula == null || formula.length() == 0) return formula; String restformula = new String(formula).trim(); char c = restformula.charAt(0); if('0' <= c && c <= '9') { int i; double value=0.0; boolean dot = true; for(i = 0; i < restformula.length(); i++) { c = restformula.charAt(i); if('0' <= c && c <= '9') continue; if(c == '.' && dot) { dot = false; continue; } break; } node.type = Node.VALUE; try { node.value = Double.valueOf(restformula.substring(0,i)).doubleValue(); } catch (NumberFormatException e) { node.value = 0.0; System.out.println("Can't convert to numerical value !!"); } restformula = restformula.substring(i).trim(); return restformula; } if('a' <= c && c <= 'z') { int i; int row; int column = c - 'a'; restformula = restformula.substring(1); row = 0; for(i = 0; i < restformula.length(); i++) { c = restformula.charAt(i); if('0' <= c && c <= '9') { row = 10 * row + c - '0'; continue; } break; } node.row = row - 1; node.column = column; node.type = Node.CELL; restformula = restformula.substring(i).trim(); return restformula; } if('A' <= c && c <= 'Z') { Node temp; int i = restformula.indexOf('('); if(i == -1) return restformula; node.type = Node.FUNC; node.func = new String(restformula.substring(0, i)); node.args = new Vector(); restformula = restformula.substring(i + 1); while(restformula != null && restformula.length() != 0) { restformula = parseFormula(restformula, temp = new Node()); node.args.addElement(temp); if(restformula == null || restformula.length() == 0) break; if(restformula.charAt(0) == ',') { restformula = restformula.substring(1).trim(); continue; } if(restformula.charAt(0) == ')') restformula = restformula.substring(1).trim(); break; } return restformula; } if(c == '(') { restformula = parseFormula(restformula.substring(1), node); if(restformula == null || restformula.length() == 0) return restformula; if(restformula.charAt(0) == ')') restformula = restformula.substring(1).trim(); if(node.type == Node.OP) node.type = Node.PRE; return restformula; } return restformula; } public void setValue(String s) { needRedisplay = true; printString = new String(s); if(s != null && s.length() != 0) if(s.charAt(0) == '=') { type = Cell.FORMULA; parseFormula(s.substring(1), parseRoot = new Node()); value = 0.0; } else { type = Cell.LABEL; try { value = Double.valueOf(s).doubleValue(); } catch(NumberFormatException e) { value = 0.0; }; } else { type = Cell.LABEL; value = 0.0; } app.recalculate(); } public void select() { selected = true; needRedisplay = true; app.repaint(); } public void deselect() { selected = false; needRedisplay = true; app.repaint(); } public void paint(Graphics g, int x, int y) { int char_count = width / 8; if (selected) g.setColor(highlightColor); else g.setColor(bgColor); g.fillRect(x, y, width, height); if(printString != null) { switch (type) { case Cell.LABEL: g.setColor(fgColor); if(char_count < printString.length()) g.drawString(printString.substring(0, char_count), x, y + (height / 2) + 5); else g.drawString(printString, x, y + (height / 2) + 5); break; case Cell.FORMULA: g.setColor(Color.blue); if(char_count < valueString.length()) g.drawString(valueString.substring(0, char_count), x, y + (height / 2) + 5); else g.drawString(valueString, x, y + (height / 2) + 5); break; } } else { g.setColor(fgColor); g.drawString("", x, y + (height / 2) + 5); } needRedisplay = false; } } class Node { public static final int OP = 0; public static final int VALUE = 1; public static final int CELL = 2; public static final int FUNC = 3; public static final int PRE = 4; int type; Node left; Node right; int row; int column; double value; char op; String func; Vector args; public Node() { type = Node.VALUE; left = null; right = null; row = -1; column = -1; value = 0; op = 0; func = new String(); args = new Vector(); } public Node(Node n) { type = n.type; left = n.left; right = n.right; row = n.row; column = n.column; value = n.value; op = n.op; func = new String(n.func); args = new Vector(); for(Enumeration e = n.args.elements(); e.hasMoreElements(); ) args.addElement(new Node((Node)e.nextElement())); } } class InputField { final int maxchars = 50; Applet app; String sval; char buffer[]; int nChars; int width; int height; Color bgColor; Color fgColor; public InputField(String initValue, Applet app, int width, int height, Color bgColor, Color fgColor) { this.width = width; this.height = height; this.bgColor = bgColor; this.fgColor = fgColor; this.app = app; buffer = new char[maxchars]; nChars = 0; if (initValue != null) { initValue.getChars(0, initValue.length(), this.buffer, 0); nChars = initValue.length(); } sval = initValue; } public void setText(String val) { for(int i = 0; i < maxchars; i++) buffer[i] = 0; if(val == null) { sval = new String(""); nChars = 0; buffer[0] = 0; } else { val.getChars(0, val.length(), buffer, 0); nChars = val.length(); sval = new String(buffer).substring(0, nChars); } } public void paint(Graphics g, int x, int y) { g.setColor(bgColor); g.fillRect(x, y, width, height); g.setColor(fgColor); if(sval != null) g.drawString(sval + "_", x, y + (height / 2) + 3); else g.drawString("_", x, y + (height / 2) + 3); } public void keyDown(int key) { switch (key) { case 8: // delete --nChars; if(nChars < 0) { nChars = 0; } buffer[nChars] = 0; sval = new String(new String(buffer).substring(0, nChars)); break; case 10: // return break; default: if(nChars < maxchars) { buffer[nChars++] = (char)key; sval = new String(new String(buffer).substring(0, nChars)); } break; } ((spreadsheet)app).setCurrentValue(sval); app.repaint(); } }