2017-04-02 15:02:22 +02:00
|
|
|
|
2017-01-08 03:14:27 +01:00
|
|
|
/*
|
|
|
|
* To change this license header, choose License Headers in Project Properties.
|
|
|
|
* To change this template file, choose Tools | Templates
|
|
|
|
* and open the template in the editor.
|
|
|
|
*/
|
|
|
|
package chart;
|
|
|
|
|
2017-01-22 07:03:16 +01:00
|
|
|
import sesim.OHLCDataItem;
|
|
|
|
import sesim.OHLCData;
|
2017-01-08 03:14:27 +01:00
|
|
|
import java.awt.*;
|
2017-01-08 13:16:00 +01:00
|
|
|
import sesim.Exchange.*;
|
|
|
|
import sesim.Quote;
|
2017-01-22 10:04:50 +01:00
|
|
|
import gui.Globals;
|
2017-01-12 23:16:37 +01:00
|
|
|
import java.util.ArrayList;
|
2017-02-18 17:44:25 +01:00
|
|
|
import javax.swing.JViewport;
|
2017-01-23 18:56:09 +01:00
|
|
|
import javax.swing.Scrollable;
|
|
|
|
import sesim.MinMax;
|
2017-02-18 17:44:25 +01:00
|
|
|
import sesim.Scheduler;
|
2017-01-08 03:14:27 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @author 7u83 <7u83@mail.ru>
|
|
|
|
*/
|
2017-01-23 18:56:09 +01:00
|
|
|
public class Chart extends javax.swing.JPanel implements QuoteReceiver, Scrollable {
|
|
|
|
|
2017-03-27 01:11:50 +02:00
|
|
|
private int em_height;
|
|
|
|
private int em_width;
|
2017-02-18 23:32:03 +01:00
|
|
|
|
2017-04-02 20:44:14 +02:00
|
|
|
protected double x_legend_height = 6;
|
2017-03-27 01:31:55 +02:00
|
|
|
|
2017-03-27 01:11:50 +02:00
|
|
|
protected double x_unit_width = 1.0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* width of y legend in em
|
|
|
|
*/
|
2017-04-03 19:12:48 +02:00
|
|
|
protected float yl_width = 10;
|
2017-01-23 18:56:09 +01:00
|
|
|
|
|
|
|
protected int num_bars = 4000;
|
|
|
|
|
|
|
|
protected Rectangle clip_bounds = new Rectangle();
|
|
|
|
|
2017-02-04 16:29:45 +01:00
|
|
|
private int first_bar, last_bar;
|
2017-02-16 18:37:08 +01:00
|
|
|
|
2017-01-08 03:14:27 +01:00
|
|
|
/**
|
|
|
|
* Creates new form Chart
|
|
|
|
*/
|
|
|
|
public Chart() {
|
2017-02-18 23:32:03 +01:00
|
|
|
if (Globals.se == null) {
|
2017-02-18 17:44:25 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-01-08 03:14:27 +01:00
|
|
|
initComponents();
|
2017-01-22 10:04:50 +01:00
|
|
|
if (Globals.se == null) {
|
2017-01-09 08:25:29 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-01-22 10:04:50 +01:00
|
|
|
Globals.se.addQuoteReceiver(this);
|
2017-01-08 03:14:27 +01:00
|
|
|
|
|
|
|
}
|
2017-01-09 08:25:29 +01:00
|
|
|
|
2017-01-23 18:56:09 +01:00
|
|
|
@Override
|
|
|
|
public Dimension getPreferredScrollableViewportSize() {
|
|
|
|
return this.getPreferredSize();
|
|
|
|
}
|
2017-01-20 17:03:50 +01:00
|
|
|
|
2017-01-23 18:56:09 +01:00
|
|
|
@Override
|
|
|
|
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
|
|
|
|
return 100;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean getScrollableTracksViewportWidth() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean getScrollableTracksViewportHeight() {
|
|
|
|
return false;
|
2017-01-13 08:16:09 +01:00
|
|
|
}
|
2017-01-13 21:49:09 +01:00
|
|
|
|
2017-01-20 17:03:50 +01:00
|
|
|
class XLegendDef {
|
|
|
|
|
2017-04-02 20:44:14 +02:00
|
|
|
//int big_tick = 10;
|
|
|
|
// long start;
|
2017-01-22 07:03:16 +01:00
|
|
|
XLegendDef() {
|
|
|
|
|
2017-01-20 17:03:50 +01:00
|
|
|
}
|
2017-01-22 07:03:16 +01:00
|
|
|
|
|
|
|
String getAt(int unit) {
|
2017-02-18 23:32:03 +01:00
|
|
|
|
2017-02-18 17:44:25 +01:00
|
|
|
int fs = data.getFrameSize();
|
2017-02-18 23:32:03 +01:00
|
|
|
return Scheduler.formatTimeMillis(0 + unit * fs);
|
2017-02-18 17:44:25 +01:00
|
|
|
|
2017-01-20 17:03:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2017-04-02 20:44:14 +02:00
|
|
|
|
|
|
|
protected void setXLegendHeight(int h) {
|
|
|
|
this.x_legend_height = h;
|
2017-04-02 16:30:37 +02:00
|
|
|
}
|
2017-04-02 20:44:14 +02:00
|
|
|
|
|
|
|
/**
|
2017-04-03 18:54:32 +02:00
|
|
|
* Text Color of X-legend
|
2017-04-02 20:44:14 +02:00
|
|
|
*/
|
|
|
|
protected Color xl_color = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Background color of X-legend
|
|
|
|
*/
|
|
|
|
protected Color xl_bgcolor = null;
|
|
|
|
/**
|
|
|
|
* Height of X-legend
|
|
|
|
*/
|
|
|
|
protected int xl_height;
|
2017-01-20 17:03:50 +01:00
|
|
|
|
2017-02-18 23:32:03 +01:00
|
|
|
/**
|
|
|
|
* Draw the one and only one X legend
|
|
|
|
*
|
|
|
|
* @param g Graphics conext to draw
|
|
|
|
* @param xld Definition
|
|
|
|
*/
|
2017-01-22 07:03:16 +01:00
|
|
|
void drawXLegend(Graphics2D g, XLegendDef xld) {
|
2017-01-20 17:03:50 +01:00
|
|
|
|
2017-04-03 21:07:20 +02:00
|
|
|
Color cur = g.getColor(); // save current color
|
|
|
|
|
2017-04-03 19:12:48 +02:00
|
|
|
// Caluclate with of y legend in pixels
|
|
|
|
int yl_width_p = (int) (yl_width * em_width);
|
2017-02-20 08:21:01 +01:00
|
|
|
|
2017-04-03 19:12:48 +02:00
|
|
|
g.setClip(clip_bounds.x, clip_bounds.y, clip_bounds.width - yl_width_p, clip_bounds.height);
|
2017-04-03 08:16:40 +02:00
|
|
|
|
|
|
|
int y = clip_bounds.height - em_height * xl_height;
|
2017-01-13 08:16:09 +01:00
|
|
|
|
2017-04-03 21:07:20 +02:00
|
|
|
// Draw background
|
2017-04-02 20:44:14 +02:00
|
|
|
if (this.xl_bgcolor != null) {
|
2017-04-03 21:07:20 +02:00
|
|
|
|
2017-04-02 20:44:14 +02:00
|
|
|
g.setColor(xl_bgcolor);
|
2017-04-03 19:12:48 +02:00
|
|
|
g.fillRect(clip_bounds.x, y, clip_bounds.width, em_height * xl_height);
|
|
|
|
g.drawRect(clip_bounds.y, y, clip_bounds.width, em_height * xl_height);
|
2017-04-02 20:44:14 +02:00
|
|
|
g.setColor(cur);
|
|
|
|
}
|
|
|
|
|
2017-04-03 21:07:20 +02:00
|
|
|
if (xl_color != null) {
|
|
|
|
g.setColor(xl_color);
|
|
|
|
}
|
2017-09-10 00:52:43 +02:00
|
|
|
|
|
|
|
g.drawLine(clip_bounds.x, y, clip_bounds.width, y);
|
2017-04-03 21:07:20 +02:00
|
|
|
|
2017-04-03 19:12:48 +02:00
|
|
|
Dimension dim = getSize();
|
|
|
|
|
2017-01-22 07:03:16 +01:00
|
|
|
int n;
|
|
|
|
double x;
|
2017-03-27 01:31:55 +02:00
|
|
|
|
2017-03-24 19:55:56 +01:00
|
|
|
long big_tick = 1;
|
|
|
|
|
2017-03-27 01:31:55 +02:00
|
|
|
double btl, xxx;
|
2017-03-24 19:55:56 +01:00
|
|
|
do {
|
|
|
|
big_tick++;
|
2017-03-27 01:31:55 +02:00
|
|
|
btl = em_width * big_tick * x_unit_width;
|
|
|
|
xxx = 7 * em_width;
|
|
|
|
|
|
|
|
} while (btl < xxx);
|
2017-03-25 18:14:23 +01:00
|
|
|
|
2017-03-27 01:11:50 +02:00
|
|
|
for (n = 0, x = 0; x < dim.width; x += em_width * x_unit_width) {
|
2017-01-20 17:03:50 +01:00
|
|
|
|
2017-03-25 18:14:23 +01:00
|
|
|
if (n % big_tick == 1) {
|
2017-03-27 01:11:50 +02:00
|
|
|
g.drawLine((int) x, y, (int) x, y + em_width);
|
2017-02-18 17:44:25 +01:00
|
|
|
String text;
|
2017-01-22 07:03:16 +01:00
|
|
|
text = xld.getAt(n);
|
2017-02-18 23:32:03 +01:00
|
|
|
|
2017-01-20 17:03:50 +01:00
|
|
|
int swidth = g.getFontMetrics().stringWidth(text);
|
|
|
|
|
2017-03-27 01:31:55 +02:00
|
|
|
g.drawString(text, (int) x - swidth / 2, y + em_height * 2);
|
2017-03-24 19:55:56 +01:00
|
|
|
} else {
|
2017-03-27 01:11:50 +02:00
|
|
|
g.drawLine((int) x, y, (int) x, y + em_width / 2);
|
2017-03-24 19:55:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (n % big_tick == 0) {
|
|
|
|
|
2017-01-20 17:03:50 +01:00
|
|
|
}
|
2017-01-22 07:03:16 +01:00
|
|
|
|
2017-03-27 01:31:55 +02:00
|
|
|
n += 1;
|
2017-01-20 17:03:50 +01:00
|
|
|
}
|
2017-04-03 21:07:20 +02:00
|
|
|
|
|
|
|
g.setColor(cur);
|
2017-01-22 07:03:16 +01:00
|
|
|
}
|
2017-01-20 17:03:50 +01:00
|
|
|
|
2017-01-22 07:03:16 +01:00
|
|
|
class RenderCtx {
|
2017-01-13 08:16:09 +01:00
|
|
|
|
2017-01-22 07:03:16 +01:00
|
|
|
Rectangle rect;
|
|
|
|
float scaling;
|
|
|
|
float min;
|
|
|
|
Graphics2D g;
|
|
|
|
float iwidth;
|
2017-01-13 08:16:09 +01:00
|
|
|
|
2017-04-02 15:02:22 +02:00
|
|
|
float getY(float y) {
|
|
|
|
|
|
|
|
float ys = rect.height / c_mm.getDiff();
|
|
|
|
if (c_mm.isLog()) {
|
|
|
|
return rect.height + rect.y - ((float) Math.log(y) - c_mm.getMin()) * ys;
|
|
|
|
}
|
|
|
|
return (rect.height - ((y - c_mm.getMin()) * c_yscaling)) + rect.y;
|
2017-01-22 07:03:16 +01:00
|
|
|
}
|
2017-04-03 21:07:20 +02:00
|
|
|
|
|
|
|
double getValAtY(float y) {
|
|
|
|
float val = 0;
|
|
|
|
|
|
|
|
if (c_mm.isLog()) {
|
|
|
|
float ys = rect.height / c_mm.getDiff();
|
|
|
|
|
|
|
|
return Math.exp((rect.height + rect.y) / ys + c_mm.getMin() - y / ys);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return (-(y - rect.y - rect.height)) / c_yscaling + c_mm.getMin();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-01-22 07:03:16 +01:00
|
|
|
}
|
2017-01-13 08:16:09 +01:00
|
|
|
|
2017-09-10 00:52:43 +02:00
|
|
|
private void drawLineItem(RenderCtx ctx, int prevx, int x, OHLCDataItem prev, OHLCDataItem i) {
|
|
|
|
Graphics2D g = ctx.g;
|
|
|
|
if (prev == null) {
|
|
|
|
prev = i;
|
|
|
|
}
|
|
|
|
int y1 = (int) ctx.getY(prev.close);
|
|
|
|
int y2 = (int) ctx.getY(i.close);
|
|
|
|
Color cur = g.getColor();
|
|
|
|
g.setColor(Color.RED);
|
|
|
|
g.drawLine(prevx, y1, x, y2);
|
|
|
|
g.setColor(cur);
|
|
|
|
}
|
|
|
|
|
2017-02-18 23:32:03 +01:00
|
|
|
private void drawCandleItem(RenderCtx ctx, int prevx, int x, OHLCDataItem prev, OHLCDataItem i) {
|
2017-01-22 07:03:16 +01:00
|
|
|
|
|
|
|
Graphics2D g = ctx.g;
|
|
|
|
|
|
|
|
if (i.open < i.close) {
|
2017-01-23 18:56:09 +01:00
|
|
|
int xl = (int) (x + ctx.iwidth / 2);
|
2017-01-22 07:03:16 +01:00
|
|
|
|
|
|
|
g.setColor(Color.BLACK);
|
2017-04-02 15:02:22 +02:00
|
|
|
g.drawLine(xl, (int) ctx.getY(i.close), xl, (int) ctx.getY(i.high));
|
|
|
|
g.drawLine(xl, (int) ctx.getY(i.low), xl, (int) ctx.getY(i.open));
|
2017-01-22 07:03:16 +01:00
|
|
|
|
|
|
|
float w = ctx.iwidth;
|
2017-04-02 15:02:22 +02:00
|
|
|
float h = (int) (ctx.getY(i.open) - ctx.getY(i.close));
|
2017-01-22 07:03:16 +01:00
|
|
|
|
|
|
|
g.setColor(Color.GREEN);
|
2017-04-02 15:02:22 +02:00
|
|
|
g.fillRect((int) (x), (int) ctx.getY(i.close), (int) w, (int) h);
|
2017-01-22 07:03:16 +01:00
|
|
|
g.setColor(Color.BLACK);
|
2017-04-02 15:02:22 +02:00
|
|
|
g.drawRect((int) (x), (int) ctx.getY(i.close), (int) w, (int) h);
|
2017-01-22 07:03:16 +01:00
|
|
|
|
|
|
|
} else {
|
2017-01-23 18:56:09 +01:00
|
|
|
int xl = (int) (x + ctx.iwidth / 2);
|
2017-01-22 07:03:16 +01:00
|
|
|
g.setColor(Color.RED);
|
2017-04-02 15:02:22 +02:00
|
|
|
g.drawLine(xl, (int) ctx.getY(i.high), xl, (int) ctx.getY(i.close));
|
|
|
|
g.drawLine(xl, (int) ctx.getY(i.open), xl, (int) ctx.getY(i.low));
|
2017-01-22 07:03:16 +01:00
|
|
|
|
|
|
|
float w = ctx.iwidth;
|
2017-04-02 15:02:22 +02:00
|
|
|
float h = (int) (ctx.getY(i.close) - ctx.getY(i.open));
|
2017-01-22 07:03:16 +01:00
|
|
|
|
2017-04-02 15:02:22 +02:00
|
|
|
g.fillRect((int) (x), (int) ctx.getY(i.open), (int) w, (int) h);
|
2017-01-22 07:03:16 +01:00
|
|
|
g.setColor(Color.BLACK);
|
2017-04-02 15:02:22 +02:00
|
|
|
g.drawRect((int) (x), (int) ctx.getY(i.open), (int) w, (int) h);
|
2017-01-09 08:25:29 +01:00
|
|
|
|
2017-01-08 13:16:00 +01:00
|
|
|
}
|
2017-01-09 08:25:29 +01:00
|
|
|
|
2017-01-08 13:16:00 +01:00
|
|
|
}
|
2017-01-09 08:25:29 +01:00
|
|
|
|
2017-02-18 23:32:03 +01:00
|
|
|
private void drawBarItem(RenderCtx ctx, int prevx, int x, OHLCDataItem prev, OHLCDataItem i) {
|
|
|
|
Graphics2D g = ctx.g;
|
2017-02-19 15:04:07 +01:00
|
|
|
g.setColor(Color.BLACK);
|
2017-02-18 23:32:03 +01:00
|
|
|
|
2017-04-02 15:02:22 +02:00
|
|
|
g.drawLine(x, (int) ctx.getY(0), x, (int) ctx.getY(i.volume));
|
2017-02-18 23:32:03 +01:00
|
|
|
|
|
|
|
Rectangle r = ctx.rect;
|
|
|
|
}
|
|
|
|
|
2017-03-27 00:44:42 +02:00
|
|
|
/**
|
|
|
|
* Char types
|
|
|
|
*/
|
|
|
|
protected enum ChartType {
|
2017-02-18 23:32:03 +01:00
|
|
|
CANDLESTICK,
|
2017-04-04 01:17:51 +02:00
|
|
|
LINE,
|
2017-02-18 23:32:03 +01:00
|
|
|
BAR,
|
|
|
|
VOL,
|
|
|
|
}
|
|
|
|
|
|
|
|
ChartType ct = ChartType.CANDLESTICK;
|
|
|
|
|
|
|
|
private void drawItem(RenderCtx ctx, int prevx, int x, OHLCDataItem prev, OHLCDataItem i) {
|
|
|
|
switch (ct) {
|
|
|
|
case CANDLESTICK:
|
|
|
|
this.drawCandleItem(ctx, prevx, x, prev, i);
|
|
|
|
break;
|
2017-04-04 01:17:51 +02:00
|
|
|
case LINE:
|
|
|
|
this.drawLineItem(ctx, prevx, x, prev, i);
|
|
|
|
break;
|
2017-02-18 23:32:03 +01:00
|
|
|
case VOL:
|
|
|
|
this.drawBarItem(ctx, prevx, x, prev, i);
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-23 18:56:09 +01:00
|
|
|
float c_yscaling;
|
|
|
|
|
2017-02-18 23:32:03 +01:00
|
|
|
private void drawYLegend(RenderCtx ctx) {
|
2017-01-23 18:56:09 +01:00
|
|
|
|
2017-02-19 15:04:07 +01:00
|
|
|
Graphics2D g = ctx.g;
|
|
|
|
|
|
|
|
Rectangle dim;
|
2017-02-18 23:32:03 +01:00
|
|
|
dim = this.clip_bounds;
|
|
|
|
|
2017-04-02 15:02:22 +02:00
|
|
|
// Dimension rv = this.getSize();
|
2017-04-03 19:12:48 +02:00
|
|
|
int yw = (int) (this.yl_width * this.em_width);
|
2017-01-23 18:56:09 +01:00
|
|
|
|
|
|
|
g.drawLine(dim.width + dim.x - yw, 0, dim.width + dim.x - yw, dim.height);
|
2017-03-27 01:31:55 +02:00
|
|
|
|
2017-04-02 15:02:22 +02:00
|
|
|
float y1 = ctx.getY(c_mm.getMin(false));
|
|
|
|
float y2 = ctx.getY(c_mm.getMax(false));
|
2017-03-27 01:31:55 +02:00
|
|
|
float ydiff = y1 - y2;
|
2017-04-03 18:54:32 +02:00
|
|
|
// System.out.printf("%s y1: %f, y2: %f, diff %f\n", Boolean.toString(c_mm.isLog()), y1, y2, ydiff);
|
2017-01-23 18:56:09 +01:00
|
|
|
|
2017-03-31 18:55:24 +02:00
|
|
|
for (int yp = (int) y2; yp < y1; yp += em_width * 5) {
|
2017-03-27 01:31:55 +02:00
|
|
|
g.drawLine(dim.width + dim.x - yw, yp, dim.width + dim.x - yw + em_width, yp);
|
2017-04-03 21:07:20 +02:00
|
|
|
double v1 = ctx.getValAtY(yp);
|
2017-03-27 01:31:55 +02:00
|
|
|
g.drawString(String.format("%.2f", v1), dim.width + dim.x - yw + em_width * 1.5f, yp + c_font_height / 3);
|
|
|
|
}
|
2017-01-23 18:56:09 +01:00
|
|
|
|
2017-03-27 01:31:55 +02:00
|
|
|
double v1, v2;
|
2017-04-03 21:07:20 +02:00
|
|
|
v1 = ctx.getValAtY(y1);
|
|
|
|
v2 = ctx.getValAtY(y2);
|
2017-01-23 18:56:09 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private MinMax c_mm = null;
|
2017-04-03 21:07:20 +02:00
|
|
|
// private Rectangle c_rect;
|
2017-01-23 18:56:09 +01:00
|
|
|
|
2017-02-18 23:32:03 +01:00
|
|
|
void drawChart(RenderCtx ctx) {
|
|
|
|
|
2017-04-03 21:07:20 +02:00
|
|
|
c_yscaling = ctx.rect.height / c_mm.getDiff();
|
2017-02-18 23:32:03 +01:00
|
|
|
|
|
|
|
ctx.g.setClip(null);
|
2017-02-19 15:04:07 +01:00
|
|
|
// ctx.g.setColor(Color.ORANGE);
|
|
|
|
// ctx.g.setClip(ctx.rect.x, ctx.rect.y, ctx.rect.width, ctx.rect.height);
|
|
|
|
// ctx.g.drawRect(ctx.rect.x, ctx.rect.y, ctx.rect.width, ctx.rect.height);
|
2017-02-18 23:32:03 +01:00
|
|
|
this.drawYLegend(ctx);
|
|
|
|
|
2017-02-19 15:04:07 +01:00
|
|
|
/// ctx.g.setColor(Color.ORANGE);
|
2017-04-03 19:12:48 +02:00
|
|
|
int yw = (int) (this.yl_width * this.em_width);
|
2017-02-19 15:04:07 +01:00
|
|
|
|
|
|
|
ctx.g.setClip(clip_bounds.x, clip_bounds.y, clip_bounds.width - yw, clip_bounds.height);
|
2017-02-18 23:32:03 +01:00
|
|
|
// ctx.g.setClip(ctx.rect.x, ctx.rect.y, ctx.rect.width-yw, ctx.rect.height);
|
|
|
|
|
|
|
|
OHLCDataItem prev = null;
|
|
|
|
for (int i = first_bar; i < last_bar && i < data.size(); i++) {
|
|
|
|
OHLCDataItem di = data.get(i);
|
2017-03-27 01:11:50 +02:00
|
|
|
int x = (int) (i * em_width * x_unit_width); //em_width;
|
2017-04-04 01:17:51 +02:00
|
|
|
this.drawItem(ctx, (int) (x - em_width * x_unit_width), x, prev, di); //, ctx.scaling, data.getMin());
|
2017-02-18 23:32:03 +01:00
|
|
|
|
|
|
|
// myi++;
|
|
|
|
prev = di;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-02-19 15:04:07 +01:00
|
|
|
boolean autoScroll = true;
|
|
|
|
int lastvpos = 0;
|
|
|
|
|
2017-04-03 21:07:20 +02:00
|
|
|
/**
|
|
|
|
* definition for a sub-chart window
|
|
|
|
*/
|
2017-04-04 01:17:51 +02:00
|
|
|
public class SubChartDef {
|
2017-04-02 15:02:22 +02:00
|
|
|
|
2017-04-03 21:07:20 +02:00
|
|
|
/**
|
|
|
|
* Height of sub-chart in percent
|
|
|
|
*/
|
2017-04-04 01:17:51 +02:00
|
|
|
public float height;
|
2017-04-03 21:07:20 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* top padding in em_height
|
|
|
|
*/
|
2017-04-04 01:17:51 +02:00
|
|
|
public float padding_top = 0;
|
2017-04-03 21:07:20 +02:00
|
|
|
/**
|
2017-04-04 01:17:51 +02:00
|
|
|
* bottom padding in em_height (not implemented yet)
|
2017-04-03 21:07:20 +02:00
|
|
|
*/
|
2017-04-04 01:17:51 +02:00
|
|
|
public float padding_bottom = 0;
|
2017-04-03 21:07:20 +02:00
|
|
|
|
2017-04-04 01:17:51 +02:00
|
|
|
public ChartType type;
|
|
|
|
public OHLCData data;
|
2017-04-02 20:44:14 +02:00
|
|
|
|
2017-04-04 01:17:51 +02:00
|
|
|
public Color bgcolor = null;
|
2017-09-10 00:52:43 +02:00
|
|
|
|
2017-04-04 01:17:51 +02:00
|
|
|
/**
|
|
|
|
* logarithmic scaling
|
|
|
|
*/
|
2017-09-10 00:52:43 +02:00
|
|
|
public boolean log = false;
|
2017-04-02 15:02:22 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-04-04 01:17:51 +02:00
|
|
|
protected OHLCData data;
|
2017-09-10 00:52:43 +02:00
|
|
|
|
2017-04-03 21:07:20 +02:00
|
|
|
ArrayList<SubChartDef> charts = new ArrayList<>();
|
2017-04-02 15:02:22 +02:00
|
|
|
|
2017-04-04 01:17:51 +02:00
|
|
|
protected void addChart(SubChartDef d) {
|
2017-04-02 15:02:22 +02:00
|
|
|
|
|
|
|
charts.add(d);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void drawAll(Graphics2D g) {
|
|
|
|
int pwidth = (int) (em_width * x_unit_width * (num_bars + 1)) + clip_bounds.width;
|
|
|
|
this.setPreferredSize(new Dimension(pwidth, gdim.height));
|
|
|
|
this.revalidate();
|
|
|
|
|
|
|
|
int h1 = 0;
|
|
|
|
|
2017-04-03 21:07:20 +02:00
|
|
|
for (SubChartDef d : charts) {
|
2017-09-10 00:52:43 +02:00
|
|
|
|
|
|
|
if (d.data == null) {
|
2017-04-04 01:17:51 +02:00
|
|
|
System.out.printf("Data is null\n");
|
2017-09-10 00:52:43 +02:00
|
|
|
System.exit(0);
|
2017-04-04 01:17:51 +02:00
|
|
|
}
|
2017-04-02 15:02:22 +02:00
|
|
|
|
2017-04-03 21:07:20 +02:00
|
|
|
// calclulate the min/max values
|
2017-04-02 15:02:22 +02:00
|
|
|
switch (d.type) {
|
|
|
|
case VOL:
|
|
|
|
c_mm = d.data.getVolMinMax(first_bar, last_bar);
|
|
|
|
c_mm.setMin(0);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
c_mm = d.data.getMinMax(first_bar, last_bar);
|
|
|
|
}
|
|
|
|
|
2017-04-03 21:07:20 +02:00
|
|
|
// Calculate the height for all sub-charts
|
|
|
|
// this is the height of out panel minus the height of x-legend
|
|
|
|
int chartwin_height = clip_bounds.height - xl_height * em_height;
|
2017-04-03 19:12:48 +02:00
|
|
|
|
2017-04-03 21:07:20 +02:00
|
|
|
// Caclulate the height of our sub-chart
|
|
|
|
int subchartwin_height = (int) (chartwin_height * d.height);
|
2017-04-03 19:12:48 +02:00
|
|
|
|
2017-04-03 21:07:20 +02:00
|
|
|
// Draw background
|
|
|
|
if (d.bgcolor != null) {
|
|
|
|
Color cur = g.getColor();
|
|
|
|
g.setColor(d.bgcolor);
|
|
|
|
g.fillRect(clip_bounds.x, h1, clip_bounds.width, subchartwin_height);
|
|
|
|
g.drawRect(clip_bounds.x, h1, clip_bounds.width, subchartwin_height);
|
|
|
|
g.setColor(cur);
|
|
|
|
}
|
2017-04-03 18:54:32 +02:00
|
|
|
|
2017-04-03 21:07:20 +02:00
|
|
|
// Caclulate the top padding
|
|
|
|
int pad_top = (int) (subchartwin_height * d.padding_top);
|
2017-04-02 15:02:22 +02:00
|
|
|
|
|
|
|
RenderCtx ctx = new RenderCtx();
|
|
|
|
|
2017-04-03 21:07:20 +02:00
|
|
|
ctx.rect = new Rectangle(0, h1 + pad_top, pwidth, subchartwin_height - pad_top);
|
|
|
|
ctx.scaling = (float) ctx.rect.height / (c_mm.getMax() - c_mm.getMin());
|
2017-04-02 15:02:22 +02:00
|
|
|
ctx.min = c_mm.getMin();
|
|
|
|
ctx.g = g;
|
|
|
|
ctx.iwidth = (float) ((x_unit_width * em_width) * 0.9f);
|
|
|
|
|
|
|
|
this.ct = d.type;
|
2017-04-04 01:17:51 +02:00
|
|
|
// logs = false;
|
|
|
|
c_mm.setLog(d.log);
|
2017-04-02 20:44:14 +02:00
|
|
|
|
2017-04-02 15:02:22 +02:00
|
|
|
drawChart(ctx);
|
|
|
|
|
2017-04-03 21:07:20 +02:00
|
|
|
h1 = h1 + subchartwin_height;
|
2017-04-02 15:02:22 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2017-09-10 00:52:43 +02:00
|
|
|
|
|
|
|
protected void setupSubCharts() {
|
|
|
|
|
2017-04-04 01:17:51 +02:00
|
|
|
}
|
2017-04-02 15:02:22 +02:00
|
|
|
|
2017-01-22 07:03:16 +01:00
|
|
|
private void draw(Graphics2D g) {
|
2017-01-23 18:56:09 +01:00
|
|
|
|
|
|
|
if (data == null) {
|
2017-01-22 08:53:02 +01:00
|
|
|
return;
|
2017-01-23 18:56:09 +01:00
|
|
|
}
|
|
|
|
if (data.size() == 0) {
|
2017-01-22 08:53:02 +01:00
|
|
|
return;
|
2017-01-23 18:56:09 +01:00
|
|
|
}
|
2017-09-10 00:52:43 +02:00
|
|
|
|
2017-04-18 08:20:44 +02:00
|
|
|
// Point m = MouseInfo.getPointerInfo().getLocation() ;
|
|
|
|
// g.drawLine(0, m.y, 1000, m.y);
|
2017-04-05 17:40:23 +02:00
|
|
|
int pwidth = (int) (em_width * x_unit_width * (num_bars + 1)) + clip_bounds.width;
|
2017-04-04 14:59:30 +02:00
|
|
|
|
|
|
|
this.setPreferredSize(new Dimension(pwidth, gdim.height));
|
|
|
|
this.revalidate();
|
|
|
|
|
|
|
|
int bww = (int) (data.size() * (this.x_unit_width * this.em_width));
|
|
|
|
int p0 = pwidth - clip_bounds.width - (clip_bounds.width - (int) (13 * em_width));
|
|
|
|
if (p0 < 0) {
|
|
|
|
p0 = 0;
|
|
|
|
}
|
|
|
|
JViewport vp = (JViewport) this.getParent();
|
2017-09-30 08:03:48 +02:00
|
|
|
// Point pp = vp.getViewPosition();
|
2017-04-04 14:59:30 +02:00
|
|
|
Point cp = vp.getViewPosition();
|
|
|
|
|
|
|
|
if (autoScroll && this.lastvpos != cp.x) {
|
|
|
|
autoScroll = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!autoScroll && cp.x >= p0) {
|
|
|
|
autoScroll = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (autoScroll) {
|
|
|
|
vp.setViewPosition(new Point(p0, 0));
|
|
|
|
lastvpos = p0;
|
|
|
|
|
|
|
|
}
|
2017-04-03 19:12:48 +02:00
|
|
|
|
2017-04-04 01:17:51 +02:00
|
|
|
this.charts = new ArrayList<>();
|
|
|
|
setupSubCharts();
|
2017-04-02 15:02:22 +02:00
|
|
|
|
|
|
|
num_bars = data.size();
|
|
|
|
|
|
|
|
em_height = g.getFontMetrics().getHeight();
|
|
|
|
em_width = g.getFontMetrics().stringWidth("M");
|
|
|
|
|
|
|
|
XLegendDef xld = new XLegendDef();
|
|
|
|
this.drawXLegend(g, xld);
|
|
|
|
|
|
|
|
drawAll(g);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Dimension gdim;
|
|
|
|
|
2017-04-03 21:07:20 +02:00
|
|
|
Rectangle c_rect0;
|
|
|
|
|
2017-04-02 15:02:22 +02:00
|
|
|
|
2017-01-23 18:56:09 +01:00
|
|
|
private float c_font_height;
|
2017-01-08 08:48:31 +01:00
|
|
|
|
2017-01-23 18:56:09 +01:00
|
|
|
@Override
|
|
|
|
public final void paintComponent(Graphics g) {
|
2017-03-27 01:31:55 +02:00
|
|
|
if (Globals.se == null) {
|
2017-02-20 08:21:01 +01:00
|
|
|
return;
|
|
|
|
}
|
2017-03-27 01:31:55 +02:00
|
|
|
|
2017-01-23 18:56:09 +01:00
|
|
|
super.paintComponent(g);
|
2017-01-08 08:48:31 +01:00
|
|
|
|
2017-02-18 17:44:25 +01:00
|
|
|
// Calculate the number of pixels for 1 em
|
2017-03-27 01:11:50 +02:00
|
|
|
em_width = g.getFontMetrics().stringWidth("M");
|
2017-02-18 17:44:25 +01:00
|
|
|
|
2017-01-31 08:04:11 +01:00
|
|
|
this.gdim = this.getParent().getSize(gdim);
|
|
|
|
this.getParent().setPreferredSize(gdim);
|
2017-02-16 18:37:08 +01:00
|
|
|
|
2017-02-18 23:32:03 +01:00
|
|
|
//Object o = this.getParent();
|
2017-02-18 17:44:25 +01:00
|
|
|
JViewport vp = (JViewport) this.getParent();
|
2017-02-18 23:32:03 +01:00
|
|
|
|
|
|
|
//this.clip_bounds=g.getClipBounds();
|
|
|
|
this.clip_bounds = vp.getViewRect();
|
|
|
|
|
2017-03-27 01:11:50 +02:00
|
|
|
first_bar = (int) (clip_bounds.x / (this.x_unit_width * this.em_width));
|
2017-04-03 19:12:48 +02:00
|
|
|
last_bar = 1 + (int) ((clip_bounds.x + clip_bounds.width - (this.yl_width * em_width)) / (this.x_unit_width * this.em_width));
|
2017-02-18 23:32:03 +01:00
|
|
|
|
2017-03-27 01:11:50 +02:00
|
|
|
// num_bars = data.size(); // + (int) (clip_bounds.width / (this.x_unit_width * this.em_width))+5;
|
2017-03-26 21:36:53 +02:00
|
|
|
// num_bars=1;
|
2017-02-19 15:04:07 +01:00
|
|
|
c_font_height = g.getFontMetrics().getHeight();
|
2017-01-08 08:48:31 +01:00
|
|
|
|
2017-01-23 18:56:09 +01:00
|
|
|
draw((Graphics2D) g);
|
2017-01-08 03:14:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This method is called from within the constructor to initialize the form.
|
|
|
|
* WARNING: Do NOT modify this code. The content of this method is always
|
|
|
|
* regenerated by the Form Editor.
|
|
|
|
*/
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
|
|
|
private void initComponents() {
|
|
|
|
|
2017-01-08 13:16:00 +01:00
|
|
|
setBackground(java.awt.Color.white);
|
2017-01-22 21:41:45 +01:00
|
|
|
setBorder(null);
|
|
|
|
setOpaque(false);
|
2017-01-08 13:16:00 +01:00
|
|
|
setPreferredSize(new java.awt.Dimension(300, 300));
|
2017-01-08 08:48:31 +01:00
|
|
|
setRequestFocusEnabled(false);
|
2017-04-18 08:20:44 +02:00
|
|
|
addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
|
|
|
|
public void mouseMoved(java.awt.event.MouseEvent evt) {
|
|
|
|
formMouseMoved(evt);
|
|
|
|
}
|
|
|
|
});
|
2017-01-08 08:48:31 +01:00
|
|
|
|
2017-01-08 03:14:27 +01:00
|
|
|
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
|
|
|
this.setLayout(layout);
|
|
|
|
layout.setHorizontalGroup(
|
|
|
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
2017-02-18 17:44:25 +01:00
|
|
|
.addGap(0, 589, Short.MAX_VALUE)
|
2017-01-08 03:14:27 +01:00
|
|
|
);
|
|
|
|
layout.setVerticalGroup(
|
|
|
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
2017-01-22 21:41:45 +01:00
|
|
|
.addGap(0, 300, Short.MAX_VALUE)
|
2017-01-08 03:14:27 +01:00
|
|
|
);
|
|
|
|
}// </editor-fold>//GEN-END:initComponents
|
|
|
|
|
2017-04-18 08:20:44 +02:00
|
|
|
private void formMouseMoved(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseMoved
|
2017-09-10 00:52:43 +02:00
|
|
|
|
2017-04-18 08:20:44 +02:00
|
|
|
}//GEN-LAST:event_formMouseMoved
|
|
|
|
|
2017-01-08 13:16:00 +01:00
|
|
|
@Override
|
|
|
|
public void UpdateQuote(Quote q) {
|
2017-01-09 08:25:29 +01:00
|
|
|
this.repaint();
|
2017-01-08 13:16:00 +01:00
|
|
|
}
|
|
|
|
|
2017-01-08 03:14:27 +01:00
|
|
|
|
|
|
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
|
|
|
// End of variables declaration//GEN-END:variables
|
|
|
|
}
|