View Javadoc
1   /*
2    * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
3    *
4    * Redistribution and use in source and binary forms, with or without
5    * modification, are permitted provided that the following conditions
6    * are met:
7    *
8    *   - Redistributions of source code must retain the above copyright
9    *     notice, this list of conditions and the following disclaimer.
10   *
11   *   - Redistributions in binary form must reproduce the above copyright
12   *     notice, this list of conditions and the following disclaimer in the
13   *     documentation and/or other materials provided with the distribution.
14   *
15   *   - Neither the name of Oracle nor the names of its
16   *     contributors may be used to endorse or promote products derived
17   *     from this software without specific prior written permission.
18   *
19   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20   * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21   * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22   * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30   */
31  
32  /*
33   * This source code is provided to illustrate the usage of a given feature
34   * or technique and has been deliberately simplified. Additional steps
35   * required for a production-quality application, such as security checks,
36   * input validation and proper error handling, might not be present in
37   * this sample code.
38   */
39  
40  
41  
42  import java.applet.Applet;
43  import java.awt.*;
44  import java.awt.event.*;
45  import java.io.*;
46  import java.net.*;
47  
48  
49  @SuppressWarnings("serial")
50  public class SpreadSheet extends Applet implements MouseListener, KeyListener {
51  
52      String title;
53      Font titleFont;
54      Color cellColor;
55      Color inputColor;
56      int cellWidth = 100;
57      int cellHeight = 15;
58      int titleHeight = 15;
59      int rowLabelWidth = 15;
60      Font inputFont;
61      boolean isStopped = false;
62      boolean fullUpdate = true;
63      int rows;
64      int columns;
65      int currentKey = -1;
66      int selectedRow = -1;
67      int selectedColumn = -1;
68      SpreadSheetInput inputArea;
69      Cell cells[][];
70      Cell current = null;
71  
72      @Override
73      public synchronized void init() {
74          String rs;
75  
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");
81          if (title == null) {
82              title = "Spreadsheet";
83          }
84          rs = getParameter("rows");
85          if (rs == null) {
86              rows = 9;
87          } else {
88              rows = Integer.parseInt(rs);
89          }
90          rs = getParameter("columns");
91          if (rs == null) {
92              columns = 5;
93          } else {
94              columns = Integer.parseInt(rs);
95          }
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++) {
100 
101                 cells[i][j] = new Cell(this,
102                         Color.lightGray,
103                         Color.black,
104                         cellColor,
105                         cellWidth - 2,
106                         cellHeight - 2);
107                 l[0] = (char) ((int) 'a' + j);
108                 rs = getParameter("" + new String(l) + (i + 1));
109                 if (rs != null) {
110                     cells[i][j].setUnparsedValue(rs);
111                 }
112             }
113         }
114 
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);
122     }
123 
124     public void setCurrentValue(float val) {
125         if (selectedRow == -1 || selectedColumn == -1) {
126             return;
127         }
128         cells[selectedRow][selectedColumn].setValue(val);
129         repaint();
130     }
131 
132     @Override
133     public void stop() {
134         isStopped = true;
135     }
136 
137     @Override
138     public void start() {
139         isStopped = false;
140     }
141 
142     @Override
143     public void destroy() {
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;
148                 }
149             }
150         }
151     }
152 
153     public void setCurrentValue(int type, String val) {
154         if (selectedRow == -1 || selectedColumn == -1) {
155             return;
156         }
157         cells[selectedRow][selectedColumn].setValue(type, val);
158         repaint();
159     }
160 
161     @Override
162     public void update(Graphics g) {
163         if (!fullUpdate) {
164             int cx, cy;
165 
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);
173                     }
174                 }
175             }
176         } else {
177             paint(g);
178             fullUpdate = false;
179         }
180     }
181 
182     public void recalculate() {
183         int i, j;
184 
185         //System.out.println("SpreadSheet.recalculate");
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;
192                 }
193             }
194         }
195         repaint();
196     }
197 
198     float evaluateFormula(Node n) {
199         float val = 0.0f;
200 
201         //System.out.println("evaluateFormula:");
202         //n.print(3);
203         if (n == null) {
204             //System.out.println("Null node");
205             return val;
206         }
207         switch (n.type) {
208             case Node.OP:
209                 val = evaluateFormula(n.left);
210                 switch (n.op) {
211                     case '+':
212                         val += evaluateFormula(n.right);
213                         break;
214                     case '*':
215                         val *= evaluateFormula(n.right);
216                         break;
217                     case '-':
218                         val -= evaluateFormula(n.right);
219                         break;
220                     case '/':
221                         val /= evaluateFormula(n.right);
222                         break;
223                 }
224                 break;
225             case Node.VALUE:
226                 //System.out.println("=>" + n.value);
227                 return n.value;
228             case Node.CELL:
229                 if (cells[n.row][n.column] == null) {
230                     //System.out.println("NULL at 193");
231                 } else {
232                     //System.out.println("=>" + cells[n.row][n.column].value);
233                     return cells[n.row][n.column].value;
234                 }
235         }
236 
237         //System.out.println("=>" + val);
238         return val;
239     }
240 
241     @Override
242     public synchronized void paint(Graphics g) {
243         int i, j;
244         int cx, cy;
245         char l[] = new char[1];
246 
247 
248         Dimension d = getSize();
249 
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);
261             if (i < rows) {
262                 g.setColor(Color.red);
263                 g.drawString("" + (i + 1), 2, cy + 12);
264             }
265         }
266 
267         g.setColor(Color.red);
268         cy = (rows + 3) * cellHeight + (cellHeight / 2);
269         for (i = 0; i < columns; i++) {
270             cx = i * cellWidth;
271             g.setColor(getBackground());
272             g.draw3DRect(cx + rowLabelWidth,
273                     2 * cellHeight, 1, d.height, true);
274             if (i < columns) {
275                 g.setColor(Color.red);
276                 l[0] = (char) ((int) 'A' + i);
277                 g.drawString(new String(l),
278                         cx + rowLabelWidth + (cellWidth / 2),
279                         cy);
280             }
281         }
282 
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);
289                 }
290             }
291         }
292 
293         g.setColor(getBackground());
294         g.draw3DRect(0, titleHeight,
295                 d.width,
296                 d.height - titleHeight,
297                 false);
298         inputArea.paint(g, 1, titleHeight + 1);
299     }
300 
301     //1.1 event handling
302     @Override
303     public void mouseClicked(MouseEvent e) {
304     }
305 
306     @Override
307     public void mousePressed(MouseEvent e) {
308         int x = e.getX();
309         int y = e.getY();
310         Cell cell;
311         if (y < (titleHeight + cellHeight)) {
312             selectedRow = -1;
313             if (y <= titleHeight && current != null) {
314                 current.deselect();
315                 current = null;
316             }
317             e.consume();
318         }
319         if (x < rowLabelWidth) {
320             selectedRow = -1;
321             if (current != null) {
322                 current.deselect();
323                 current = null;
324             }
325             e.consume();
326 
327         }
328         selectedRow = ((y - cellHeight - titleHeight) / cellHeight);
329         selectedColumn = (x - rowLabelWidth) / cellWidth;
330         if (selectedRow > rows
331                 || selectedColumn >= columns) {
332             selectedRow = -1;
333             if (current != null) {
334                 current.deselect();
335                 current = null;
336             }
337         } else {
338             if (selectedRow >= rows) {
339                 selectedRow = -1;
340                 if (current != null) {
341                     current.deselect();
342                     current = null;
343                 }
344                 e.consume();
345             }
346             if (selectedRow != -1) {
347                 cell = cells[selectedRow][selectedColumn];
348                 inputArea.setText(cell.getPrintString());
349                 if (current != null) {
350                     current.deselect();
351                 }
352                 current = cell;
353                 current.select();
354                 requestFocus();
355                 fullUpdate = true;
356                 repaint();
357             }
358             e.consume();
359         }
360     }
361 
362     @Override
363     public void mouseReleased(MouseEvent e) {
364     }
365 
366     @Override
367     public void mouseEntered(MouseEvent e) {
368     }
369 
370     @Override
371     public void mouseExited(MouseEvent e) {
372     }
373 
374     @Override
375     public void keyPressed(KeyEvent e) {
376     }
377 
378     @Override
379     public void keyTyped(KeyEvent e) {
380         fullUpdate = true;
381         inputArea.processKey(e);
382         e.consume();
383     }
384 
385     @Override
386     public void keyReleased(KeyEvent e) {
387     }
388 
389     @Override
390     public String getAppletInfo() {
391         return "Title: SpreadSheet \nAuthor: Sami Shaio \nA simple spread sheet.";
392     }
393 
394     @Override
395     public String[][] getParameterInfo() {
396         String[][] info = {
397             { "title", "string",
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." }
401         };
402         return info;
403     }
404 }
405 
406 
407 class CellUpdater extends Thread {
408 
409     Cell target;
410     InputStream dataStream = null;
411     StreamTokenizer tokenStream;
412     public volatile boolean run = true;
413 
414     public CellUpdater(Cell c) {
415         super("cell updater");
416         target = c;
417     }
418 
419     @Override
420     public void run() {
421         try {
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);
427 
428             while (run) {
429                 switch (tokenStream.nextToken()) {
430                     case StreamTokenizer.TT_EOF:
431                         dataStream.close();
432                         return;
433                     default:
434                         break;
435                     case StreamTokenizer.TT_NUMBER:
436                         target.setTransientValue((float) tokenStream.nval);
437                         if (!target.app.isStopped && !target.paused) {
438                             target.app.repaint();
439                         }
440                         break;
441                 }
442                 try {
443                     Thread.sleep(2000);
444                 } catch (InterruptedException e) {
445                     break;
446                 }
447             }
448         } catch (IOException e) {
449             return;
450         }
451     }
452 }
453 
454 
455 class Cell {
456 
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;
461     Node parseRoot;
462     boolean needRedisplay;
463     boolean selected = false;
464     boolean transientValue = false;
465     public int type = Cell.VALUE;
466     String valueString = "";
467     String printString = "v";
468     float value;
469     Color bgColor;
470     Color fgColor;
471     Color highlightColor;
472     int width;
473     int height;
474     SpreadSheet app;
475     CellUpdater updaterThread;
476     boolean paused = false;
477 
478     public Cell(SpreadSheet app,
479             Color bgColor,
480             Color fgColor,
481             Color highlightColor,
482             int width,
483             int height) {
484         this.app = app;
485         this.bgColor = bgColor;
486         this.fgColor = fgColor;
487         this.highlightColor = highlightColor;
488         this.width = width;
489         this.height = height;
490         needRedisplay = true;
491     }
492 
493     public void setRawValue(float f) {
494         valueString = Float.toString(f);
495         value = f;
496     }
497 
498     public void setValue(float f) {
499         setRawValue(f);
500         printString = "v" + valueString;
501         type = Cell.VALUE;
502         paused = false;
503         app.recalculate();
504         needRedisplay = true;
505     }
506 
507     public void setTransientValue(float f) {
508         transientValue = true;
509         value = f;
510         needRedisplay = true;
511         app.recalculate();
512     }
513 
514     public void setUnparsedValue(String s) {
515         switch (s.charAt(0)) {
516             case 'v':
517                 setValue(Cell.VALUE, s.substring(1));
518                 break;
519             case 'f':
520                 setValue(Cell.FORMULA, s.substring(1));
521                 break;
522             case 'l':
523                 setValue(Cell.LABEL, s.substring(1));
524                 break;
525             case 'u':
526                 setValue(Cell.URL, s.substring(1));
527                 break;
528         }
529     }
530 
531     /**
532      * Parse a spreadsheet formula. The syntax is defined as:
533      *
534      * formula -> value
535      * formula -> value op value
536      * value -> '(' formula ')'
537      * value -> cell
538      * value -> <number>
539      * op -> '+' | '*' | '/' | '-'
540      * cell -> <letter><number>
541      */
542     public String parseFormula(String formula, Node node) {
543         String subformula;
544         String restFormula;
545         Node left;
546         Node right;
547         char op;
548 
549         if (formula == null) {
550             return null;
551         }
552         subformula = parseValue(formula, node);
553         //System.out.println("subformula = " + subformula);
554         if (subformula == null || subformula.length() == 0) {
555             //System.out.println("Parse succeeded");
556             return null;
557         }
558         if (subformula.equals(formula)) {
559             //System.out.println("Parse failed");
560             return formula;
561         }
562 
563         // parse an operator and then another value
564         switch (op = subformula.charAt(0)) {
565             case 0:
566                 //System.out.println("Parse succeeded");
567                 return null;
568             case ')':
569                 //System.out.println("Returning subformula=" + subformula);
570                 return subformula;
571             case '+':
572             case '*':
573             case '-':
574             case '/':
575                 restFormula = subformula.substring(1);
576                 subformula = parseValue(restFormula, right = new Node());
577                 //System.out.println("subformula(2) = " + subformula);
578                 if (subformula == null ? restFormula != null : !subformula.
579                         equals(restFormula)) {
580                     //System.out.println("Parse succeeded");
581                     left = new Node(node);
582                     node.left = left;
583                     node.right = right;
584                     node.op = op;
585                     node.type = Node.OP;
586                     //node.print(3);
587                     return subformula;
588                 } else {
589                     //System.out.println("Parse failed");
590                     return formula;
591                 }
592             default:
593                 //System.out.println("Parse failed (bad operator): " + subformula);
594                 return formula;
595         }
596     }
597 
598     public String parseValue(String formula, Node node) {
599         char c = formula.charAt(0);
600         String subformula;
601         String restFormula;
602         float _value;
603         int row;
604         int column;
605 
606         //System.out.println("parseValue: " + formula);
607         restFormula = formula;
608         if (c == '(') {
609             //System.out.println("parseValue(" + formula + ")");
610             restFormula = formula.substring(1);
611             subformula = parseFormula(restFormula, node);
612             //System.out.println("rest=(" + subformula + ")");
613             if (subformula == null
614                     || subformula.length() == restFormula.length()) {
615                 //System.out.println("Failed");
616                 return formula;
617             } else if (!(subformula.charAt(0) == ')')) {
618                 //System.out.println("Failed (missing parentheses)");
619                 return formula;
620             }
621             restFormula = subformula;
622         } else if (c >= '0' && c <= '9') {
623             int i;
624 
625             //System.out.println("formula=" + formula);
626             for (i = 0; i < formula.length(); i++) {
627                 c = formula.charAt(i);
628                 if ((c < '0' || c > '9') && c != '.') {
629                     break;
630                 }
631             }
632             try {
633                 _value = Float.valueOf(formula.substring(0, i)).floatValue();
634             } catch (NumberFormatException e) {
635                 //System.out.println("Failed (number format error)");
636                 return formula;
637             }
638             node.type = Node.VALUE;
639             node.value = _value;
640             //node.print(3);
641             restFormula = formula.substring(i);
642             //System.out.println("value= " + value + " i=" + i +
643             //                     " rest = " + restFormula);
644             return restFormula;
645         } else if (c >= 'A' && c <= 'Z') {
646             int i;
647 
648             column = c - 'A';
649             restFormula = formula.substring(1);
650             for (i = 0; i < restFormula.length(); i++) {
651                 c = restFormula.charAt(i);
652                 if (c < '0' || c > '9') {
653                     break;
654                 }
655             }
656             row = Float.valueOf(restFormula.substring(0, i)).intValue();
657             //System.out.println("row = " + row + " column = " + column);
658             node.row = row - 1;
659             node.column = column;
660             node.type = Node.CELL;
661             //node.print(3);
662             if (i == restFormula.length()) {
663                 restFormula = null;
664             } else {
665                 restFormula = restFormula.substring(i);
666                 if (restFormula.charAt(0) == 0) {
667                     return null;
668                 }
669             }
670         }
671 
672         return restFormula;
673     }
674 
675     public void setValue(int type, String s) {
676         paused = false;
677         if (this.type == Cell.URL) {
678             updaterThread.run = false;
679             updaterThread = null;
680         }
681 
682         valueString = s;
683         this.type = type;
684         needRedisplay = true;
685         switch (type) {
686             case Cell.VALUE:
687                 setValue(Float.valueOf(s).floatValue());
688                 break;
689             case Cell.LABEL:
690                 printString = "l" + valueString;
691                 break;
692             case Cell.URL:
693                 printString = "u" + valueString;
694                 updaterThread = new CellUpdater(this);
695                 updaterThread.start();
696                 break;
697             case Cell.FORMULA:
698                 parseFormula(valueString, parseRoot = new Node());
699                 printString = "f" + valueString;
700                 break;
701         }
702         app.recalculate();
703     }
704 
705     public String getValueString() {
706         return valueString;
707     }
708 
709     public String getPrintString() {
710         return printString;
711     }
712 
713     public void select() {
714         selected = true;
715         paused = true;
716     }
717 
718     public void deselect() {
719         selected = false;
720         paused = false;
721         needRedisplay = true;
722         app.repaint();
723     }
724 
725     public void paint(Graphics g, int x, int y) {
726         if (selected) {
727             g.setColor(highlightColor);
728         } else {
729             g.setColor(bgColor);
730         }
731         g.fillRect(x, y, width - 1, height);
732         if (valueString != null) {
733             switch (type) {
734                 case Cell.VALUE:
735                 case Cell.LABEL:
736                     g.setColor(fgColor);
737                     break;
738                 case Cell.FORMULA:
739                     g.setColor(Color.red);
740                     break;
741                 case Cell.URL:
742                     g.setColor(Color.blue);
743                     break;
744             }
745             if (transientValue) {
746                 g.drawString("" + value, x, y + (height / 2) + 5);
747             } else {
748                 if (valueString.length() > 14) {
749                     g.drawString(valueString.substring(0, 14),
750                             x, y + (height / 2) + 5);
751                 } else {
752                     g.drawString(valueString, x, y + (height / 2) + 5);
753                 }
754             }
755         }
756         needRedisplay = false;
757     }
758 }
759 
760 
761 class Node {
762 
763     public static final int OP = 0;
764     public static final int VALUE = 1;
765     public static final int CELL = 2;
766     int type;
767     Node left;
768     Node right;
769     int row;
770     int column;
771     float value;
772     char op;
773 
774     public Node() {
775         left = null;
776         right = null;
777         value = 0;
778         row = -1;
779         column = -1;
780         op = 0;
781         type = Node.VALUE;
782     }
783 
784     public Node(Node n) {
785         left = n.left;
786         right = n.right;
787         value = n.value;
788         row = n.row;
789         column = n.column;
790         op = n.op;
791         type = n.type;
792     }
793 
794     public void indent(int ind) {
795         for (int i = 0; i < ind; i++) {
796             System.out.print(" ");
797         }
798     }
799 
800     public void print(int indentLevel) {
801         char l[] = new char[1];
802         indent(indentLevel);
803         System.out.println("NODE type=" + type);
804         indent(indentLevel);
805         switch (type) {
806             case Node.VALUE:
807                 System.out.println(" value=" + value);
808                 break;
809             case Node.CELL:
810                 l[0] = (char) ((int) 'A' + column);
811                 System.out.println(" cell=" + new String(l) + (row + 1));
812                 break;
813             case Node.OP:
814                 System.out.println(" op=" + op);
815                 left.print(indentLevel + 3);
816                 right.print(indentLevel + 3);
817                 break;
818         }
819     }
820 }
821 
822 
823 class InputField {
824 
825     int maxchars = 50;
826     int cursorPos = 0;
827     Applet app;
828     String sval;
829     char buffer[];
830     int nChars;
831     int width;
832     int height;
833     Color bgColor;
834     Color fgColor;
835 
836     public InputField(String initValue, Applet app, int width, int height,
837             Color bgColor, Color fgColor) {
838         this.width = width;
839         this.height = height;
840         this.bgColor = bgColor;
841         this.fgColor = fgColor;
842         this.app = app;
843         buffer = new char[maxchars];
844         nChars = 0;
845         if (initValue != null) {
846             initValue.getChars(0, initValue.length(), this.buffer, 0);
847             nChars = initValue.length();
848         }
849         sval = initValue;
850     }
851 
852     public void setText(String val) {
853         int i;
854 
855         for (i = 0; i < maxchars; i++) {
856             buffer[i] = 0;
857         }
858         if (val == null) {
859             sval = "";
860         } else {
861             sval = val;
862         }
863         nChars = sval.length();
864         sval.getChars(0, sval.length(), buffer, 0);
865     }
866 
867     public String getValue() {
868         return sval;
869     }
870 
871     public void paint(Graphics g, int x, int y) {
872         g.setColor(bgColor);
873         g.fillRect(x, y, width, height);
874         if (sval != null) {
875             g.setColor(fgColor);
876             g.drawString(sval, x, y + (height / 2) + 3);
877         }
878     }
879 
880     public void processKey(KeyEvent e) {
881         char ch = e.getKeyChar();
882         switch (ch) {
883             case '\b': // delete
884                 if (nChars > 0) {
885                     nChars--;
886                     sval = new String(buffer, 0, nChars);
887                 }
888                 break;
889             case '\n': // return
890                 selected();
891                 break;
892             default:
893                 if (nChars < maxchars && ch >= '0') {
894                     buffer[nChars++] = ch;
895                     sval = new String(buffer, 0, nChars);
896                 }
897         }
898         app.repaint();
899     }
900 
901     public void keyReleased(KeyEvent e) {
902     }
903 
904     public void selected() {
905     }
906 }
907 
908 
909 class SpreadSheetInput
910         extends InputField {
911 
912     public SpreadSheetInput(String initValue,
913             SpreadSheet app,
914             int width,
915             int height,
916             Color bgColor,
917             Color fgColor) {
918         super(initValue, app, width, height, bgColor, fgColor);
919     }
920 
921     @Override
922     public void selected() {
923         float f;
924         sval = ("".equals(sval)) ? "v" : sval;
925         switch (sval.charAt(0)) {
926             case 'v':
927                 String s = sval.substring(1);
928                 try {
929                     int i;
930                     for (i = 0; i < s.length(); i++) {
931                         char c = s.charAt(i);
932                         if (c < '0' || c > '9') {
933                             break;
934                         }
935                     }
936                     s = s.substring(0, i);
937                     f = Float.valueOf(s).floatValue();
938                     ((SpreadSheet) app).setCurrentValue(f);
939                 } catch (NumberFormatException e) {
940                     System.out.println("Not a float: '" + s + "'");
941                 }
942                 break;
943             case 'l':
944                 ((SpreadSheet) app).setCurrentValue(Cell.LABEL,
945                         sval.substring(1));
946                 break;
947             case 'u':
948                 ((SpreadSheet) app).setCurrentValue(Cell.URL, sval.substring(1));
949                 break;
950             case 'f':
951                 ((SpreadSheet) app).setCurrentValue(Cell.FORMULA,
952                         sval.substring(1));
953                 break;
954         }
955     }
956 }