package totalcross.ui.chart; import totalcross.ui.Container; import totalcross.ui.Window; import totalcross.ui.event.Event; import totalcross.ui.event.PenEvent; import totalcross.ui.gfx.Color; import totalcross.ui.gfx.Graphics; import totalcross.util.Properties; import totalcross.util.Vector; /** The ChartData class represents a table with data that would be displayed in the chart. Here's a sample: <pre> double[] xAxis = new double[0]; double[] pressureyAxis = new double[0]; Series pressureSeries = new Series("Pression", xAxis, pressureyAxis, Color.BLUE); int cols = 24, rows = 5; XYChart chart = new XYChart(); chart.showHGrids = chart.showVGrids = true; // Shows grids. chart.showTitle = true; // Shows title. chart.showYValues = true; // Shows the Y axis values. chart.yDecimalPlaces = 0; // No decimal places. chart.setXAxis(0, 240, 24); // The X axis is time in minutes. It has an interval of 5 minutes and a maximum of 2 hours. chart.setYAxis(0, 240, 24); // The Y axis is different for each graph. chart.snapToTop = chart.snapToBottom = true; String[][] data = new String[rows][cols]; String[] tit = new String[rows]; for (int r = 0; r < rows; r++) { tit[r] = "row "+(r+1); for (int c = 0; c < cols; c++) data[r][c] = "999"; } ChartData cd1 = new ChartData(chart, tit, data); cd1.lineColor = Color.BLACK; cd1.setFont(font.adjustedBy(-2)); cd1.snapToTop = true; ChartData cd2 = new ChartData(chart, data); cd2.lineColor = Color.BLACK; cd2.setFont(font.adjustedBy(-2)); cd2.snapToBottom = true; add(cd1,LEFT,TOP+25,FILL,PREFERRED); add(cd2,LEFT,BOTTOM-25,FILL,PREFERRED); add(chart,LEFT,AFTER,FILL,FIT,cd1); cd2.bringToFront(); cd1.bringToFront(); chart.showLines = false; chart.yValuesSize = fm.stringWidth("99999"); chart.series.addElement(pressureSeries); // setup the xy chart pressureSeries.xValues = new double[]{10,20,30,40,50}; pressureSeries.yValues = new double[]{10,20,30,40,35}; try { pressureSeries.dot = Resources.radioBkg.getNormalInstance(fmH,fmH,-1); } catch (ImageException e) { MessageBox.showException(e, true); } cd2.reposition(); </pre> @since TotalCross 2.0 */ public class ChartData extends Container { Vector rows = new Vector(10); protected Chart chart; public int lineColor = Color.DARK; public int fillColor2 = 0xDDDDDD; public int titleForeColor = -1, titleBackColor = -1; public int selectedCol = -1, selectedRow = -1; public boolean snapToTop; public boolean snapToBottom; public int use2ndColorEveryXColumns = 1; /** * The height of the cell when using PREFERRED, defined as a % of the control's font height. Default value is 100(%). */ public int preferredCellHeight = 100; /** * Constructs an empty ChartData. */ public ChartData(Chart chart) { this.chart = chart; } /** * Constructs a ChartData with the given data. * * @param data * The values to be displayed in the format [rows][cols] */ public ChartData(Chart chart, ChartDataRow[] data) { this(chart); this.rows.addElements(data); } public void onPaint(Graphics g) { g.backColor = backColor; if (!transparentBackground) { g.fillRect(0, 0, width, height); if (chart.axisBackColor != -1) { g.backColor = chart.axisBackColor; g.fillRect(chart.xAxisX1, 0, chart.xAxisX2 - chart.xAxisX1, height); } } double inc = (chart.xAxisMaxValue - chart.xAxisMinValue) / chart.xAxisSteps; double val = chart.xAxisMinValue; if (chart.getXValuePos(val) == 0) return; int v0 = chart.getXValuePos(val), xx; int cw = chart.getXValuePos(val + inc) - v0; int rowsLength = rows.size(); if (rowsLength > 0) { int ystep = this.height / rowsLength; int yxtra = this.height % ystep; if (fillColor2 != -1) { double x0 = val + inc * use2ndColorEveryXColumns; g.backColor = fillColor2; // vertical lines for (int j = 1, n = getRow(0).columns.length; j <= n; j += 2, x0 += inc * use2ndColorEveryXColumns * 2) { xx = chart.getXValuePos(x0); g.fillRect(xx, 0, chart.getXValuePos(x0 + inc * use2ndColorEveryXColumns) - xx, height); } } int xx0 = chart.getXValuePos(val); if (titleBackColor != -1) { g.backColor = titleBackColor; g.fillRect(0, 0, xx0 + 1, height); } g.backColor = backColor; g.foreColor = foreColor; int yy = 0; for (int i = 0; i < rowsLength; i++) { ChartDataRow row = getRow(i); double x0 = val; int hh = ystep; if (i < yxtra) hh++; if (row.title != null) { g.setClip(0, yy, chart.getXValuePos(x0) - 2, hh); g.foreColor = titleForeColor != -1 ? titleForeColor : foreColor; g.drawText(row.title, 0, yy + (hh - fmH) / 2); } g.foreColor = foreColor; for (int j = 0, n = row.columns.length; j < n; j++, x0 += inc) { xx = chart.getXValuePos(x0); g.setClip(xx, yy, cw, hh - 1); Properties.Value value = row.columns[j]; if (value != null) { String d = value.toString(); int sw = fm.stringWidth(d); g.drawText(d, xx + (cw - sw) / 2, yy + (hh - fmH) / 2); } } yy += hh; } if (lineColor != -1) { g.clearClip(); g.foreColor = chart.axisForeColor; g.drawLine(xx = chart.getXValuePos(val), 0, xx, height); // draw Y axis g.backColor = fillColor2 != -1 ? fillColor2 : lineColor; int xf = width - chart.border.right - 1; double x0 = val + (fillColor2 != -1 ? inc : 0); // vertical lines for (int j = fillColor2 != -1 ? 1 : 0, n = getRow(0).columns.length; j <= n; j++, x0 += inc) g.drawDots(xx = chart.getXValuePos(x0), 0, xx, height); yy = 0; int hh = 0; for (int i = snapToBottom ? 1 : 0, n = rowsLength; i <= n; i++, yy += hh) // horizontal lines { g.drawDots(v0, yy, xf, yy); hh = ystep; if (i < yxtra) hh++; } if (!snapToTop) g.drawDots(v0, height - 1, xf, height - 1); } } if (chart.markPos != Chart.UNSET) { g.foreColor = chart.categoryMarkColor; g.drawLine(chart.markPos - 1, 0, chart.markPos - 1, height); g.drawLine(chart.markPos, 0, chart.markPos, height); g.drawLine(chart.markPos + 1, 0, chart.markPos + 1, height); } } public void onEvent(Event e) { switch (e.type) { case PenEvent.PEN_UP: if (isEnabled() && !hadParentScrolled()) { selectedRow = selectedCol = -1; PenEvent pe = (PenEvent) e; int pex = pe.x, pey = pe.y; int rowsLength = rows.size(); if (rowsLength > 0 && chart.xAxisX1 <= pex && pex <= chart.xAxisX2 && chart.yAxisY2 <= pey && pey <= chart.yAxisY1) { int ystep = this.height / rowsLength; int yxtra = this.height % ystep; selectedCol = (pex - chart.xAxisX1) / chart.columnW; for (int i = 0, hh, yy = chart.yAxisY2; i <= rowsLength; i++, yy += hh) // horizontal lines { hh = ystep; if (i < yxtra) hh++; if (yy <= pey && pey < yy + hh) { selectedRow = i; break; } } postPressedEvent(); } } break; } } public void reposition() { super.reposition(false); removeAll(); initUI(); } public int getPreferredHeight() { int rowsLength = rows.size(); if (rowsLength == 0) return 0; return fmH * preferredCellHeight * rowsLength / 100; } public Properties.Value getSelectedCell() { if (selectedCol >= 0 && selectedRow >= 0) return getRow(selectedRow).columns[selectedCol]; return null; } public Properties.Value getCell(int col, int row) { return getRow(row).columns[col]; } public void setCell(Properties.Value value, int col, int row) { getRow(row).columns[col] = value; Window.needsPaint = true; } public ChartDataRow getRow(int row) { if (row < 0 || row >= rows.size()) throw new IndexOutOfBoundsException(); return (ChartDataRow) rows.items[row]; } public ChartDataRow getSelectedRow() { if (selectedRow >= 0) return getRow(selectedRow); return null; } /** Adds a new row. Pass -1 to add at the end */ public void addLine(int pos, ChartDataRow row) { int rowCount = rows.size(); if (pos < 0 || pos > rowCount) pos = rowCount; rows.insertElementAt(row, pos); } public void removeLine(int pos) { int rowCount = rows.size(); if (pos == -1) pos = rowCount - 1; if (pos < 0 || pos >= rowCount) return; this.rows.removeElementAt(pos); } public void removeLine(String title) { for (int i = rows.size() - 1; i >= 0; i--) if (getRow(i).title.equals(title)) removeLine(i); } public class ChartDataRow { String title; Properties.Value[] columns; public ChartDataRow(int columnCount) { this(null, new Properties.Value[columnCount]); } public ChartDataRow(String title, int columnCount) { this(title, new Properties.Value[columnCount]); } public ChartDataRow(String title, Properties.Value[] data) { this.title = title; this.columns = data; } public String getTitle() { return title; } } }