/*
* Copyright (c) 2004-2011 Marco Maccaferri and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Marco Maccaferri - initial API and implementation
*/
package org.eclipsetrader.ui.charts;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Composite;
import org.eclipsetrader.core.charts.IDataSeries;
import org.eclipsetrader.core.charts.OHLCDataSeries;
import org.eclipsetrader.core.feed.IOHLC;
import org.eclipsetrader.core.feed.TimeSpan;
public class BarChart implements IChartObject, ISummaryBarDecorator, IAdaptable {
private IDataSeries dataSeries;
private int width = 5;
private RGB positiveColor = new RGB(0, 254, 0);
private RGB negativeColor = new RGB(254, 0, 0);
private IAdaptable[] values;
private List<Bar> pointArray;
private boolean valid;
private boolean hasFocus;
private SummaryDateItem dateItem;
private SummaryOHLCItem ohlcItem;
private DateFormat dateFormat = DateFormat.getDateInstance();
private NumberFormat numberFormat = NumberFormat.getInstance();
public BarChart(IDataSeries dataSeries) {
this.dataSeries = dataSeries;
if (dataSeries instanceof OHLCDataSeries) {
TimeSpan resolution = ((OHLCDataSeries) dataSeries).getResolution();
if (resolution != null && resolution.getUnits() == TimeSpan.Units.Minutes) {
dateFormat = DateFormat.getDateTimeInstance();
}
}
numberFormat.setGroupingUsed(true);
numberFormat.setMinimumIntegerDigits(1);
numberFormat.setMinimumFractionDigits(0);
numberFormat.setMaximumFractionDigits(4);
}
public BarChart(IDataSeries dataSeries, RGB positiveColor, RGB negativeColor) {
this.dataSeries = dataSeries;
if (positiveColor != null) {
this.positiveColor = positiveColor;
}
if (negativeColor != null) {
this.negativeColor = negativeColor;
}
if (dataSeries instanceof OHLCDataSeries) {
TimeSpan resolution = ((OHLCDataSeries) dataSeries).getResolution();
if (resolution != null && resolution.getUnits() == TimeSpan.Units.Minutes) {
dateFormat = DateFormat.getDateTimeInstance();
}
}
numberFormat.setGroupingUsed(true);
numberFormat.setMinimumIntegerDigits(1);
numberFormat.setMinimumFractionDigits(0);
numberFormat.setMaximumFractionDigits(4);
}
/* (non-Javadoc)
* @see org.eclipsetrader.ui.charts.IChartObject#setDataBounds(org.eclipsetrader.ui.charts.DataBounds)
*/
@Override
public void setDataBounds(DataBounds dataBounds) {
List<IAdaptable> l = new ArrayList<IAdaptable>(2048);
for (IAdaptable value : dataSeries.getValues()) {
Date date = (Date) value.getAdapter(Date.class);
if ((dataBounds.first == null || !date.before(dataBounds.first)) && (dataBounds.last == null || !date.after(dataBounds.last))) {
l.add(value);
}
}
this.values = l.toArray(new IAdaptable[l.size()]);
this.width = dataBounds.horizontalSpacing;
this.valid = false;
}
/* (non-Javadoc)
* @see org.eclipsetrader.ui.charts.IChartObject#invalidate()
*/
@Override
public void invalidate() {
this.valid = false;
}
/* (non-Javadoc)
* @see org.eclipsetrader.ui.charts.IChartObject#paint(org.eclipsetrader.ui.charts.IGraphics)
*/
@Override
public void paint(IGraphics graphics) {
if (!valid || pointArray == null && values != null) {
if (pointArray == null) {
pointArray = new ArrayList<Bar>(values.length);
}
for (int i = 0; i < values.length; i++) {
IOHLC ohlc = (IOHLC) values[i].getAdapter(IOHLC.class);
if (ohlc == null) {
continue;
}
int h = graphics.mapToVerticalAxis(ohlc.getHigh());
int l = graphics.mapToVerticalAxis(ohlc.getLow());
int c = graphics.mapToVerticalAxis(ohlc.getClose());
int o = graphics.mapToVerticalAxis(ohlc.getOpen());
int x = graphics.mapToHorizontalAxis(ohlc.getDate());
Bar bar;
if (i < pointArray.size()) {
bar = pointArray.get(i);
bar.setBounds(x, h, o, c, l);
}
else {
bar = new Bar(x, h, o, c, l);
pointArray.add(bar);
}
bar.setOhlc(ohlc);
bar.setColor(ohlc.getClose() < ohlc.getOpen() ? negativeColor : positiveColor);
}
while (pointArray.size() > values.length) {
pointArray.remove(pointArray.size() - 1);
}
valid = true;
}
if (pointArray != null) {
graphics.pushState();
graphics.setLineWidth(hasFocus ? 2 : 1);
for (Bar c : pointArray) {
c.paint(graphics);
}
graphics.popState();
}
}
/* (non-Javadoc)
* @see org.eclipsetrader.ui.charts.IChartObject#paintScale(org.eclipsetrader.ui.charts.Graphics)
*/
@Override
public void paintScale(Graphics graphics) {
}
/* (non-Javadoc)
* @see org.eclipsetrader.ui.charts.IChartObject#containsPoint(int, int)
*/
@Override
public boolean containsPoint(int x, int y) {
if (pointArray != null) {
for (Bar c : pointArray) {
if (c.containsPoint(x, y)) {
return true;
}
}
}
return false;
}
/* (non-Javadoc)
* @see org.eclipsetrader.ui.charts.IChartObject#getDataSeries()
*/
@Override
public IDataSeries getDataSeries() {
return dataSeries;
}
/* (non-Javadoc)
* @see org.eclipsetrader.ui.charts.IChartObject#getToolTip()
*/
@Override
public String getToolTip() {
if (dataSeries.getLast() != null) {
IOHLC ohlc = (IOHLC) dataSeries.getLast().getAdapter(IOHLC.class);
return dateFormat.format(ohlc.getDate()) + " O:" + numberFormat.format(ohlc.getOpen()) + //$NON-NLS-1$
" H:" + numberFormat.format(ohlc.getHigh()) + //$NON-NLS-1$
" L:" + numberFormat.format(ohlc.getLow()) + //$NON-NLS-1$
" C:" + numberFormat.format(ohlc.getHigh()); //$NON-NLS-1$
}
return dataSeries.getName();
}
/* (non-Javadoc)
* @see org.eclipsetrader.ui.charts.IChartObject#getToolTip(int, int)
*/
@Override
public String getToolTip(int x, int y) {
if (pointArray != null) {
for (Bar c : pointArray) {
if (c.containsPoint(x, y)) {
return c.getToolTip();
}
}
}
return null;
}
/* (non-Javadoc)
* @see org.eclipsetrader.ui.charts.IChartObject#handleFocusGained(org.eclipsetrader.ui.charts.ChartObjectFocusEvent)
*/
@Override
public void handleFocusGained(ChartObjectFocusEvent event) {
hasFocus = true;
}
/* (non-Javadoc)
* @see org.eclipsetrader.ui.charts.IChartObject#handleFocusLost(org.eclipsetrader.ui.charts.ChartObjectFocusEvent)
*/
@Override
public void handleFocusLost(ChartObjectFocusEvent event) {
hasFocus = false;
}
/* (non-Javadoc)
* @see org.eclipsetrader.ui.charts.IChartObject#accept(org.eclipsetrader.ui.charts.IChartObjectVisitor)
*/
@Override
public void accept(IChartObjectVisitor visitor) {
visitor.visit(this);
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
*/
@Override
@SuppressWarnings("unchecked")
public Object getAdapter(Class adapter) {
if (adapter.isAssignableFrom(ISummaryBarDecorator.class)) {
return this;
}
return null;
}
/* (non-Javadoc)
* @see org.eclipsetrader.ui.charts.ISummaryBarDecorator#createDecorator(org.eclipse.swt.widgets.Composite)
*/
@Override
public void createDecorator(Composite parent) {
IAdaptable[] values = dataSeries.getValues();
IOHLC ohlc = (IOHLC) (values.length > 0 ? values[values.length - 1].getAdapter(IOHLC.class) : null);
IOHLC previousOhlc = (IOHLC) (values.length > 1 ? values[values.length - 2].getAdapter(IOHLC.class) : null);
dateItem = new SummaryDateItem(parent, SWT.DATE);
dateItem.setDate(ohlc != null ? ohlc.getDate() : null);
ohlcItem = new SummaryOHLCItem(parent, SWT.NONE);
ohlcItem.setOHLC(ohlc, previousOhlc);
}
/* (non-Javadoc)
* @see org.eclipsetrader.ui.charts.ISummaryBarDecorator#updateDecorator(int, int)
*/
@Override
public void updateDecorator(int x, int y) {
if (x == SWT.DEFAULT) {
IAdaptable[] values = dataSeries.getValues();
IOHLC ohlc = (IOHLC) (values.length > 0 ? values[values.length - 1].getAdapter(IOHLC.class) : null);
IOHLC previousOhlc = (IOHLC) (values.length > 1 ? values[values.length - 2].getAdapter(IOHLC.class) : null);
dateItem.setDate(ohlc != null ? ohlc.getDate() : null);
ohlcItem.setOHLC(ohlc, previousOhlc);
}
else if (pointArray != null) {
for (int i = 1; i < pointArray.size(); i++) {
Bar c = pointArray.get(i);
if (c.containsPoint(x, y)) {
dateItem.setDate(c.ohlc.getDate());
ohlcItem.setOHLC(c.ohlc, pointArray.get(i - 1).ohlc);
}
}
}
}
private class Bar {
int x;
int yHigh;
int yOpen;
int yClose;
int yLow;
IOHLC ohlc;
RGB color;
public Bar(int x, int yHigh, int yOpen, int yClose, int yLow) {
this.x = x;
this.yHigh = yHigh;
this.yOpen = yOpen;
this.yClose = yClose;
this.yLow = yLow;
}
public void setBounds(int x, int yHigh, int yOpen, int yClose, int yLow) {
this.x = x;
this.yHigh = yHigh;
this.yOpen = yOpen;
this.yClose = yClose;
this.yLow = yLow;
}
public void setOhlc(IOHLC ohlc) {
this.ohlc = ohlc;
}
public void setColor(RGB color) {
this.color = color;
}
public void paint(IGraphics graphics) {
graphics.setForegroundColor(color);
graphics.drawLine(x, yHigh, x, yLow);
graphics.drawLine(x - width / 2, yOpen, x, yOpen);
graphics.drawLine(x, yClose, x + width / 2, yClose);
}
public boolean containsPoint(int x, int y) {
if (y == SWT.DEFAULT) {
return x >= this.x - width / 2 && x <= this.x + width / 2;
}
if (x == this.x || x == SWT.DEFAULT) {
return y >= yHigh && y <= yLow;
}
if (x >= this.x - width / 2 && x <= this.x + width / 2) {
return y >= yHigh && y <= yLow;
}
return false;
}
public String getToolTip() {
return dataSeries.getName() + "\r\nD:" + dateFormat.format(ohlc.getDate()) + //$NON-NLS-1$
"\r\nO:" + numberFormat.format(ohlc.getOpen()) + //$NON-NLS-1$
"\r\nH:" + numberFormat.format(ohlc.getHigh()) + //$NON-NLS-1$
"\r\nL:" + numberFormat.format(ohlc.getLow()) + //$NON-NLS-1$
"\r\nC:" + numberFormat.format(ohlc.getClose()); //$NON-NLS-1$
}
}
}