/* * A simple spread_sheet using sub_arctic. * I took out all of the business with URLs & threads because I couldn't * figure out what that was trying to do. All of the parsing and calculating * code is identical to the original. * 9/24/96 Colleen Kehoe * * Based on: * @(#)SpreadSheet.java 1.17 95/03/09 Sami Shaio * Copyright (c) 1994-1996 Sun Microsystems, Inc. All Rights Reserved. * */ package sub_arctic.demo_apps; import java.awt.*; import java.io.*; import java.lang.*; import java.net.*; import sub_arctic.lib.*; import sub_arctic.output.*; import sub_arctic.input.*; import sub_arctic.constraints.std_function; public class spread_sheet extends interactor_applet { String title; Font titleFont; int cellWidth = 100; Font inputFont; int rows; int columns; SpreadSheetInput inputArea; Cell cells[][]; Cell current = null; grid ss_grid; boolean isStopped = false; label titleLabel; public void build_ui(base_parent_interactor top) { String rs; inputFont = new Font("Courier", Font.PLAIN, 10); titleFont = new Font("Courier", Font.BOLD, 12); title = getParameter("title"); if (title == null) { title = "Spreadsheet"; } rs = getParameter("rows"); if (rs == null) { rows = 9; } else { rows = Integer.parseInt(rs); } rs = getParameter("columns"); if (rs == null) { columns = 5; } else { columns = Integer.parseInt(rs); } // set up title titleLabel = new label(title,titleFont); titleLabel.set_x_constraint(std_function.eq(PARENT.X1())); titleLabel.set_y_constraint(std_function.eq(PARENT.Y1())); top.add_child(titleLabel); // inputArea is the bar you type into at the top inputArea = new SpreadSheetInput(null,this); inputArea.set_w_constraint(std_function.eq(PARENT.X2())); inputArea.set_y_constraint(std_function.eq(PREV_SIBLING.Y2())); top.add_child(inputArea); // set up labels--use the 0th row & column to store them ss_grid = new grid(rows+1,columns+1); char l[] = new char[1]; ss_grid.set_element_at(0,0,new label("")); for (int i=1; i<=rows; i++) { ss_grid.set_element_at(i,0,new label(String.valueOf(i))); } for (int j=1; j<=columns; j++) { l[0] = (char)((int)'A' + j-1); String s = new String(l); // cheezy, I know ss_grid.set_element_at(0,j, new label(" "+s,cellWidth-2)); } // create all cells first for (int i=1; i <= rows; i++) { for (int j=1; j <= columns; j++) { ss_grid.set_element_at(i,j, new Cell(this, Color.yellow, Color.black, cellWidth - 2)); } } // initialize cells with applet parameters for (int i=1; i <= rows; i++) { for (int j=1; j <= columns; j++) { l[0] = (char)((int)'a' + j-1); rs = getParameter("" + new String(l) + i); if (rs != null) { ((Cell)ss_grid.element_at(i,j)).setUnparsedValue(rs); } } } ss_grid.set_y_constraint(std_function.eq(PREV_SIBLING.Y2())); top.add_child(ss_grid); } public void setCurrentValue(float val) { current.setValue(val); } public void stop() { isStopped = true; } public void start() { isStopped = false; } public void destroy() { } public void setCurrentCell(Cell c) { unsetCurrentCell(); current = c; current.select(); // display the value of the current cell in the inputArea inputArea.set_text(new String(c.getPrintString())); // set the selection point so it is not beyond the end of the text inputArea.set_selection(oneline_text_display.SELECT_END, oneline_text_display.SELECT_END); } public void unsetCurrentCell() { if (current != null) { current.deselect(); current = null; } } public void setCurrentValue(int type, String val) { current.setValue(type,val); } public void recalculate() { int i,j; for (i=1; i <= rows; i++) { for (j=1; j <= columns; j++) { if (((Cell)ss_grid.element_at(i,j)) != null && ((Cell)ss_grid.element_at(i,j)).type == Cell.FORMULA) { ((Cell)ss_grid.element_at(i,j)).setRawValue(evaluateFormula(((Cell)ss_grid.element_at(i,j)).parseRoot)); } } } } public float evaluateFormula(Node n) { float val = 0.0f; if (n == null) { return val; } switch (n.type) { case Node.OP: val = evaluateFormula(n.left); switch (n.op) { case '+': val += evaluateFormula(n.right); break; case '*': val *= evaluateFormula(n.right); break; case '-': val -= evaluateFormula(n.right); break; case '/': val /= evaluateFormula(n.right); break; } break; case Node.VALUE: return n.value; case Node.CELL: if (n == null) { } else { if (ss_grid.element_at(n.row+1,n.column+1) == null) { } else { return ((Cell)ss_grid.element_at(n.row+1,n.column+1)).value; } } } return val; } } class Cell extends oneline_text_display implements pressable { public static final int VALUE = 0; public static final int LABEL = 1; public static final int URL = 2; public static final int FORMULA = 3; Node parseRoot; boolean selected = false; boolean transientValue = false; public int type = Cell.VALUE; String valueString = ""; String printString = "v"; float value; spread_sheet app; // the color to use when this cell is selected color_pair selected_cp; public Cell(spread_sheet app, Color bgColor, Color fgColor, int width) { super(0,0,width,null,null,false); this.app = app; selected_cp = new color_pair(fgColor,bgColor); } public void setRawValue(float f) { valueString = Float.toString(f); value = f; set_text(valueString); } public void setValue(float f) { setRawValue(f); set_text(valueString); printString = "v" + valueString; type = Cell.VALUE; app.recalculate(); } public void setTransientValue(float f) { transientValue = true; value = f; app.recalculate(); } public void setUnparsedValue(String s) { switch (s.charAt(0)) { case 'v': setValue(Cell.VALUE, s.substring(1)); break; case 'f': setValue(Cell.FORMULA, s.substring(1)); break; case 'l': setValue(Cell.LABEL, s.substring(1)); break; } } /** * Parse a spreadsheet formula. The syntax is defined as: * * formula -> value * formula -> value op value * value -> '(' formula ')' * value -> cell * value -> * op -> '+' | '*' | '/' | '-' * cell -> */ public String parseFormula(String formula, Node node) { String subformula; String restFormula; float value; int length = formula.length(); Node left; Node right; char op; if (formula == null) { return null; } subformula = parseValue(formula, node); if (subformula == null || subformula.length() == 0) { return null; } if (subformula == formula) { return formula; } // parse an operator and then another value switch (op = subformula.charAt(0)) { case 0: return null; case ')': return subformula; case '+': case '*': case '-': case '/': restFormula = subformula.substring(1); subformula = parseValue(restFormula, right=new Node()); if (subformula != restFormula) { left = new Node(node); node.left = left; node.right = right; node.op = op; node.type = Node.OP; return subformula; } else { return formula; } default: return formula; } } public String parseValue(String formula, Node node) { char c = formula.charAt(0); String subformula; String restFormula; float value; int row; int column; restFormula = formula; if (c == '(') { restFormula = formula.substring(1); subformula = parseFormula(restFormula, node); if (subformula == null || subformula.length() == restFormula.length()) { return formula; } else if (! (subformula.charAt(0) == ')')) { return formula; } restFormula = subformula; } else if (c >= '0' && c <= '9') { int i; try { value = Float.valueOf(formula).floatValue(); } catch (NumberFormatException e) { return formula; } for (i=0; i < formula.length(); i++) { c = formula.charAt(i); if ((c < '0' || c > '9') && c != '.') { break; } } node.type = Node.VALUE; node.value = value; restFormula = formula.substring(i); return restFormula; } else if (c >= 'A' && c <= 'Z') { int i; column = c - 'A'; restFormula = formula.substring(1); row = Float.valueOf(restFormula).intValue(); for (i=0; i < restFormula.length(); i++) { c = restFormula.charAt(i); if (c < '0' || c > '9') { break; } } node.row = row - 1; node.column = column; node.type = Node.CELL; if (i == restFormula.length()) { restFormula = null; } else { restFormula = restFormula.substring(i); if (restFormula.charAt(0) == 0) { return null; } } } return restFormula; } public void setValue(int type, String s) { valueString = new String(s); this.type = type; switch (type) { case Cell.VALUE: setValue(Float.valueOf(s).floatValue()); break; case Cell.LABEL: set_text(valueString); printString = "l" + valueString; break; case Cell.FORMULA: parseFormula(valueString, parseRoot = new Node()); printString = "f" + valueString; set_text(valueString); break; } app.recalculate(); } public String getValueString() { return valueString; } public String getPrintString() { return printString; } public void select() { selected = true; set_colors(selected_cp); } public void deselect() { selected = false; set_colors(null); } public boolean press(event evt, Object user_info) { return true; } public boolean release(event evt, Object user_info) { // selection only takes effect on release, so that the // user can back out if they want to app.setCurrentCell(this); return true; } } class Node { public static final int OP = 0; public static final int VALUE = 1; public static final int CELL = 2; int type; Node left; Node right; int row; int column; float value; char op; public Node() { left = null; right = null; value = 0; row = -1; column = -1; op = 0; type = Node.VALUE; } public Node(Node n) { left = n.left; right = n.right; value = n.value; row = n.row; column = n.column; op = n.op; type = n.type; } public void indent(int ind) { for (int i = 0; i < ind; i++) { System.out.print(" "); } } public void print(int indentLevel) { char l[] = new char[1]; indent(indentLevel); System.out.println("NODE type=" + type); indent(indentLevel); switch (type) { case Node.VALUE: System.out.println(" value=" + value); break; case Node.CELL: l[0] = (char)((int)'A' + column); System.out.println(" cell=" + new String(l) + (row+1)); break; case Node.OP: System.out.println(" op=" + op); left.print(indentLevel + 3); right.print(indentLevel + 3); break; } } } class SpreadSheetInput extends oneline_text_edit { spread_sheet app; public SpreadSheetInput(String initValue, spread_sheet app) { super(initValue); this.app = app; } // this means to consider the "eol" (i.e. return) character to be a // special action character public int char_filter(int input_char, int modifiers) { return text_focus_agent.eol_action(input_char); } // when we get the action character (i.e. return) we update the value of // the current cell with what was typed in public boolean action_char(event evt, char ch, Object user_info) { float f; switch (text().charAt(0)) { case 'v': try { f = Float.valueOf(text().substring(1)).floatValue(); ((spread_sheet)app).setCurrentValue(f); } catch (NumberFormatException e) { System.out.println("Not a float..."); } break; case 'l': ((spread_sheet)app).setCurrentValue(Cell.LABEL, text().substring(1)); break; case 'f': ((spread_sheet)app).setCurrentValue(Cell.FORMULA, text().substring(1)); break; } return true; } } /*=========================== COPYRIGHT NOTICE =========================== This file is part of the subArctic user interface toolkit. Copyright (c) 1996 Scott Hudson and Ian Smith All rights reserved. The subArctic system is freely available for most uses under the terms and conditions described in http://www.cc.gatech.edu/gvu/ui/sub_arctic/sub_arctic/doc/usage.html and appearing in full in the lib/interactor.java source file. The current release and additional information about this software can be found starting at: http://www.cc.gatech.edu/gvu/ui/sub_arctic/ ========================================================================*/