/* * @(#)SpreadSheet.java 1.17 95/03/09 Sami Shaio * * Copyright (c) 1994-1995 Sun Microsystems, Inc. All Rights Reserved. * * Permission to use, copy, modify, and distribute this software * and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and * without fee is hereby granted. * Please refer to the file http://java.sun.com/copy_trademarks.html * for further important copyright and trademark information and to * http://java.sun.com/licensing.html for further important licensing * information for the Java (tm) Technology. * * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. * * THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE * CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE * PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT * NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE * SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE * SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE * PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). SUN * SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR * HIGH RISK ACTIVITIES. */ import java.applet.Applet; import java.awt.*; import java.io.*; import java.net.*; //****************************************************************************************************** // ***************************************** SPREADSHEET *********************************************** //****************************************************************************************************** public class SpreadSheet extends Applet { private final int mLeftArrow=1006; private final int mRightArrow=1007; private final int mUpArrow=1004; private final int mDownArrow=1005; private String mStrTitle; private Font mTitleFont; private Color mCellColor; private Color mInputColor; private int mCellWidth=100; private int mCellHeight=15; private int mTitleHeight=15; private int mRowLabelWidth=15; private boolean mIsStopped=false; private boolean mFullUpdate=true; private int mRows; private int mColumns; private int mSelectedRow = -1; private int mSelectedColumn = -1; private SpreadSheetInput mInputArea; private Cell mCells[][]; private Cell mCurrent=null; public synchronized void init() { String strParam; mCellColor=Color.white; mInputColor=new Color(100, 100, 225); mTitleFont=new Font("Courier", Font.BOLD, 12); mStrTitle=getParameter("title"); if(null==mStrTitle)mStrTitle="SpreadSheet"; strParam=getParameter("rows"); if(null==strParam)mRows=9; else mRows=Integer.parseInt(strParam); strParam=getParameter("columns"); if(null==strParam)mColumns=5; else mColumns=Integer.parseInt(strParam); mCells=new Cell[mRows][mColumns]; char l[]=new char[1]; for(int i=0; i < mRows; i++) { for (int j=0; j < mColumns; j++) { mCells[i][j]=new Cell(this,Color.lightGray,Color.black,mCellColor,mCellWidth-2,mCellHeight-2); l[0]=(char)((int)'a'+j); strParam=getParameter(""+new String(l)+(i+1)); if(null!=strParam)mCells[i][j].setUnparsedValue(strParam); } } Dimension workArea=size(); mInputArea=new SpreadSheetInput(null, this,workArea.width-2,mCellHeight-1,mInputColor,Color.white); resize(mColumns*mCellWidth+mRowLabelWidth,((mRows+1)*mCellHeight)+mCellHeight+mTitleHeight); } public void setCurrentValue(float val) { if(-1==mSelectedRow||-1==mSelectedColumn)return; mCells[mSelectedRow][mSelectedColumn].setValue(val); repaint(); } public void stop() { stopped(true); } public void start() { stopped(false); } public void stopped(boolean stopped) { mIsStopped=stopped; } public boolean stopped() { return mIsStopped; } public void destroy() { for(int i=0;imRows||mSelectedColumn>=mColumns) { mSelectedRow = -1; deselect(true); } else { if(mSelectedRow>=mRows) { mSelectedRow = -1; deselect(true); return true; } select(); } return true; } public boolean keyDown(Event evt, int key) { mFullUpdate=true; if(mLeftArrow==key||mRightArrow==key||mUpArrow==key||mDownArrow==key)navigate(key); else mInputArea.keyDown(key); return true; } private void navigate(int key) { switch(key) { case mLeftArrow : if(mSelectedColumn<=0)break; mSelectedColumn--; select(); break; case mRightArrow : if(mSelectedColumn>=mColumns)break; mSelectedColumn++; select(); break; case mUpArrow : if(mSelectedRow<=0)break; mSelectedRow--; select(); break; case mDownArrow : if(mSelectedRow>=mRows)break; mSelectedRow++; select(); break; } } private void select() { Cell cell; cell=mCells[mSelectedRow][mSelectedColumn]; if(null!=cell.getPrintString())mInputArea.setText(new String(cell.getPrintString())); else mInputArea.setText(new String("")); if(mCurrent!=null)mCurrent.deselect(); mCurrent=cell; mCurrent.select(); requestFocus(); mFullUpdate=true; repaint(); } private void deselect(boolean destroy) { if(null==mCurrent)return; mCurrent.deselect(); if(destroy)mCurrent=null; } } // ****** class ScrollManager { private int mFirstVisibleCell; public ScrollManager() { mFirstVisibleCell=0; } public int firstVisibleCell() { return mFirstVisibleCell; } public void firstVisibleCell(int firstVisibleCell) { mFirstVisibleCell=firstVisibleCell; } } //****************************************************************************************************** // ***************************************** CELLTHREAD *********************************************** //****************************************************************************************************** class CellThread extends Thread { private Cell mTarget; private InputStream mDataStream=null; private StreamTokenizer mTokenStream; public CellThread(Cell cell) { super("CellThread"); mTarget=cell; } public void run() { try { mDataStream = new URL(mTarget.spreadSheet().getDocumentBase(),mTarget.getValueString()).openStream(); mTokenStream = new StreamTokenizer(mDataStream); mTokenStream.eolIsSignificant(false); while(true) { switch(mTokenStream.nextToken()) { case mTokenStream.TT_EOF: mDataStream.close(); return; default: break; case mTokenStream.TT_NUMBER: mTarget.setTransientValue((float)mTokenStream.nval); if(!mTarget.spreadSheet().stopped()&&!mTarget.paused())mTarget.spreadSheet().repaint(); break; } try {Thread.sleep(2000);} catch(InterruptedException exception){break;} } } catch (IOException exception){return;} } } //****************************************************************************************************** // ******************************************** CELL *************************************************** //****************************************************************************************************** class Cell { public static final int VALUE = 0; public static final int LABEL = 1; public static final int URL = 2; public static final int FORMULA = 3; private Node mParseRoot; private boolean mNeedRedisplay; private boolean mSelected = false; private boolean mTransientValue = false; private int mType=Cell.VALUE; private String mValueString = ""; private String mPrintString = "v"; private float mValue; private Color mBgColor; private Color mFgColor; private Color mHighlightColor; private int mWidth; private int mHeight; private SpreadSheet mSpreadSheet; private CellThread mCellThread; private boolean mPaused=false; public Cell(SpreadSheet spreadSheet,Color bgColor,Color fgColor,Color highlightColor,int width,int height) { mSpreadSheet=spreadSheet; mBgColor=bgColor; mFgColor=fgColor; mHighlightColor=highlightColor; mWidth=width; mHeight=height; mNeedRedisplay=true; } public void setRawValue(float value) { mValueString=Float.toString(value); mValue=value; } public void setValue(float value) { setRawValue(value); mPrintString="v"+mValueString; mType=Cell.VALUE; mPaused=false; mSpreadSheet.recalculate(); mNeedRedisplay=true; } public void setTransientValue(float value) { mTransientValue=true; mValue=value; mNeedRedisplay=true; mSpreadSheet.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; case 'u': setValue(Cell.URL, s.substring(1)); break; } } public String parseFormula(String formula, Node node) { String subformula; String restFormula; float value; int length = formula.length(); Node left; Node right; char op; if(null==formula)return null; subformula=parseValue(formula, node); if(null==subformula||0==subformula.length())return null; if(subformula==formula)return formula; 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 exception){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) { mPaused=false; if(mType == Cell.URL) { mCellThread.stop(); mCellThread=null; } mValueString=new String(s); mType=type; mNeedRedisplay=true; switch(mType) { case Cell.VALUE: setValue(Float.valueOf(s).floatValue()); break; case Cell.LABEL: mPrintString = "l" + mValueString; break; case Cell.URL: mPrintString = "u" + mValueString; mCellThread = new CellThread(this); mCellThread.start(); break; case Cell.FORMULA: parseFormula(mValueString,mParseRoot=new Node()); mPrintString = "f" + mValueString; break; } mSpreadSheet.recalculate(); } public String getValueString() { return mValueString; } public String getPrintString() { return mPrintString; } public void select() { mSelected = true; mPaused=true; } public void deselect() { mSelected = false; mPaused=false; mNeedRedisplay = true; mSpreadSheet.repaint(); } public void paint(Graphics graphics,int x,int y) { if(mSelected)graphics.setColor(mHighlightColor); else graphics.setColor(mBgColor); graphics.fillRect(x+1,y+1,mWidth-2,mHeight-2); if(null!=mValueString) { switch(mType) { case Cell.VALUE: case Cell.LABEL: graphics.setColor(mFgColor); break; case Cell.FORMULA: graphics.setColor(Color.red); break; case Cell.URL: graphics.setColor(Color.blue); break; } if(!mTransientValue) { if(mValueString.length()>14)graphics.drawString(mValueString.substring(0,14),x,y+(mHeight/2)+5); else graphics.drawString(mValueString,x,y+(mHeight/2)+5); } else graphics.drawString(""+mValue,x,y+(mHeight/2)+5); mNeedRedisplay=false; } } public int type() { return mType; } public Node parseRoot() { return mParseRoot; } public void needRedisplay(boolean needRedisplay) { mNeedRedisplay=needRedisplay; } public boolean needRedisplay() { return mNeedRedisplay; } public float value() { return mValue; } public CellThread cellThread() { return mCellThread; } public SpreadSheet spreadSheet() { return mSpreadSheet; } public boolean paused() { return mPaused; } } //****************************************************************************************************** // *************************************** NODE ******************************************************* //****************************************************************************************************** class Node { public static final int OP = 0; public static final int VALUE = 1; public static final int CELL = 2; private int mType; private Node mLeft; private Node mRight; private int mRow; private int mColumn; private float mValue; private char mOp; public Node() { mLeft=null; mRight=null; mValue=0; mRow=-1; mColumn=-1; mOp=0; mType=Node.VALUE; } public Node(Node node) { mLeft=node.mLeft; mRight=node.mRight; mValue=node.mValue; mRow=node.mRow; mColumn=node.mColumn; mOp=node.mOp; mType=node.mType; } public void print(int indentLevel) { char l[] = new char[1]; switch(mType) { case Node.VALUE: break; case Node.CELL: l[0] = (char)((int)'A' + column()); break; case Node.OP: mLeft.print(indentLevel + 3); mRight.print(indentLevel + 3); break; } } public int type() { return mType; } public void type(int type) { mType=type; } public float value() { return mValue; } public void value(float value) { mValue=value; } public Node left() { return mLeft; } public void left(Node left) { mLeft=left; } public Node right() { return mRight; } public void right(Node right) { mRight=right; } public char op() { return mOp; } public void op(char op) { mOp=op; } public int row() { return mRow; } public void row(int row) { mRow=row; } public int column() { return mColumn; } public void column(int column) { mColumn=column; } } class InputField { private int mMaxchars = 50; private Applet mApplet; private String mStringValue; private char mBuffer[]; private int mChars; private int mWidth; private int mHeight; private Color mBgColor; private Color mFgColor; public InputField(String initValue,Applet applet, int width, int height,Color bgColor, Color fgColor) { mWidth=width; mHeight=height; mBgColor=bgColor; mFgColor=fgColor; mApplet=applet; mBuffer = new char[mMaxchars]; mChars = 0; if(initValue != null) { initValue.getChars(0, initValue.length(),mBuffer, 0); mChars = initValue.length(); } mStringValue=initValue; } public void setText(String val) { int i; for(i=0; i < mMaxchars; i++)mBuffer[i] = 0; mStringValue=new String(val); if (val == null) { mStringValue= ""; mChars = 0; mBuffer[0] = 0; } else { mStringValue.getChars(0,mStringValue.length(), mBuffer, 0); mChars = val.length(); mStringValue = new String(mBuffer); } } public String getValue() { return mStringValue; } public void paint(Graphics g, int x, int y) { g.setColor(mBgColor); g.fillRect(x, y,mWidth,mHeight); if (mStringValue!= null) { g.setColor(mFgColor); g.drawString(mStringValue, x, y + (mHeight / 2) + 3); } } public void mouseUp(int x, int y) { } public void keyDown(int key) { if(mChars