42 import java.applet.Applet;
44 import java.awt.event.*;
49 @SuppressWarnings(
"serial")
50 public class
SpreadSheet extends Applet implements MouseListener, KeyListener {
59 int rowLabelWidth = 15;
61 boolean isStopped =
false;
62 boolean fullUpdate =
true;
67 int selectedColumn = -1;
68 SpreadSheetInput inputArea;
73 public synchronized void init() {
76 cellColor = Color.white;
77 inputColor =
new Color(100, 100, 225);
78 inputFont =
new Font(
"Monospaced", Font.PLAIN, 10);
79 titleFont =
new Font(
"Monospaced", Font.BOLD, 12);
80 title = getParameter(
"title");
82 title =
"Spreadsheet";
84 rs = getParameter(
"rows");
88 rows = Integer.parseInt(rs);
90 rs = getParameter(
"columns");
94 columns = Integer.parseInt(rs);
96 cells =
new Cell[rows][columns];
97 char l[] =
new char[1];
98 for (
int i = 0; i < rows; i++) {
99 for (
int j = 0; j < columns; j++) {
101 cells[i][j] =
new Cell(
this,
107 l[0] = (char) ((
int)
'a' + j);
108 rs = getParameter(
"" +
new String(l) + (i + 1));
110 cells[i][j].setUnparsedValue(rs);
115 Dimension d = getSize();
116 inputArea =
new SpreadSheetInput(
null,
this, d.width - 2, cellHeight - 1,
117 inputColor, Color.white);
118 resize(columns * cellWidth + rowLabelWidth,
119 (rows + 3) * cellHeight + titleHeight);
120 addMouseListener(
this);
121 addKeyListener(
this);
125 if (selectedRow == -1 || selectedColumn == -1) {
128 cells[selectedRow][selectedColumn].setValue(val);
144 for (
int i = 0; i < rows; i++) {
145 for (
int j = 0; j < columns; j++) {
146 if (cells[i][j].type == Cell.URL) {
147 cells[i][j].updaterThread.run =
false;
154 if (selectedRow == -1 || selectedColumn == -1) {
157 cells[selectedRow][selectedColumn].setValue(type, val);
166 g.setFont(titleFont);
167 for (
int i = 0; i < rows; i++) {
168 for (
int j = 0; j < columns; j++) {
169 if (cells[i][j].needRedisplay) {
170 cx = (j * cellWidth) + 2 + rowLabelWidth;
171 cy = ((i + 1) * cellHeight) + 2 + titleHeight;
172 cells[i][j].paint(g, cx, cy);
186 for (i = 0; i < rows; i++) {
187 for (j = 0; j < columns; j++) {
188 if (cells[i][j] !=
null && cells[i][j].type == Cell.FORMULA) {
189 cells[i][j].setRawValue(evaluateFormula(
190 cells[i][j].parseRoot));
191 cells[i][j].needRedisplay =
true;
198 float evaluateFormula(Node n) {
209 val = evaluateFormula(n.left);
212 val += evaluateFormula(n.right);
215 val *= evaluateFormula(n.right);
218 val -= evaluateFormula(n.right);
221 val /= evaluateFormula(n.right);
229 if (cells[n.row][n.column] ==
null) {
233 return cells[n.row][n.column].value;
242 public synchronized void paint(Graphics g) {
245 char l[] =
new char[1];
248 Dimension d = getSize();
250 g.setFont(titleFont);
251 i = g.getFontMetrics().stringWidth(title);
252 g.drawString((title ==
null) ?
"Spreadsheet" : title,
253 (d.width - i) / 2, 12);
254 g.setColor(inputColor);
255 g.fillRect(0, cellHeight, d.width, cellHeight);
256 g.setFont(titleFont);
257 for (i = 0; i < rows + 1; i++) {
258 cy = (i + 2) * cellHeight;
259 g.setColor(getBackground());
260 g.draw3DRect(0, cy, d.width, 2,
true);
262 g.setColor(Color.red);
263 g.drawString(
"" + (i + 1), 2, cy + 12);
267 g.setColor(Color.red);
268 cy = (rows + 3) * cellHeight + (cellHeight / 2);
269 for (i = 0; i < columns; i++) {
271 g.setColor(getBackground());
272 g.draw3DRect(cx + rowLabelWidth,
273 2 * cellHeight, 1, d.height,
true);
275 g.setColor(Color.red);
276 l[0] = (char) ((
int)
'A' + i);
277 g.drawString(
new String(l),
278 cx + rowLabelWidth + (cellWidth / 2),
283 for (i = 0; i < rows; i++) {
284 for (j = 0; j < columns; j++) {
285 cx = (j * cellWidth) + 2 + rowLabelWidth;
286 cy = ((i + 1) * cellHeight) + 2 + titleHeight;
287 if (cells[i][j] !=
null) {
288 cells[i][j].paint(g, cx, cy);
293 g.setColor(getBackground());
294 g.draw3DRect(0, titleHeight,
296 d.height - titleHeight,
298 inputArea.paint(g, 1, titleHeight + 1);
311 if (y < (titleHeight + cellHeight)) {
313 if (y <= titleHeight && current !=
null) {
319 if (x < rowLabelWidth) {
321 if (current !=
null) {
328 selectedRow = ((y - cellHeight - titleHeight) / cellHeight);
329 selectedColumn = (x - rowLabelWidth) / cellWidth;
330 if (selectedRow > rows
331 || selectedColumn >= columns) {
333 if (current !=
null) {
338 if (selectedRow >= rows) {
340 if (current !=
null) {
346 if (selectedRow != -1) {
347 cell = cells[selectedRow][selectedColumn];
348 inputArea.setText(cell.getPrintString());
349 if (current !=
null) {
381 inputArea.processKey(e);
391 return "Title: SpreadSheet \nAuthor: Sami Shaio \nA simple spread sheet.";
398 "The title of the spread sheet. Default is 'Spreadsheet'" },
399 {
"rows",
"int",
"The number of rows. Default is 9." },
400 {
"columns",
"int",
"The number of columns. Default is 5." }
407 class CellUpdater
extends Thread {
410 InputStream dataStream =
null;
411 StreamTokenizer tokenStream;
412 public volatile boolean run =
true;
414 public CellUpdater(Cell c) {
415 super(
"cell updater");
422 dataStream =
new URL(target.app.getDocumentBase(),
423 target.getValueString()).openStream();
424 tokenStream =
new StreamTokenizer(
new BufferedReader(
425 new InputStreamReader(dataStream)));
426 tokenStream.eolIsSignificant(
false);
429 switch (tokenStream.nextToken()) {
430 case StreamTokenizer.TT_EOF:
435 case StreamTokenizer.TT_NUMBER:
436 target.setTransientValue((
float) tokenStream.nval);
437 if (!target.app.isStopped && !target.paused) {
438 target.app.repaint();
444 }
catch (InterruptedException e) {
448 }
catch (IOException e) {
457 public static final int VALUE = 0;
458 public static final int LABEL = 1;
459 public static final int URL = 2;
460 public static final int FORMULA = 3;
462 boolean needRedisplay;
463 boolean selected =
false;
464 boolean transientValue =
false;
465 public int type = Cell.VALUE;
466 String valueString =
"";
467 String printString =
"v";
471 Color highlightColor;
475 CellUpdater updaterThread;
476 boolean paused =
false;
481 Color highlightColor,
485 this.bgColor = bgColor;
486 this.fgColor = fgColor;
487 this.highlightColor = highlightColor;
489 this.height = height;
490 needRedisplay =
true;
493 public void setRawValue(
float f) {
494 valueString = Float.toString(f);
498 public void setValue(
float f) {
500 printString =
"v" + valueString;
504 needRedisplay =
true;
507 public void setTransientValue(
float f) {
508 transientValue =
true;
510 needRedisplay =
true;
514 public void setUnparsedValue(String s) {
515 switch (s.charAt(0)) {
517 setValue(Cell.VALUE, s.substring(1));
520 setValue(Cell.FORMULA, s.substring(1));
523 setValue(Cell.LABEL, s.substring(1));
526 setValue(Cell.URL, s.substring(1));
542 public String parseFormula(String formula, Node node) {
549 if (formula ==
null) {
552 subformula = parseValue(formula, node);
554 if (subformula ==
null || subformula.length() == 0) {
558 if (subformula.equals(formula)) {
564 switch (op = subformula.charAt(0)) {
575 restFormula = subformula.substring(1);
576 subformula = parseValue(restFormula, right =
new Node());
578 if (subformula ==
null ? restFormula !=
null : !subformula.
579 equals(restFormula)) {
581 left =
new Node(node);
598 public String parseValue(String formula, Node node) {
599 char c = formula.charAt(0);
607 restFormula = formula;
610 restFormula = formula.substring(1);
611 subformula = parseFormula(restFormula, node);
613 if (subformula ==
null
614 || subformula.length() == restFormula.length()) {
617 }
else if (!(subformula.charAt(0) ==
')')) {
621 restFormula = subformula;
622 }
else if (c >=
'0' && c <=
'9') {
626 for (i = 0; i < formula.length(); i++) {
627 c = formula.charAt(i);
628 if ((c < '0' || c >
'9') && c !=
'.') {
633 _value = Float.valueOf(formula.substring(0, i)).floatValue();
634 }
catch (NumberFormatException e) {
638 node.type = Node.VALUE;
641 restFormula = formula.substring(i);
645 }
else if (c >=
'A' && c <=
'Z') {
649 restFormula = formula.substring(1);
650 for (i = 0; i < restFormula.length(); i++) {
651 c = restFormula.charAt(i);
652 if (c < '0' || c >
'9') {
656 row = Float.valueOf(restFormula.substring(0, i)).intValue();
659 node.column = column;
660 node.type = Node.CELL;
662 if (i == restFormula.length()) {
665 restFormula = restFormula.substring(i);
666 if (restFormula.charAt(0) == 0) {
675 public void setValue(
int type, String s) {
677 if (this.type == Cell.URL) {
678 updaterThread.run =
false;
679 updaterThread =
null;
684 needRedisplay =
true;
687 setValue(Float.valueOf(s).floatValue());
690 printString =
"l" + valueString;
693 printString =
"u" + valueString;
694 updaterThread =
new CellUpdater(
this);
695 updaterThread.start();
698 parseFormula(valueString, parseRoot =
new Node());
699 printString =
"f" + valueString;
705 public String getValueString() {
709 public String getPrintString() {
713 public void select() {
718 public void deselect() {
721 needRedisplay =
true;
725 public void paint(Graphics g,
int x,
int y) {
727 g.setColor(highlightColor);
731 g.fillRect(x, y, width - 1, height);
732 if (valueString !=
null) {
739 g.setColor(Color.red);
742 g.setColor(Color.blue);
745 if (transientValue) {
746 g.drawString(
"" + value, x, y + (height / 2) + 5);
748 if (valueString.length() > 14) {
749 g.drawString(valueString.substring(0, 14),
750 x, y + (height / 2) + 5);
752 g.drawString(valueString, x, y + (height / 2) + 5);
756 needRedisplay =
false;
763 public static final int OP = 0;
764 public static final int VALUE = 1;
765 public static final int CELL = 2;
784 public Node(Node n) {
794 public void indent(
int ind) {
795 for (
int i = 0; i < ind; i++) {
796 System.out.print(
" ");
800 public void print(
int indentLevel) {
801 char l[] =
new char[1];
803 System.out.println(
"NODE type=" + type);
807 System.out.println(
" value=" + value);
810 l[0] = (char) ((
int)
'A' + column);
811 System.out.println(
" cell=" +
new String(l) + (row + 1));
814 System.out.println(
" op=" + op);
815 left.print(indentLevel + 3);
816 right.print(indentLevel + 3);
836 public InputField(String initValue, Applet app,
int width,
int height,
837 Color bgColor, Color fgColor) {
839 this.height = height;
840 this.bgColor = bgColor;
841 this.fgColor = fgColor;
843 buffer =
new char[maxchars];
845 if (initValue !=
null) {
846 initValue.getChars(0, initValue.length(),
this.buffer, 0);
847 nChars = initValue.length();
852 public void setText(String val) {
855 for (i = 0; i < maxchars; i++) {
863 nChars = sval.length();
864 sval.getChars(0, sval.length(), buffer, 0);
867 public String getValue() {
871 public void paint(Graphics g,
int x,
int y) {
873 g.fillRect(x, y, width, height);
876 g.drawString(sval, x, y + (height / 2) + 3);
880 public void processKey(KeyEvent e) {
881 char ch = e.getKeyChar();
886 sval =
new String(buffer, 0, nChars);
893 if (nChars < maxchars && ch >=
'0') {
894 buffer[nChars++] = ch;
895 sval =
new String(buffer, 0, nChars);
901 public void keyReleased(KeyEvent e) {
904 public void selected() {
909 class SpreadSheetInput
912 public SpreadSheetInput(String initValue,
918 super(initValue, app, width, height, bgColor, fgColor);
922 public void selected() {
924 sval = (
"".equals(sval)) ?
"v" : sval;
925 switch (sval.charAt(0)) {
927 String s = sval.substring(1);
930 for (i = 0; i < s.length(); i++) {
931 char c = s.charAt(i);
932 if (c < '0' || c >
'9') {
936 s = s.substring(0, i);
937 f = Float.valueOf(s).floatValue();
939 }
catch (NumberFormatException e) {
940 System.out.println(
"Not a float: '" + s +
"'");
948 ((
SpreadSheet) app).setCurrentValue(Cell.URL, sval.substring(1));