/*********************************************************************************
* TotalCross Software Development Kit *
* Copyright (C) 2000-2012 SuperWaba Ltda. *
* All Rights Reserved *
* *
* This library and virtual machine is distributed in the hope that it will *
* be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* *
* This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0 *
* A copy of this license is located in file license.txt at the root of this *
* SDK or can be downloaded here: *
* http://www.gnu.org/licenses/lgpl-3.0.txt *
* *
*********************************************************************************/
package totalcross.ui.chart;
import totalcross.sys.*;
import totalcross.ui.*;
import totalcross.ui.event.ControlEvent;
import totalcross.ui.event.Event;
import totalcross.ui.event.KeyEvent;
import totalcross.ui.event.PenEvent;
import totalcross.ui.gfx.Color;
import totalcross.ui.gfx.Graphics;
import totalcross.ui.gfx.Rect;
import totalcross.util.Vector;
/** A vertical column chart. */
public class ColumnChart extends Chart
{
/** The current selected column */
private int selection = -1;
/** Vector of columns rectangles */
protected Vector columns = new Vector();
/** Used to draw the column perspective when chart type is 3D */
private int[] xPoints = new int[4];
/** Used to draw the column perspective when chart type is 3D */
private int[] yPoints = new int[4];
/** Perspective horizontal distance. */
public int perspectiveH = 5;
/** Perspective vertical distance. */
public int perspectiveV = 3;
/**
* Creates a new 2D column chart
* @param categories The categories that will be used
*/
public ColumnChart(String[] categories)
{
this(0, categories);
}
/**
* Creates a new column chart
* @param type the chart's type (2D or 3D)
* @param categories The categories that will be used
* @see #type
*/
public ColumnChart(int type, String[] categories)
{
this.type = type;
setXAxis(categories);
}
public void onPaint(Graphics g)
{
if (columns.size() != series.size())
columns.removeAllElements();
if (!draw(g)) // draw axis, title, etc
return;
int sCount = series.size();
int cCount = xAxisSteps;
int numCols = sCount * cCount;
for (int i = columns.size(); i < numCols; i ++) // add missing columns rectangles - guich@tc115_82: changed <= to <
columns.addElement(new Rect());
int catW = getXValuePos(1.0) - getXValuePos(0.0);
int colW = catW / (sCount + 1);
int colS = (type & IS_3D) != 0 ? (colW - 5) / 2 : colW / 2;
// Draw series
boolean forward = perspectiveH >= 0;
int col = 0;
for (int i = 0; i < cCount; i ++) // for each category
{
int x = getXValuePos(i) + colS + (forward ? 0 : colW*(sCount-1)-perspectiveH);
for (int j = forward ? 0 : sCount-1; forward ? (j < sCount) : (j >= 0); j+=(forward?1:-1), x += (forward?colW:-colW), col++) // for each series
{
Series s = (Series) series.items[j];
int y = getYValuePos(s.yValues[i]);
Rect r = (Rect) columns.items[col]; // update column rect
r.x = x;
r.y = y;
r.width = colW + 1;
r.height = yAxisY1 - y + 1;
int c = s.color;
if (selection == col) // this column is selected
c = Color.darker(c);
if ((type & (GRADIENT_HORIZONTAL|GRADIENT_VERTICAL)) != 0)
{
int fade = (type & GRADIENT_DARK) != 0 ? Color.darker(c,128) : Color.brighter(c,128);
boolean invertGradient = (type & GRADIENT_INVERT) != 0;
boolean vertical = (type & GRADIENT_VERTICAL) != 0;
g.fillShadedRect(x,y,colW, yAxisY1-y,!invertGradient,!vertical,c,fade,100); // note: original method was drawRoundGradient, which draws from c2 to c1, that's why we use !invert here
}
else
{
g.backColor = c;
g.fillRect(x, y, colW + 1, yAxisY1 - y + 1);
}
g.foreColor = Color.BLACK;
g.drawRect(x, y, colW + 1, yAxisY1 - y + 1);
if ((type & IS_3D) != 0) // draw perspective
{
// Include the perspective into rect
r.y -= perspectiveV;
r.height += perspectiveV;
r.width += perspectiveH;
// top
g.backColor = Color.darker(c);
xPoints[0] = x; yPoints[0] = y;
xPoints[1] = x + perspectiveH; yPoints[1] = y - perspectiveV;
xPoints[2] = x + colW + perspectiveH; yPoints[2] = y - perspectiveV;
xPoints[3] = x + colW; yPoints[3] = y;
g.fillPolygon(xPoints, yPoints, 4);
g.drawPolygon(xPoints, yPoints, 4);
// side
if (perspectiveH >= 0)
{
xPoints[0] = x + colW; yPoints[0] = yAxisY1;
xPoints[1] = x + colW + perspectiveH; yPoints[1] = yAxisY1;
}
else
{
xPoints[0] = x; yPoints[0] = yAxisY1;
xPoints[1] = x + perspectiveH; yPoints[1] = yAxisY1;
xPoints[2] = x + perspectiveH; yPoints[2] = y - perspectiveV;
xPoints[3] = x; yPoints[3] = y;
}
g.fillPolygon(xPoints, yPoints, 4);
g.drawPolygon(xPoints, yPoints, 4);
}
}
}
if (selection >= 0) // there is a selection
{
Rect r = (Rect)columns.items[selection];
String text = Convert.toCurrencyString(((Series) series.items[selection % sCount]).yValues[selection / sCount], xDecimalPlaces);
drawTextBox(g, r.x, r.y, text);
}
}
public void onEvent(Event e)
{
switch (e.type)
{
case PenEvent.PEN_UP:
if (!hadParentScrolled())
{
PenEvent pe = (PenEvent) e;
pe.consumed = true;
if (pe.x >= xAxisX1 && pe.x <= xAxisX2 && pe.y >= yAxisY2 && pe.y <= yAxisY1)
{
selection = -1;
int len = columns.size();
int i;
for (i = 0; i < len; i ++)
{
Rect r = (Rect) columns.items[i];
if (r.contains(pe.x, pe.y))
break;
}
if (i < len)
{
if ((type & IS_3D) != 0 && i < len - 1) // check overlaping with next column if 3D
{
Rect r = (Rect) columns.items[i + 1];
if (r.contains(pe.x, pe.y)) // overlaping - use the next column
i ++;
}
selection = i;
}
Window.needsPaint = true;
}
else if (selection >= 0) // there is a column selected, invalidate
{
selection = -1;
Window.needsPaint = true;
}
}
break;
case KeyEvent.SPECIAL_KEY_PRESS:
if (Settings.keyboardFocusTraversable)
{
KeyEvent ke = (KeyEvent) e;
if (ke.isActionKey())
{
isHighlighting = true;
parent.requestFocus();
}
else
{
int len = columns.size();
if (len > 0)
{
if (ke.isNextKey()) // next column
{
if (selection < len - 1)
{
selection ++;
Window.needsPaint = true;
}
}
else if (ke.isPrevKey()) // previous column
{
if (selection > 0)
{
selection --;
Window.needsPaint = true;
}
}
}
}
}
break;
case ControlEvent.FOCUS_IN:
if (Settings.keyboardFocusTraversable && columns.size() > 0)
{
selection = 0;
Window.needsPaint = true;
}
break;
case ControlEvent.FOCUS_OUT:
if (selection >= 0)
{
selection = -1;
Window.needsPaint = true;
}
break;
}
}
}