Source Code Cross Referenced for DataTableInput.java in  » Science » jcm1-source » edu » hws » jcm » awt » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Science » jcm1 source » edu.hws.jcm.awt 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*************************************************************************
0002:         *                                                                        *
0003:         *   1) This source code file, in unmodified form, and compiled classes   *
0004:         *      derived from it can be used and distributed without restriction,  *
0005:         *      including for commercial use.  (Attribution is not required       *
0006:         *      but is appreciated.)                                              *
0007:         *                                                                        *
0008:         *    2) Modified versions of this file can be made and distributed       *
0009:         *       provided:  the modified versions are put into a Java package     *
0010:         *       different from the original package, edu.hws;  modified          *
0011:         *       versions are distributed under the same terms as the original;   *
0012:         *       and the modifications are documented in comments.  (Modification *
0013:         *       here does not include simply making subclasses that belong to    *
0014:         *       a package other than edu.hws, which can be done without any      *
0015:         *       restriction.)                                                    *
0016:         *                                                                        *
0017:         *   David J. Eck                                                         *
0018:         *   Department of Mathematics and Computer Science                       *
0019:         *   Hobart and William Smith Colleges                                    *
0020:         *   Geneva, New York 14456,   USA                                        *
0021:         *   Email: eck@hws.edu          WWW: http://math.hws.edu/eck/            *
0022:         *                                                                        *
0023:         *************************************************************************/package edu.hws.jcm.awt;
0024:
0025:        import edu.hws.jcm.data.*;
0026:        import java.awt.*;
0027:        import java.awt.event.*;
0028:        import java.util.Vector;
0029:        import java.io.*;
0030:
0031:        /**
0032:         *  A DataTableInput lets the user input a grid of real numbers arranged
0033:         *  in rows and columns.  Each column has a name, and rows are numberd
0034:         *  starting from 1.  The column names and row numbers can be
0035:         *  displayed, optionally.  By default, a new row is added automatically
0036:         *  if the user moves down out of the last row by pressing return
0037:         *  or down-arrow, provided the last row is non-empty.  Rows can also be
0038:         *  added programmatically.  Columns are usually added in the constructor,
0039:         *  but they can also be added later.  If the user leaves a cell
0040:         *  at a time when the content of that cell does not represent a legal
0041:         *  number, then the message "bad input" will be displayed in the cell.
0042:         *  <p>A DataTableInput can be given a name and can then be added to a parser. 
0043:         *  The parser will then recognize the name of the table.  In an expression, the table name
0044:         *  can be followed by a ".", then one of the column names from table, then
0045:         *  an expression in parentheses.  This represents a reference to the number in
0046:         *  one of the cells in the table.  The expression gives the number of
0047:         *  the row, where rows are numbered starting from 1.  For example:
0048:         *  "data.A(3)" represents the third number in column "A" in a table
0049:         *  named data.  The table name and "." can also be followed by the word "sum" and
0050:         *  an expression in parentheses.  An expression used in this way
0051:         *  can include references to the column names in the table and to
0052:         *  the special value "rowNumber".  The value of expression is computed
0053:         *  for each row in the table and the sum of the values is computed.  In the expression that
0054:         *  is being summed, a column name represents the 
0055:         *  number in that column and rowNumber represents the number of
0056:         *  the row.  For example:  "data.sum(A^2)".  Finally, the "." can be
0057:         *  followed by the word "count".  "count" can, optionally be followed
0058:         *  by an empty pair of parentheses.  This represents the number of
0059:         *  rows.  For example:  "data.count".   Empty rows at the bottom
0060:         *  of the table are ignored in both "sum" and "count".
0061:         *  <p>Note that rows are numbered starting from 1 because row numbers
0062:         *  can be visible to the user, who shouldn't be subjected to zero-based
0063:         *  numbering.  However, columns are always numbered from zero.
0064:         */
0065:        public class DataTableInput extends Panel implements  ParserExtension {
0066:
0067:            private String objectName; // The name of the DataTableObject
0068:
0069:            private Vector rows; // Vector of double[].  Each entry holds
0070:            // the values in a row of a table.  An
0071:            // entry is null for an empty row.
0072:
0073:            private Vector rowStrings; // Row values as Strings.  The string "bad input"
0074:            // means that the string in the cell does not
0075:            // represent a legal number
0076:
0077:            private String[] columnName; // The name to be used to refer to the column
0078:            // in expressions.  Also used as a label at the
0079:            // top of the column.
0080:
0081:            private int columnCount; // Number of columns
0082:
0083:            private int currentRow = 1; // The number of the "currentRow" which is
0084:            // specified in the setCurrentRowNumber() method.
0085:
0086:            private double emptyCellValue = Double.NaN; // An empty cell is considered to have this value.
0087:
0088:            private boolean throwErrors = false; // Should an error be thrown when getValue(col,row) 
0089:            // sees "bad input" in the cell.  If not, then the
0090:            // value is considered to be Double.NaN.
0091:
0092:            private boolean autoAddRows = true; // If this is true, an empty row is added
0093:            // when the user presses enter or hits
0094:            // down-arrow while in the last row, if that row
0095:            // is empty.
0096:
0097:            private boolean showColumnTitles; // If true, column names are shown above the columns
0098:
0099:            private boolean showRowNumbers; // If true, row numbers are shown to left of each row
0100:
0101:            private DisplayPanel canvas; // Where the table appears; nested class DisplayPanel is defined below.
0102:
0103:            private long serialNumber; // This is incremented each time the table changes.
0104:
0105:            private Color labelBackground = new Color(220, 220, 220); // Colors of various things.
0106:            private Color cellBackground = new Color(255, 255, 220);
0107:            private Color blankBackground = Color.gray;
0108:            private Color gridColor = Color.blue;
0109:
0110:            /**
0111:             *  Create a DataTableInput with no columns.  Columns can be added later
0112:             *  using the addColumn() methods.  The table initially has no name.
0113:             */
0114:            public DataTableInput() {
0115:                this (null, 0);
0116:            }
0117:
0118:            /**  
0119:             *   Create a table with the specified column names.  If columnNames
0120:             *   is null, the number of columns is zero.  The name can be null, if you
0121:             *   don't need a name for the table.  The length of the array determines
0122:             *   the number of columns in the table.
0123:             */
0124:            public DataTableInput(String name, String[] columnNames) {
0125:                this (name, columnNames == null ? 0 : columnNames.length);
0126:                if (columnNames != null)
0127:                    for (int i = 0; i < columnNames.length; i++)
0128:                        setColumnName(i, columnNames[i]);
0129:            }
0130:
0131:            /**  
0132:             *   Create a table with the specified number of columns,
0133:             *   named "A", "B", etc.  The name can be null, if you
0134:             *   don't need a name for the table.  The number of columns can be zero.
0135:             */
0136:            public DataTableInput(String name, int columns) {
0137:                if (columns < 0)
0138:                    columns = 0;
0139:                setName(name);
0140:                rowStrings = new Vector();
0141:                rows = new Vector();
0142:                rowStrings.addElement(null);
0143:                rows.addElement(null);
0144:                columnName = new String[columns];
0145:                for (int i = 0; i < columns; i++)
0146:                    columnName[i] = "" + (char) ((int) 'A' + i);
0147:                canvas = new DisplayPanel();
0148:                setLayout(new BorderLayout());
0149:                setBackground(Color.lightGray);
0150:                add(canvas, BorderLayout.CENTER);
0151:                add(canvas.vScroll, BorderLayout.EAST);
0152:                columnCount = columns;
0153:            }
0154:
0155:            //------------------------- Data access and variables
0156:
0157:            /**
0158:             *   Required by the ParserExtension interface and not meant to be called directly.
0159:             *   This is called by a parser if it encounters the name of the table in an
0160:             *   expression.  It parses the complete table reference, such as "data.A(3)"
0161:             *   or "data.sum(A^2)".
0162:             */
0163:            public void doParse(Parser parser, ParserContext context) {
0164:                int tok = context.next();
0165:                if (tok != ParserContext.OPCHARS
0166:                        || !context.tokenString.equals("."))
0167:                    throw new ParseError(
0168:                            "Expected a '.' after the name of a data table.",
0169:                            context);
0170:                tok = context.next();
0171:                if (tok != ParserContext.IDENTIFIER)
0172:                    throw new ParseError(
0173:                            "Expected 'sum', 'count', or the name of a column after data table name.",
0174:                            context);
0175:                String commandName = context.tokenString;
0176:                int command = -10;
0177:                for (int i = 0; i < columnCount; i++)
0178:                    if (commandName.equalsIgnoreCase(getColumnName(i))) {
0179:                        command = i;
0180:                        break;
0181:                    }
0182:                if (command == -10) {
0183:                    if (commandName.equalsIgnoreCase("sum"))
0184:                        command = -1;
0185:                    else if (commandName.equalsIgnoreCase("count"))
0186:                        command = -2;
0187:                }
0188:                if (command == -10)
0189:                    throw new ParseError("Unrecognized table command \""
0190:                            + commandName + "\".", context);
0191:                if (command == -2) {
0192:                    if (context.look() == ParserContext.OPCHARS
0193:                            && context.tokenString.equals("(")) {
0194:                        context.next();
0195:                        if (context.next() != ParserContext.OPCHARS
0196:                                || !context.tokenString.equals(")"))
0197:                            throw new ParseError(
0198:                                    "Missing right parenthesis; \"count\" does not take a parameter.",
0199:                                    context);
0200:                    }
0201:                    context.prog.addCommandObject(new DTEC(-2, null));
0202:                    return;
0203:                }
0204:                if (context.next() != ParserContext.OPCHARS
0205:                        || !context.tokenString.equals("("))
0206:                    throw new ParseError(
0207:                            "Expected a left parentheses after table command \""
0208:                                    + commandName + "\".", context);
0209:                ExpressionProgram saveProg = context.prog;
0210:                ExpressionProgram tableProg = new ExpressionProgram();
0211:                context.prog = tableProg;
0212:                if (command == -1) {
0213:                    context.mark();
0214:                    for (int i = 0; i < columnCount; i++)
0215:                        context.add(getColumnVariable(i));
0216:                    context.add(getRowNumberVariable());
0217:                }
0218:                parser.parseExpression(context);
0219:                context.prog = saveProg;
0220:                if (context.next() != ParserContext.OPCHARS
0221:                        || !context.tokenString.equals(")"))
0222:                    throw new ParseError("Missing right parenthesis.", context);
0223:                context.prog.addCommandObject(new DTEC(command, tableProg));
0224:                if (command == -1)
0225:                    context.revert();
0226:            }
0227:
0228:            /**
0229:             *  Return the number of rows in the table, ignoring empty rows at the bottom
0230:             *  of the table.  Note that an empty row that precedes some non-empty row
0231:             *  is included in the count.
0232:             */
0233:            public int getNonEmptyRowCount() {
0234:                int rowCt = rows.size();
0235:                while (rowCt > 0 && rows.elementAt(rowCt - 1) == null)
0236:                    rowCt--;
0237:                return rowCt;
0238:            }
0239:
0240:            /**  
0241:             *   Get the number in the specified row and column.  Rows are numberd starting
0242:             *   from 1, but columns are numbered starting from zero.  If the specified
0243:             *   cell does not exist in the table, Double.NaN is returned.  If the cell is empty,
0244:             *   emptyCellValue is returned.  If the content of the cell does not define
0245:             *   a legal real number, then the action depends on the value of the missingValueIsError
0246:             *   property:  If this property is true, then a JCMError is thrown; if it is
0247:             *   false, then Double.NaN is returned.
0248:             */
0249:            public double getCellContents(int row, int col) {
0250:                if (row < 1 || row > rows.size() || col < 0
0251:                        || col > columnCount)
0252:                    return Double.NaN;
0253:                return canvas.getValue(col, row);
0254:            }
0255:
0256:            /**
0257:             *  Put the given real number, val, in the cell in the specified row
0258:             *  and column, where rows are numbered starting from 1 and columns are
0259:             *  numbered starting from zero.  This is ignored if the specified row
0260:             *  and column do not exist in the table.
0261:             */
0262:            public void setCellContents(int row, int col, double val) {
0263:                if (row < 1 || row > rows.size() || col < 0
0264:                        || col > columnCount)
0265:                    return;
0266:                canvas.setValue(col, row, val);
0267:            }
0268:
0269:            /**
0270:             *  Set the current row in the table.  If the parameter is less than 1, the current
0271:             *  row is set to 1.
0272:             *  (The table keeps track of a "current row number", which is always greater than
0273:             *  or equal to 1.  This row number is used when a column variable or row number
0274:             *  variable is evaluated.  These variables can be added to a parser using the
0275:             *  addVariablesToParser method, and can then be used in expressions parsed by
0276:             *  that parser.  When the row number variable, which is named rowNumber, is 
0277:             *  evaluated, its value is the current row number in the table.  When a column
0278:             *  variable is evaluated, its value is the number in the cell in the associated
0279:             *  column and in the current row.  The setCurrentRowNumber() method, in combination
0280:             *  with the getNonEmptyRowCount() method allow you to iterate through the rows
0281:             *  of the table and evaluate the expression for each row.)
0282:             */
0283:            public void setCurrentRowNumber(int i) {
0284:                currentRow = (i < 1) ? 1 : i;
0285:            }
0286:
0287:            /**
0288:             *  Return the current row number.
0289:             */
0290:            public int getCurrentRowNumber() {
0291:                return currentRow;
0292:            }
0293:
0294:            /**
0295:             *  Return a column variable for the specified column, where columns are
0296:             *  numbered starting from 1.  The value of this variable is the number
0297:             *  in the specified column and in the current row of the table (as set
0298:             *  by the setCurrentRowNumber() method.)  The name of the variable is
0299:             *  the name of the column.  This method is protected since variables are
0300:             *  not meant to be used as regular variables.  But they can be added to
0301:             *  a Parser by calling the addVariablesToParser() method.)
0302:             */
0303:            protected Variable getColumnVariable(final int columnNum) {
0304:                if (columnNum < 0 || columnNum >= columnCount)
0305:                    throw new IllegalArgumentException(
0306:                            "Column number out of range.");
0307:                return new Variable(getColumnName(columnNum), 0) {
0308:                    public void setVal(double v) {
0309:                        if (currentRow < rows.size())
0310:                            canvas.setValue(columnNum, currentRow, v);
0311:                        super .setVal(v);
0312:                    }
0313:
0314:                    public double getVal() {
0315:                        if (currentRow > rows.size())
0316:                            return Double.NaN;
0317:                        else
0318:                            return canvas.getValue(columnNum, currentRow);
0319:                    }
0320:                };
0321:            }
0322:
0323:            /**
0324:             *  Get a variable that represents the current row number in the table,
0325:             *  as set by the setCurrentRowNumber() method.  The name of the
0326:             *  variable is rowNumber.
0327:             */
0328:            protected Variable getRowNumberVariable() {
0329:                return new Variable("rowNumber", 0) {
0330:                    public void setVal(double v) {
0331:                        int val = (int) (v + 0.5);
0332:                        if (val < 1 || val > getNonEmptyRowCount())
0333:                            val = getNonEmptyRowCount() + 1;
0334:                        currentRow = val;
0335:                        super .setVal(val);
0336:                    }
0337:
0338:                    public double getVal() {
0339:                        return currentRow;
0340:                    }
0341:                };
0342:            }
0343:
0344:            /**
0345:             *  Add a row number variable (from the getRowNumberVariable() method) and
0346:             *  a column variable for each column (from the getColumnVariable() method)
0347:             *  to the parser.  The parser will then be able to parse expressions that
0348:             *  refer to these variables.  The value of such an expression depends on
0349:             *  the current row number, as set by setCurrentRowNumber().
0350:             */
0351:            public void addVariablesToParser(Parser p) {
0352:                p.add(getRowNumberVariable());
0353:                for (int i = 0; i < columnCount; i++) {
0354:                    p.add(getColumnVariable(i));
0355:                }
0356:            }
0357:
0358:            /**
0359:             *  Get the serial number of the table.  This is incremented each time the
0360:             *  table changes in any way.
0361:             */
0362:            public long getSerialNumber() {
0363:                return serialNumber;
0364:            }
0365:
0366:            //-------------------------
0367:
0368:            /**
0369:             *  Set the throwErrors property.  If this is true, then a JCMError is thrown when
0370:             *  an attempt is made to use the value of a cell that contains an invalid String.
0371:             *  Note that referring to an empty cell is not an error.  The default value is true. 
0372:             */
0373:            public void setThrowErrors(boolean throwErr) {
0374:                throwErrors = throwErr;
0375:            }
0376:
0377:            /**
0378:             *  Get the value of the throwErrors property, which determines whether an error
0379:             *  is thrown when an attempt is made to refer to the value of a cell that
0380:             *  contains an invalid string.
0381:             */
0382:            public boolean getThrowErrors() {
0383:                return throwErrors;
0384:            }
0385:
0386:            /**
0387:             *  Set the value that should be returned when the value of an empty cell is
0388:             *  requested.  The default value is Double.NaN.  Another plausible value, in
0389:             *  some circumstances, would be zero.
0390:             */
0391:            public void setEmptyCellValue(double val) {
0392:                emptyCellValue = val;
0393:            }
0394:
0395:            /**
0396:             *  Get the value that is represented by an empty cell.
0397:             */
0398:            public double getEmptyCellValue() {
0399:                return emptyCellValue;
0400:            }
0401:
0402:            /**
0403:             *  If the value of autoAddRows is true, then an empty row is added to the table
0404:             *  automatically when the user attempts to move down from the last row of
0405:             *  the table, provided that the last row is non-empty (so there can only be
0406:             *  one auto-added row at a time).  If the user leaves this row while it is
0407:             *  still empty, it will automatically be deleted.  The default value is true.
0408:             */
0409:            public void setAutoAddRows(boolean auto) {
0410:                // Set the autoAddRows property. 
0411:                autoAddRows = auto;
0412:                canvas.lastRowAutoAdded = false;
0413:            }
0414:
0415:            /**
0416:             *  Get the value of the autoAddRows property, which determines whether empty 
0417:             *  rows are automatically added to the bottom of the table when needed.
0418:             */
0419:            public boolean getAutoAddRows() {
0420:                return autoAddRows;
0421:            }
0422:
0423:            /**
0424:             *  Set the name of this DataTableInput.  This is only needed if the table is
0425:             *  to be added to a parser.  The name should be a legal identifier.
0426:             */
0427:            public void setName(String name) {
0428:                objectName = name;
0429:            }
0430:
0431:            /**
0432:             *  Get the name of the DataInputTable (which might be null).
0433:             */
0434:            public String getName() {
0435:                return objectName;
0436:            }
0437:
0438:            /**
0439:             *  Set the name of column number i, where columns are numbered starting
0440:             *  from zero.  If column variables are to be used or if
0441:             *  the DataTableInput itself is to be added to a parser, then the name should
0442:             *  be a legal identifier.  If the showColumnTitles property is set to true,
0443:             *  then column names are shown at the top of the table.
0444:             */
0445:            public void setColumnName(int i, String name) {
0446:                if (name != null)
0447:                    columnName[i] = name;
0448:            }
0449:
0450:            /**
0451:             *  Get the name of column number i, where columns are numbered starting from zero.
0452:             */
0453:            public String getColumnName(int i) {
0454:                return columnName[i];
0455:            }
0456:
0457:            /**
0458:             *  Add the specified number of empty rows at the bottom of the table.  If you
0459:             *  want a table with a fixed number of rows, add them with this method and
0460:             *  set the autoAddRows property to false.
0461:             */
0462:            public void addRows(int rowCt) {
0463:                canvas.addRows(rowCt, rows.size());
0464:            }
0465:
0466:            /**
0467:             *  Insert a row before the row that contains the cell that the user is editing.
0468:             */
0469:            public void insertRow() {
0470:                canvas.addRows(1, canvas.activeRow);
0471:            }
0472:
0473:            /**
0474:             *  Delete the row that contains the cell that the user is editing.  However,
0475:             *  if that is the only row in the table, just make the row empty.
0476:             */
0477:            public void deleteCurrentRow() {
0478:                if (canvas.activeRow == rows.size() - 1 && rows.size() > 1) {
0479:                    canvas.setActive(canvas.activeRow - 1, canvas.activeColumn);
0480:                    rows.removeElementAt(canvas.activeRow + 1);
0481:                    rowStrings.removeElementAt(canvas.activeRow + 1);
0482:                } else {
0483:                    rows.removeElementAt(canvas.activeRow);
0484:                    rowStrings.removeElementAt(canvas.activeRow);
0485:                }
0486:                if (rows.size() == 0) {
0487:                    rows.addElement(null);
0488:                    rowStrings.addElement(null);
0489:                }
0490:                String[] vals = (String[]) rowStrings
0491:                        .elementAt(canvas.activeRow);
0492:                if (vals == null || vals[canvas.activeColumn] == null)
0493:                    canvas.input.setText("");
0494:                else
0495:                    canvas.input.setText(vals[canvas.activeColumn]);
0496:                canvas.checkScroll();
0497:                canvas.repaint();
0498:                if (canvas.rowLabelCanvas != null)
0499:                    canvas.rowLabelCanvas.repaint();
0500:                if (canvas.columnLabelCanvas != null)
0501:                    canvas.columnLabelCanvas.repaint();
0502:                serialNumber++;
0503:            }
0504:
0505:            /**
0506:             *  Remove all rows from the table, leaving just one empty row.
0507:             */
0508:            public void clear() {
0509:                rows = new Vector();
0510:                rowStrings = new Vector();
0511:                rows.addElement(null);
0512:                rowStrings.addElement(null);
0513:                canvas.setActive(0, 0);
0514:                canvas.checkScroll();
0515:                canvas.repaint();
0516:                if (canvas.rowLabelCanvas != null)
0517:                    canvas.rowLabelCanvas.repaint();
0518:                if (canvas.columnLabelCanvas != null)
0519:                    canvas.columnLabelCanvas.repaint();
0520:                serialNumber++;
0521:            }
0522:
0523:            /**
0524:             * Get the number of columns in the table.
0525:             */
0526:            public int getColumnCount() {
0527:                return columnName.length;
0528:            }
0529:
0530:            /**
0531:             *  Add a column at the right side of the table, with all cells initially
0532:             *  empty.  The name of the columns will be single letter such as 'A', 'B', ...
0533:             */
0534:            public int addColumn() {
0535:                return addColumn(null);
0536:            }
0537:
0538:            /**
0539:             *  Add a column with the specified name at the right side of the table, with all cells initially
0540:             *  empty.  This is inefficient if the table already contains a bunch of non-empty rows.
0541:             */
0542:            public int addColumn(String name) {
0543:                int newSize = columnName.length + 1;
0544:                String[] newNames = new String[newSize];
0545:                for (int i = 0; i < columnName.length; i++)
0546:                    newNames[i] = columnName[i];
0547:                if (name == null)
0548:                    newNames[newSize - 1] = ""
0549:                            + (char) ((int) 'A' + newSize - 1);
0550:                else
0551:                    newNames[newSize - 1] = name;
0552:                columnName = newNames;
0553:                int rowCt = rows.size();
0554:                for (int i = 0; i < rowCt; i++) {
0555:                    if (rows.elementAt(i) != null) {
0556:                        double[] oldRow = (double[]) rows.elementAt(i);
0557:                        double[] newRow = new double[newSize];
0558:                        for (int j = 0; j < oldRow.length; j++)
0559:                            newRow[j] = oldRow[j];
0560:                        newRow[newSize - 1] = Double.NaN;
0561:                        rows.setElementAt(newRow, i);
0562:                    }
0563:                    if (rowStrings.elementAt(i) != null) {
0564:                        String[] oldRow = (String[]) rows.elementAt(i);
0565:                        String[] newRow = new String[newSize];
0566:                        for (int j = 0; j < oldRow.length; j++)
0567:                            newRow[j] = oldRow[j];
0568:                        rowStrings.setElementAt(newRow, i);
0569:                    }
0570:                }
0571:                if (canvas.hScroll != null)
0572:                    canvas.checkScroll();
0573:                canvas.repaint();
0574:                if (canvas.columnLabelCanvas != null)
0575:                    canvas.columnLabelCanvas.repaint();
0576:                columnCount = columnName.length;
0577:                serialNumber++;
0578:                return columnCount - 1;
0579:            }
0580:
0581:            /**
0582:             *  Test whether the column name is shown at the top of each column.
0583:             */
0584:            public boolean getShowColumnTitles() {
0585:                return showColumnTitles;
0586:            }
0587:
0588:            /**
0589:             *  If set to true, then the column name is shown at the top of each column.  The
0590:             *  default value is false.  This is meant to be called before the table has been
0591:             *  shown on the screen, such as in the init() method of an applet.  If you call it
0592:             *  after the table has already been shown, you will have to validate the panel
0593:             *  yourself.
0594:             */
0595:            public void setShowColumnTitles(boolean show) {
0596:                if (show == showColumnTitles)
0597:                    return;
0598:                showColumnTitles = show;
0599:                if (showColumnTitles) {
0600:                    canvas.makeColumnLabelCanvas();
0601:                    add(canvas.columnLabelCanvas, BorderLayout.NORTH);
0602:                } else {
0603:                    remove(canvas.columnLabelCanvas);
0604:                    canvas.columnLabelCanvas = null;
0605:                }
0606:            }
0607:
0608:            /**
0609:             *  Test whether row numbers are shown.
0610:             */
0611:            public boolean getShowRowNumbers() {
0612:                return showRowNumbers;
0613:            }
0614:
0615:            /**
0616:             *  If set to true, then the row number is shown at the left of each row.  The
0617:             *  default value is false.  This is meant to be called before the table has been
0618:             *  shown on the screen, such as in the init() method of an applet.  If you call it
0619:             *  after the table has already been shown, you will have to validate the panel
0620:             *  yourself.
0621:             */
0622:            public void setShowRowNumbers(boolean show) {
0623:                if (show == showRowNumbers)
0624:                    return;
0625:                showRowNumbers = show;
0626:                if (showRowNumbers) {
0627:                    canvas.makeRowLabelCanvas();
0628:                    add(canvas.rowLabelCanvas, BorderLayout.WEST);
0629:                } else {
0630:                    remove(canvas.rowLabelCanvas);
0631:                    canvas.rowLabelCanvas = null;
0632:                }
0633:            }
0634:
0635:            /**
0636:             *   Returns the color that is used as a background for row numbers and column titles.
0637:             */
0638:            public Color getLabelBackground() {
0639:                return labelBackground;
0640:            }
0641:
0642:            /**
0643:             *  Set the color to be used as a background for row numbers and column titles.
0644:             *  The default is a very light gray.
0645:             */
0646:            public void setLabelBackground(Color color) {
0647:                if (color != null)
0648:                    labelBackground = color;
0649:            }
0650:
0651:            /**
0652:             *   Returns the color that is used as a background for cells in the table.
0653:             */
0654:            public Color getCellBackground() {
0655:                return cellBackground;
0656:            }
0657:
0658:            /**
0659:             *  Set the color to be used as a background for cells in the table.
0660:             *  The default is a light yellow.
0661:             */
0662:            public void setCellBackground(Color color) {
0663:                if (color != null)
0664:                    cellBackground = color;
0665:            }
0666:
0667:            /**
0668:             *  Returns the color that is used for blank areas in the table, below the
0669:             *  rows of cells.
0670:             */
0671:            public Color getBlankBackground() {
0672:                return blankBackground;
0673:            }
0674:
0675:            /**
0676:             *  Get the color to be used as a background blank areas in the table, below the
0677:             *  rows of cells.  The default is a gray.
0678:             */
0679:            public void setBlankBackground(Color color) {
0680:                if (color != null)
0681:                    blankBackground = color;
0682:            }
0683:
0684:            /**
0685:             *  Returns the color that is used for the lines between cells in the table.
0686:             */
0687:            public Color getGridColor() {
0688:                return gridColor;
0689:            }
0690:
0691:            /**
0692:             *  Get the color to be used for the lines between cells in the table.
0693:             *  The default is a blue.
0694:             */
0695:            public void setGridColor(Color color) {
0696:                if (color != null)
0697:                    gridColor = color;
0698:            }
0699:
0700:            /**
0701:             *  Read data for table from the specified Reader.  One row is filled
0702:             *  from each non-empty line of input.  The line should contain 
0703:             *  numbers separated by spaces/tabs/commas.  The word "undefined"
0704:             *  can be used to represent an empty cell.  Otherwise, if a non-number is
0705:             *  encountered, an error occurs.  If not enough numbers are
0706:             *  found on a line, the extra columns are filled with empties.  After
0707:             *  filling all columns, extra data on the line is ignored.
0708:             *  Data currently in the table is removed and replaced (if no error 
0709:             *  occurs during reading).  In the case of an error, if throwErrors is
0710:             *  true, then a JCMError is thrown; if throwErrors is false, no
0711:             *  error is thrown, but the return value is false.  If no error occurs,
0712:             *  the return value is true.  If an error occurs, the previous data
0713:             *  in the table is left unchanged.
0714:             */
0715:            public boolean readFromStream(Reader in) {
0716:                Vector newRows = new Vector();
0717:                int cols = columnCount;
0718:                try {
0719:                    StreamTokenizer tokenizer = new StreamTokenizer(in);
0720:                    tokenizer.resetSyntax();
0721:                    tokenizer.eolIsSignificant(true);
0722:                    tokenizer.whitespaceChars(',', ',');
0723:                    tokenizer.whitespaceChars(' ', ' ');
0724:                    tokenizer.whitespaceChars('\t', '\t');
0725:                    tokenizer.wordChars('a', 'z');
0726:                    tokenizer.wordChars('A', 'Z');
0727:                    tokenizer.wordChars('0', '9');
0728:                    tokenizer.wordChars('.', '.');
0729:                    tokenizer.wordChars('+', '+');
0730:                    tokenizer.wordChars('-', '-');
0731:                    int token = tokenizer.nextToken();
0732:                    while (true) {
0733:                        while (token == StreamTokenizer.TT_EOL)
0734:                            // ignore empty lines
0735:                            token = tokenizer.nextToken();
0736:                        if (token == StreamTokenizer.TT_EOF)
0737:                            break;
0738:                        double[] row = new double[cols];
0739:                        for (int i = 0; i < cols; i++) {
0740:                            if (token == StreamTokenizer.TT_EOL
0741:                                    || token == StreamTokenizer.TT_EOF)
0742:                                row[i] = Double.NaN;
0743:                            else if (token == StreamTokenizer.TT_WORD) {
0744:                                if (tokenizer.sval
0745:                                        .equalsIgnoreCase("undefined"))
0746:                                    row[i] = Double.NaN;
0747:                                else {
0748:                                    try {
0749:                                        Double d = new Double(tokenizer.sval);
0750:                                        row[i] = d.doubleValue();
0751:                                    } catch (NumberFormatException e) {
0752:                                        throw new IOException(
0753:                                                "Illegal non-numeric data ("
0754:                                                        + tokenizer.sval
0755:                                                        + ") encountered.");
0756:                                    }
0757:                                }
0758:                                token = tokenizer.nextToken();
0759:                            } else
0760:                                throw new IOException(
0761:                                        "Illegal non-numeric data encountered.");
0762:                        }
0763:                        newRows.addElement(row);
0764:                        while (token != StreamTokenizer.TT_EOL
0765:                                && token != StreamTokenizer.TT_EOF)
0766:                            token = tokenizer.nextToken();
0767:                    }
0768:                    if (rows.size() == 0)
0769:                        throw new IOException("Empty data was found.");
0770:                } catch (Exception e) {
0771:                    if (throwErrors)
0772:                        throw new JCMError("Error while reading data:  " + e,
0773:                                this );
0774:                    return false;
0775:                }
0776:                canvas.setActive(0, 0);
0777:                rows = newRows;
0778:                rowStrings = new Vector();
0779:                for (int i = 0; i < rows.size(); i++) {
0780:                    String[] s = new String[cols];
0781:                    double[] d = (double[]) rows.elementAt(i);
0782:                    for (int col = 0; col < cols; col++)
0783:                        if (Double.isNaN(d[col]))
0784:                            s[col] = null;
0785:                        else
0786:                            s[col] = NumUtils.realToString(d[col]);
0787:                    rowStrings.addElement(s);
0788:                }
0789:                canvas.input.setText(((String[]) rowStrings.elementAt(0))[0]);
0790:                if (canvas.hScroll != null)
0791:                    canvas.hScroll.setValue(0);
0792:                canvas.vScroll.setValue(0);
0793:                canvas.checkScroll();
0794:                canvas.repaint();
0795:                if (canvas.rowLabelCanvas != null)
0796:                    canvas.rowLabelCanvas.repaint();
0797:                if (canvas.columnLabelCanvas != null)
0798:                    canvas.columnLabelCanvas.repaint();
0799:                serialNumber++;
0800:                return true;
0801:            }
0802:
0803:            //------------------------------ private nested classes -------------------------------
0804:
0805:            private class InputBox extends TextField {
0806:                // An object of type InputBox is used for user input
0807:                // of numbers in the table.  There is only one input box
0808:                // and it moves around.
0809:                InputBox() {
0810:                    super (12);
0811:                    setBackground(Color.white);
0812:                    setForeground(Color.black);
0813:                    enableEvents(AWTEvent.KEY_EVENT_MASK
0814:                            + AWTEvent.MOUSE_EVENT_MASK);
0815:                }
0816:
0817:                public void processKeyEvent(KeyEvent evt) {
0818:                    if (evt.getID() == KeyEvent.KEY_PRESSED) {
0819:                        int ch = evt.getKeyCode();
0820:                        char chr = evt.getKeyChar();
0821:                        boolean use = (chr != 0 && Character.isDigit(chr)
0822:                                || chr == '.' || chr == 'E' || chr == '-'
0823:                                || chr == '+' || chr == 'e')
0824:                                || ch == KeyEvent.VK_DELETE
0825:                                || ch == KeyEvent.VK_BACK_SPACE;
0826:                        boolean useControl = use || chr == 0;
0827:                        if (!useControl || ch == KeyEvent.VK_ENTER
0828:                                || ch == KeyEvent.VK_DOWN
0829:                                || ch == KeyEvent.VK_UP
0830:                                || ch == KeyEvent.VK_TAB) {
0831:                            if (ch == KeyEvent.VK_ENTER
0832:                                    || ch == KeyEvent.VK_DOWN)
0833:                                canvas.doRowDown();
0834:                            else if (ch == KeyEvent.VK_UP)
0835:                                canvas.doRowUp();
0836:                            else if (ch == KeyEvent.VK_TAB)
0837:                                canvas.doColumnRight();
0838:                            /* else
0839:                               Toolkit.getDefaultToolkit().beep(); */
0840:                            evt.consume();
0841:                        } else if (ch == KeyEvent.VK_LEFT
0842:                                && getCaretPosition() == 0) {
0843:                            canvas.doColumnLeft();
0844:                            evt.consume();
0845:                        } else if (ch == KeyEvent.VK_RIGHT
0846:                                && getCaretPosition() == getText().length()) {
0847:                            canvas.doColumnRight();
0848:                            evt.consume();
0849:                        }
0850:                    }
0851:                    super .processKeyEvent(evt);
0852:                }
0853:
0854:                public void processMouseEvent(MouseEvent evt) {
0855:                    if (evt.getID() == MouseEvent.MOUSE_PRESSED)
0856:                        canvas.ensureActiveVisible();
0857:                    super .processMouseEvent(evt);
0858:                }
0859:            } // end nested class InputBox
0860:
0861:            private class DisplayPanel extends Panel implements  TextListener,
0862:                    MouseListener, AdjustmentListener, ComponentListener {
0863:
0864:                // An object of this class is the actual table seen by the user.
0865:                // The panel itself is just the grid of cells.  The row and column
0866:                // labels and the scroll bars are variables in the DisplayPanel
0867:                // ojbect, but they are added to the containing Panel in the 
0868:                // constructor for DataTableInput above.
0869:
0870:                InputBox input; // Text field for user input, from nested class defined above.
0871:
0872:                int activeRow = 0, activeColumn = 0; // Where the Input box is.
0873:
0874:                int rowHeight = -1, columnWidth; // Size of each cell.  rowHeight = -1 indiacates
0875:                // the size is not yet known.
0876:
0877:                Scrollbar hScroll, vScroll; // for scrolling through the grid of cells.
0878:
0879:                Canvas rowLabelCanvas, columnLabelCanvas; // These canvasses hold the row
0880:                // and column lables and are displayed
0881:                // to the left of and above the grid of cells.
0882:                // They scroll along with the grid.
0883:                // They are null and are enabled by
0884:                // methods setShowRowNumbers and setShowColumnTitles
0885:
0886:                boolean lastRowAutoAdded; // True if last row was auto added.
0887:
0888:                // If the user leaves this row while 
0889:                // it is still empty, it will be auto deleted.
0890:                // Empty rows added with the addRows() method
0891:                // are not deleted in this way.
0892:
0893:                DisplayPanel() {
0894:                    setBackground(cellBackground);
0895:                    input = new InputBox();
0896:                    vScroll = new Scrollbar(Scrollbar.VERTICAL);
0897:                    vScroll.setBackground(Color.lightGray);
0898:                    input.addTextListener(this );
0899:                    vScroll.addAdjustmentListener(this );
0900:                    addMouseListener(this );
0901:                    setLayout(null);
0902:                    add(input);
0903:                    addComponentListener(this );
0904:                }
0905:
0906:                void makeRowLabelCanvas() {
0907:                    rowLabelCanvas = new Canvas() { // canvas for showing row labels
0908:                        public void paint(Graphics g) {
0909:                            int topRow = vScroll.getValue() / rowHeight;
0910:                            int rowCt = getSize().height / rowHeight + 1;
0911:                            int tableRows = rows.size();
0912:                            FontMetrics fm = g.getFontMetrics();
0913:                            int textOffset = (rowHeight + fm.getAscent()) / 2;
0914:                            int vScrollVal = vScroll.getValue();
0915:                            for (int i = topRow; i < rowCt + topRow
0916:                                    && i < tableRows; i++) {
0917:                                String rs = "" + (i + 1);
0918:                                int os = (getSize().width - fm.stringWidth(rs)) / 2;
0919:                                g.drawString(rs, os, textOffset + rowHeight * i
0920:                                        - vScrollVal);
0921:                            }
0922:                        }
0923:
0924:                        public Dimension getPreferredSize() {
0925:                            return new Dimension(35, 50);
0926:                        }
0927:                    };
0928:                    rowLabelCanvas.setBackground(labelBackground);
0929:                }
0930:
0931:                void makeColumnLabelCanvas() {
0932:                    columnLabelCanvas = new Canvas() { // canvas for showing column labels
0933:                        public void paint(Graphics g) {
0934:                            int leftColumn = 0;
0935:                            if (hScroll != null)
0936:                                leftColumn = hScroll.getValue() / columnWidth;
0937:                            int blank = (rowLabelCanvas == null) ? 0 : 35; // width of rowLabelCanvas
0938:                            int columnCt = (getSize().width - blank)
0939:                                    / columnWidth + 1;
0940:                            FontMetrics fm = g.getFontMetrics();
0941:                            int textOffset = (getSize().height + fm.getAscent()) / 2;
0942:                            int hScrollVal = hScroll == null ? 0 : hScroll
0943:                                    .getValue();
0944:                            for (int i = leftColumn; i < leftColumn + columnCt
0945:                                    && i < columnCount; i++) {
0946:                                String s = getColumnName(i);
0947:                                int os = (columnWidth - fm.stringWidth(s)) / 2;
0948:                                g.drawString(s, blank + i * columnWidth + os
0949:                                        - hScrollVal, textOffset);
0950:                            }
0951:                            g.setColor(Color.gray);
0952:                            g.fillRect(0, 0, blank, getSize().height);
0953:                        }
0954:
0955:                        public Dimension getPreferredSize() {
0956:                            return new Dimension(50, 20);
0957:                        }
0958:                    };
0959:                    columnLabelCanvas.setBackground(labelBackground);
0960:                }
0961:
0962:                public void addNotify() {
0963:                    // Determine the size of a cell, which is based on the preferred size
0964:                    // of the input box (which can't be determined until after the peer
0965:                    // is added.  (I hope this works on all platforms!)
0966:                    super .addNotify();
0967:                    if (rowHeight != -1)
0968:                        return;
0969:                    Dimension size = input.getPreferredSize();
0970:                    rowHeight = size.height - 1;
0971:                    columnWidth = size.width - 1;
0972:                    input.setBounds(1, 1, columnWidth + 1, rowHeight + 1);
0973:                }
0974:
0975:                public void update(Graphics g) {
0976:                    // Don't fill in with background before painting.
0977:                    paint(g);
0978:                }
0979:
0980:                public void paint(Graphics g) {
0981:                    // Draw the grid of cells, in position based on scroll bar values.
0982:                    int hScrollVal = (hScroll == null) ? 0 : hScroll.getValue();
0983:                    int vScrollVal = vScroll.getValue();
0984:                    int width = getSize().width; // width and height of component
0985:                    int height = getSize().height;
0986:                    int tableWidth = columnCount * columnWidth + 2;
0987:                    int tableHeight = rows.size() * rowHeight + 2;
0988:                    int tableRows = rows.size(); // Number of rows in table
0989:                    Rectangle clip = g.getClipBounds(); // Try to avoid uncessary painting by checking clip rect.
0990:                    int topRow, rowCt, leftColumn, columnCt;
0991:                    if (clip != null) { // change data about included rows, columns
0992:                        topRow = (vScrollVal + clip.y) / rowHeight;
0993:                        rowCt = clip.height / rowHeight + 1;
0994:                        leftColumn = (hScrollVal + clip.x) / columnWidth;
0995:                        columnCt = clip.width / columnWidth + 1;
0996:                    } else {
0997:                        topRow = vScrollVal / rowHeight; // top visible row
0998:                        rowCt = height / rowHeight + 1; // num of rows possibly visible
0999:                        leftColumn = hScrollVal / columnWidth;// leftmost visible column
1000:                        columnCt = width / columnWidth + 1; // num of columns visible
1001:                    }
1002:                    FontMetrics fm = g.getFontMetrics();
1003:                    int textOffset = (rowHeight + fm.getAscent()) / 2; // from top of box to text baseline
1004:                    for (int i = topRow; i < topRow + rowCt && i < tableRows; i++) {
1005:                        String[] contents = (String[]) rowStrings.elementAt(i);
1006:                        for (int c = 0; c < columnCount; c++)
1007:                            if (c != activeColumn || i != activeRow) {
1008:                                g.setColor(cellBackground);
1009:                                g.fillRect(1 + c * columnWidth - hScrollVal, 1
1010:                                        + i * rowHeight - vScrollVal,
1011:                                        columnWidth, rowHeight);
1012:                                g.setColor(getForeground());
1013:                                if (contents != null && contents[c] != null
1014:                                        && contents[c].length() > 0) {
1015:                                    String s = contents[c];
1016:                                    g.drawString(s, c * columnWidth + 5
1017:                                            - hScrollVal, textOffset + i
1018:                                            * rowHeight - vScrollVal);
1019:                                }
1020:                            }
1021:                    }
1022:                    if (width > tableWidth) {
1023:                        g.setColor(blankBackground);
1024:                        g.fillRect(tableWidth, 0, width - tableWidth, height);
1025:                    }
1026:                    if (height > tableHeight) {
1027:                        g.setColor(blankBackground);
1028:                        g.fillRect(0, tableHeight, width, height - tableHeight);
1029:                    }
1030:                    g.setColor(gridColor);
1031:                    g.drawRect(0, 0, tableWidth, tableHeight);
1032:                    g.drawRect(1, 1, tableWidth - 2, tableHeight - 2);
1033:                    for (int i = -1; i < topRow + rowCt && i < tableRows; i++)
1034:                        g.drawLine(0, 1 + (i + 1) * rowHeight - vScrollVal,
1035:                                tableWidth - 1, 1 + (i + 1) * rowHeight
1036:                                        - vScrollVal);
1037:                    for (int j = 0; j <= columnCount; j++)
1038:                        g.drawLine(1 + j * columnWidth - hScrollVal, 0, 1 + j
1039:                                * columnWidth - hScrollVal, tableHeight - 1);
1040:                }
1041:
1042:                void setActive(int row, int column) {
1043:                    // Move the input box to the specified row and column, and
1044:                    // move the focus to the input box.
1045:                    if (row != activeRow || column != columnWidth) {
1046:                        int topOffset = vScroll.getValue();
1047:                        int leftOffset = (hScroll == null) ? 0 : hScroll
1048:                                .getValue();
1049:                        int y = -topOffset + row * rowHeight + 1;
1050:                        int x = -leftOffset + column * columnWidth + 1;
1051:                        input.setLocation(x, y);
1052:                        activeRow = row;
1053:                        activeColumn = column;
1054:                        String[] contents = (String[]) rowStrings
1055:                                .elementAt(activeRow);
1056:                        if (contents == null || contents[activeColumn] == null)
1057:                            input.setText("");
1058:                        else
1059:                            input.setText(contents[activeColumn]);
1060:                    }
1061:                    ensureActiveVisible();
1062:                    input.selectAll();
1063:                    input.requestFocus();
1064:                }
1065:
1066:                void doRowDown() {
1067:                    // Move active box down one row.  Create a new row if in the
1068:                    // last row, that row is non-empty, and autoAddRows is true.
1069:                    int tableRows = rows.size();
1070:                    if (activeRow == tableRows - 1 && autoAddRows
1071:                            && rows.elementAt(tableRows - 1) != null) {
1072:                        addRows(1, tableRows);
1073:                        lastRowAutoAdded = true;
1074:                    }
1075:                    if (activeRow < rows.size() - 1)
1076:                        setActive(activeRow + 1, activeColumn);
1077:                    else {
1078:                        ensureActiveVisible();
1079:                        input.requestFocus();
1080:                    }
1081:                }
1082:
1083:                void doRowUp() {
1084:                    // Move up one row.  If leaving an empty row that was autoadded, delete it.
1085:                    if (activeRow == 0)
1086:                        return;
1087:                    setActive(activeRow - 1, activeColumn);
1088:                    if (autoAddRows && lastRowAutoAdded == true
1089:                            && activeRow == rows.size() - 2
1090:                            && rows.elementAt(activeRow + 1) == null) {
1091:                        // delete empty row from bottom of table
1092:                        rows.removeElementAt(rows.size() - 1);
1093:                        rowStrings.removeElementAt(rowStrings.size() - 1);
1094:                        checkScroll();
1095:                        repaint();
1096:                        if (rowLabelCanvas != null)
1097:                            rowLabelCanvas.repaint();
1098:                        input.requestFocus();
1099:                    }
1100:                    lastRowAutoAdded = false;
1101:                }
1102:
1103:                void doColumnRight() {
1104:                    // Move active box right to the next column, possibly
1105:                    // wrapping around to the first column.
1106:                    int c = activeColumn + 1;
1107:                    if (c >= columnCount)
1108:                        c = 0;
1109:                    setActive(activeRow, c);
1110:                }
1111:
1112:                void doColumnLeft() {
1113:                    // Move active box left to the next column, possibly
1114:                    // wrapping around to the last column.
1115:                    int c = activeColumn - 1;
1116:                    if (c < 0)
1117:                        c = columnCount - 1;
1118:                    setActive(activeRow, c);
1119:                }
1120:
1121:                void ensureActiveVisible() {
1122:                    // Make sure that the entire input box is visible.
1123:                    int x = columnWidth * activeColumn + 1;
1124:                    int y = rowHeight * activeRow + 1;
1125:                    int visibleLeft = (hScroll == null) ? 0 : hScroll
1126:                            .getValue();
1127:                    int visibleTop = vScroll.getValue();
1128:                    int visibleRight = visibleLeft + getSize().width;
1129:                    int visibleBottom = visibleTop + getSize().height;
1130:                    int offsetX = 0;
1131:                    int offsetY = 0;
1132:                    if (x + columnWidth > visibleRight)
1133:                        offsetX = -(x + columnWidth - visibleRight);
1134:                    if (x < visibleLeft)
1135:                        offsetX = visibleLeft - x;
1136:                    if (y + rowHeight > visibleBottom)
1137:                        offsetY = -(y + rowHeight - visibleBottom);
1138:                    if (y < visibleTop)
1139:                        offsetY = visibleTop - y;
1140:                    if (offsetX == 0 && offsetY == 0)
1141:                        return;
1142:                    if (offsetX != 0) {
1143:                        if (hScroll != null)
1144:                            hScroll.setValue(visibleLeft - offsetX);
1145:                        if (columnLabelCanvas != null)
1146:                            columnLabelCanvas.repaint();
1147:                    }
1148:                    if (offsetY != 0) {
1149:                        vScroll.setValue(visibleTop - offsetY);
1150:                        if (rowLabelCanvas != null)
1151:                            rowLabelCanvas.repaint();
1152:                    }
1153:                    input.setLocation(x
1154:                            - (hScroll == null ? 0 : hScroll.getValue()), y
1155:                            - vScroll.getValue());
1156:                    repaint();
1157:                }
1158:
1159:                void addRows(int num, int before) {
1160:                    // Add specified number of rows to table, before row
1161:                    // number before.  If before is after the last existing
1162:                    // row, the rows are added at the end of the table.
1163:                    serialNumber++;
1164:                    if (num <= 0)
1165:                        return;
1166:                    if (before >= rows.size()) {
1167:                        for (int i = 0; i < num; i++) {
1168:                            rows.addElement(null);
1169:                            rowStrings.addElement(null);
1170:                            lastRowAutoAdded = false;
1171:                        }
1172:                    } else {
1173:                        if (before < 0)
1174:                            before = 0;
1175:                        for (int i = 0; i < num; i++) {
1176:                            rows.insertElementAt(null, before);
1177:                            rowStrings.insertElementAt(null, before);
1178:                        }
1179:                        if (activeRow >= before) { // data in active cell changes
1180:                            String[] vals = (String[]) rowStrings
1181:                                    .elementAt(activeRow);
1182:                            if (vals == null || vals[activeColumn] == null)
1183:                                input.setText("");
1184:                            else
1185:                                input.setText(vals[activeColumn]);
1186:                        }
1187:                    }
1188:                    checkScroll();
1189:                    repaint();
1190:                    if (rowLabelCanvas != null)
1191:                        rowLabelCanvas.repaint();
1192:                }
1193:
1194:                void setValue(int column, int row, double value) {
1195:                    // set the value in the column at given row and column.
1196:                    // Note that row numbers start at 1 in this method!!
1197:                    // Row and column numbers are assumed to be in legal range!!
1198:                    String num = NumUtils.realToString(value);
1199:                    setRowData(row - 1, column, num, value);
1200:                    if (column == activeColumn && row - 1 == activeRow)
1201:                        input.setText(num);
1202:                    else {
1203:                        repaintItem(row - 1, column);
1204:                    }
1205:                }
1206:
1207:                double getValue(int column, int row) {
1208:                    // Get the value from the table in the given row and column.
1209:                    // Note that row numbers start at 1 in this method!!
1210:                    // Row and column numbers are assumed to be in legal range!!
1211:                    if (rows.elementAt(row - 1) == null)
1212:                        return emptyCellValue;
1213:                    else {
1214:                        double d = ((double[]) rows.elementAt(row - 1))[column];
1215:                        if (!Double.isNaN(d))
1216:                            return d;
1217:                        else {
1218:                            String val = ((String[]) rowStrings
1219:                                    .elementAt(row - 1))[column];
1220:                            if (val == null || val.length() == 0)
1221:                                return emptyCellValue;
1222:                            else // val is "bad input", the only other possibility if d is NaN
1223:                            if (throwErrors)
1224:                                throw new JCMError(
1225:                                        "Invalid numerical input in data table, column \""
1226:                                                + getColumnName(column)
1227:                                                + "\", row " + row + ".", this );
1228:                            else
1229:                                return Double.NaN;
1230:                        }
1231:                    }
1232:                }
1233:
1234:                public void textValueChanged(TextEvent txt) {
1235:                    // From TextListener interface.  When text in input box changes,
1236:                    // change the stored string and stored value for that position to match.
1237:                    String num = input.getText().trim();
1238:                    if (num.length() == 0)
1239:                        setRowData(activeRow, activeColumn, num, Double.NaN);
1240:                    else {
1241:                        double x;
1242:                        try {
1243:                            Double d = new Double(num);
1244:                            x = d.doubleValue();
1245:                        } catch (NumberFormatException e) {
1246:                            x = Double.NaN;
1247:                        }
1248:                        if (Double.isNaN(x))
1249:                            setRowData(activeRow, activeColumn, "bad input", x);
1250:                        else
1251:                            setRowData(activeRow, activeColumn, num, x);
1252:                    }
1253:                }
1254:
1255:                void setRowData(int row, int col, String num, double val) {
1256:                    // puts num, val into rows, rowStrings vectors at position row,col
1257:                    // Empty rows are always represented by null's in the rowVals
1258:                    // and rowStrings vectores.  This requires a bit of care.
1259:                    serialNumber++;
1260:                    double[] rowVals = (double[]) rows.elementAt(row);
1261:                    String[] rowStr = (String[]) rowStrings.elementAt(row);
1262:                    if (num.length() == 0) {
1263:                        if (rowStr == null || rowStr[col] == null)
1264:                            return;
1265:                        rowStr[col] = null;
1266:                        rowVals[col] = Double.NaN;
1267:                        boolean empty = true;
1268:                        for (int i = 0; i < rowStr.length; i++)
1269:                            if (rowStr[i] != null) {
1270:                                empty = false;
1271:                                break;
1272:                            }
1273:                        if (empty) {
1274:                            rows.setElementAt(null, row);
1275:                            rowStrings.setElementAt(null, row);
1276:                        }
1277:                    } else {
1278:                        if (num.length() > 12)
1279:                            num = NumUtils.realToString(val, 12);
1280:                        if (rowStr == null) {
1281:                            int ct = columnCount;
1282:                            rowVals = new double[ct];
1283:                            rowStr = new String[ct];
1284:                            for (int i = 0; i < ct; i++)
1285:                                rowVals[i] = Double.NaN;
1286:                            rows.setElementAt(rowVals, row);
1287:                            rowStrings.setElementAt(rowStr, row);
1288:                        }
1289:                        rowStr[col] = num;
1290:                        rowVals[col] = val;
1291:                    }
1292:                }
1293:
1294:                protected void repaintItem(int row, int column) {
1295:                    // forces a repaint for just the specified cell
1296:                    int y = row * rowHeight - vScroll.getValue();
1297:                    int x = column * columnWidth;
1298:                    if (hScroll != null)
1299:                        x -= hScroll.getValue();
1300:                    repaint(x + 1, y + 1, columnWidth - 1, rowHeight - 1);
1301:                }
1302:
1303:                public void adjustmentValueChanged(AdjustmentEvent evt) {
1304:                    // From the AdjustmentListener interface.  React to
1305:                    // change in scroll positions.
1306:                    repaint();
1307:                    if (evt.getSource() == vScroll) {
1308:                        if (rowLabelCanvas != null)
1309:                            rowLabelCanvas.repaint();
1310:                    } else {
1311:                        if (columnLabelCanvas != null)
1312:                            columnLabelCanvas.repaint();
1313:                    }
1314:                    int x = columnWidth * activeColumn + 1;
1315:                    if (hScroll != null)
1316:                        x -= hScroll.getValue();
1317:                    int y = rowHeight * activeRow + 1 - vScroll.getValue();
1318:                    input.setLocation(x, y);
1319:                }
1320:
1321:                public void mousePressed(MouseEvent evt) {
1322:                    // From the MouseListener interface.  Move the active
1323:                    // cell to an input cell close to mouse click. 
1324:                    int hOffset = (hScroll == null) ? 0 : hScroll.getValue();
1325:                    int vOffset = vScroll.getValue();
1326:                    int row = (evt.getY() + vOffset - 1) / rowHeight;
1327:                    if (row < 0)
1328:                        row = 0;
1329:                    else if (row >= rows.size())
1330:                        row = rows.size() - 1;
1331:                    int col = (evt.getX() + hOffset - 1) / columnWidth;
1332:                    if (col < 0)
1333:                        col = 0;
1334:                    else if (col >= columnCount)
1335:                        col = columnCount - 1;
1336:                    if (row == activeRow && col == activeColumn)
1337:                        ensureActiveVisible(); // want to avoid doing selectAll if box doesn't move
1338:                    else
1339:                        setActive(row, col);
1340:                    int emptyRow = rows.size() - 1;
1341:                    if (!lastRowAutoAdded || emptyRow == row)
1342:                        return;
1343:                    lastRowAutoAdded = false;
1344:                    if (rows.elementAt(emptyRow) != null)
1345:                        return;
1346:                    rows.removeElementAt(emptyRow);
1347:                    rowStrings.removeElementAt(emptyRow);
1348:                    checkScroll();
1349:                    repaint();
1350:                    if (rowLabelCanvas != null)
1351:                        rowLabelCanvas.repaint();
1352:                }
1353:
1354:                public void componentResized(ComponentEvent evt) {
1355:                    // From ComponentListener interface.  Fix scroll bars after resize.
1356:                    checkScroll();
1357:                }
1358:
1359:                void checkScroll() {
1360:                    // Make sure scoll bars are OK after resize, adding rows, etc.
1361:                    int width = DisplayPanel.this .getSize().width;
1362:                    int height = DisplayPanel.this .getSize().height;
1363:                    if (rowHeight == -1 || width <= 1)
1364:                        return;
1365:                    int tableWidth = columnWidth * columnCount + 2;
1366:                    int tableHeight = rowHeight * rows.size() + 2;
1367:                    int oldTop = vScroll.getValue();
1368:                    int oldLeft = (hScroll == null) ? 0 : hScroll.getValue();
1369:                    boolean revalidate = false;
1370:                    if (width >= tableWidth - 2) {
1371:                        if (hScroll != null) {
1372:                            int scrollHeight = hScroll.getPreferredSize().height;
1373:                            DataTableInput.this .remove(hScroll);
1374:                            hScroll = null;
1375:                            height += scrollHeight;
1376:                            revalidate = true;
1377:                        }
1378:                    } else {
1379:                        if (hScroll == null) {
1380:                            hScroll = new Scrollbar(Scrollbar.HORIZONTAL);
1381:                            hScroll.setBackground(Color.lightGray);
1382:                            int scrollHeight = hScroll.getPreferredSize().height;
1383:                            height -= scrollHeight;
1384:                            DataTableInput.this 
1385:                                    .add(hScroll, BorderLayout.SOUTH);
1386:                            hScroll.addAdjustmentListener(this );
1387:                            revalidate = true;
1388:                        }
1389:                        if (oldLeft > tableWidth - width)
1390:                            hScroll.setValues(tableWidth - width, width, 0,
1391:                                    tableWidth);
1392:                        else
1393:                            hScroll.setValues(oldLeft, width, 0, tableWidth);
1394:                        hScroll.setUnitIncrement(columnWidth / 4);
1395:                        if (width > 1)
1396:                            hScroll.setBlockIncrement((3 * width) / 4);
1397:                    }
1398:                    if (height >= tableHeight - 2) {
1399:                        vScroll.setEnabled(false);
1400:                        vScroll.setValues(0, 1, 0, 1);
1401:                    } else {
1402:                        if (oldTop > tableHeight - height)
1403:                            vScroll.setValues(tableHeight - height, height, 0,
1404:                                    tableHeight);
1405:                        else
1406:                            vScroll.setValues(oldTop, height, 0, tableHeight);
1407:                        vScroll.setUnitIncrement(rowHeight);
1408:                        if (height > 1)
1409:                            vScroll.setBlockIncrement((3 * height) / 4);
1410:                        vScroll.setEnabled(true);
1411:                    }
1412:                    int x = columnWidth * activeColumn + 1;
1413:                    if (hScroll != null)
1414:                        x -= hScroll.getValue();
1415:                    int y = rowHeight * activeRow + 1 - vScroll.getValue();
1416:                    input.setLocation(x, y);
1417:                    if (revalidate)
1418:                        DataTableInput.this .validate();
1419:                }
1420:
1421:                public Dimension getPreferredSize() {
1422:                    if (rowHeight == -1)
1423:                        return new Dimension(350, 200);
1424:                    else if (columnCount >= 4)
1425:                        return new Dimension(4 * columnWidth + 2,
1426:                                6 * rowHeight + 2);
1427:                    else
1428:                        return new Dimension(columnCount * columnWidth + 2,
1429:                                6 * rowHeight + 2);
1430:                }
1431:
1432:                public void mouseEntered(MouseEvent evt) {
1433:                } // Other methods from listener interfaces.
1434:
1435:                public void mouseExited(MouseEvent evt) {
1436:                }
1437:
1438:                public void mouseClicked(MouseEvent evt) {
1439:                }
1440:
1441:                public void mouseReleased(MouseEvent evt) {
1442:                }
1443:
1444:                public void componentHidden(ComponentEvent evt) {
1445:                }
1446:
1447:                public void componentShown(ComponentEvent evt) {
1448:                }
1449:
1450:                public void componentMoved(ComponentEvent evt) {
1451:                }
1452:
1453:            } // end nested class Display Panel
1454:
1455:            private class DTEC implements  ExpressionCommand {
1456:
1457:                // This is used in the doParse() method.  When a reference to a DataInputTable
1458:                // is found by a parser, the doParse() method will add an object of this
1459:                // type to the ExpressionProgram that the parseris producing.  A DTEC represents
1460:                // a sub-exprssion such as "data.A(3)" or "data.sum(A^2)"
1461:
1462:                ExpressionProgram prog; // The expression inside the parentheses
1463:
1464:                int command; // The column number for an expression such as "data.A(3)" which
1465:
1466:                // represents the value of a cell in column "A".  For the sum
1467:                // function, command is -1.  For the count funtion, it is -2.
1468:
1469:                DTEC(int command, ExpressionProgram prog) {
1470:                    this .command = command;
1471:                    this .prog = prog;
1472:                }
1473:
1474:                public void apply(StackOfDouble stack, Cases cases) {
1475:                    if (command >= 0) { // Reference to a column.  Value of prog gives row number.
1476:                        double loc = prog.getVal();
1477:                        if (Double.isNaN(loc) || loc < 0.5
1478:                                || loc >= rows.size() + 0.5)
1479:                            stack.push(Double.NaN);
1480:                        else
1481:                            stack.push(canvas.getValue(command,
1482:                                    (int) (loc + 0.5)));
1483:                    } else if (command == -1) { // sum of the prog expression for all rows in the table.
1484:                        double sum = 0;
1485:                        int top = getNonEmptyRowCount();
1486:                        for (int row = 1; row <= top; row++) {
1487:                            setCurrentRowNumber(row);
1488:                            sum += prog.getVal();
1489:                        }
1490:                        stack.push(sum);
1491:                    } else if (command == -2) { // the count of rows in the table
1492:                        stack.push(getNonEmptyRowCount());
1493:                    }
1494:                }
1495:
1496:                public void compileDerivative(ExpressionProgram prog,
1497:                        int myIndex, ExpressionProgram deriv, Variable wrt) {
1498:                    if (command != -1) {
1499:                        deriv.addConstant(0);
1500:                    } else {
1501:                        ExpressionProgram d = (ExpressionProgram) this .prog
1502:                                .derivative(wrt);
1503:                        deriv.addCommandObject(new DTEC(command, d));
1504:                    }
1505:                }
1506:
1507:                public int extent(ExpressionProgram prog, int myIndex) {
1508:                    return 1;
1509:                }
1510:
1511:                public boolean dependsOn(Variable x) {
1512:                    if (command == -2)
1513:                        return false;
1514:                    else
1515:                        return prog.dependsOn(x);
1516:                }
1517:
1518:                public void appendOutputString(ExpressionProgram prog,
1519:                        int myIndex, StringBuffer buffer) {
1520:                    buffer.append(getName());
1521:                    buffer.append('.');
1522:                    if (command == -2)
1523:                        buffer.append("count");
1524:                    else if (command == -1)
1525:                        buffer.append("sum");
1526:                    else
1527:                        buffer.append(getColumnName(command));
1528:                    buffer.append("(");
1529:                    if (command != -2)
1530:                        buffer.append(this .prog.toString());
1531:                    buffer.append(")");
1532:                }
1533:
1534:            } // end nested class DTEC
1535:
1536:        } // end class DataTableInput
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.