www.pudn.com > JfreeChartPanel.zip > FastChartPanel.java, change:2006-08-15,size:48441b
//Put this into your application package
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.List;
import org.jfree.chart.ChartMouseEvent;
import org.jfree.chart.ChartMouseListener;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.entity.ChartEntity;
import org.jfree.chart.entity.EntityCollection;
import org.jfree.chart.plot.FastCombinedDomainXYPlot;
import org.jfree.chart.plot.FastXYPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.xy.FastXYSeries;
import org.jfree.ui.RectangleEdge;
import org.jfree.util.ShapeUtilities;
/**
*
* <p>Title: FastChartPanel.</p>
*
* <p>Description: This class extends ChartPanel, ostensibly to add the ability
* for the 'Scroll-Line'. Other small changes to ChartPanel that we needed are
* included here as well. Minimal changes to the original ChartPanel becuase
* other people use this copy of JFreeChart.</p>
*
* Note to others: MUCH of the stuff here is for application specific work. You
* certainly don't need most of the methods within.
*
*/
public class FastChartPanel extends ChartPanel implements ActionListener {
/**
* The Shape to draw at the final point.
*/
public static final Shape POINT =
new java.awt.geom.Rectangle2D.Double( -4.0, -4.0, 8.0, 8.0);
/**
* Number of days before Jan 1st 1970 that corresponds to MJD 0.0.
*/
public static final double MJD_OFFSET = 587.0;
/**
* An amount to increment by.
*/
public static final double SECS = 1000.0;
/**
* An amount to increment by.
*/
public static final double MINS = 60000.0;
/**
* An amount to increment by.
*/
public static final double LOW_GEAR = 1;
/**
* An amount to increment by.
*/
public static final double MED_GEAR = 5;
/**
* An amount to increment by.
*/
public static final double HIGH_GEAR = 25;
/**
* Number of milliseconds in a day.
*/
public static final long MSECS_PER_DAY = 86400000;
/**
* Denoting the Scroll-line moving right.
*/
public static final int SCROLLING_RIGHT = 1;
/**
* Denoting the Scroll-line moving left.
*/
public static final int SCROLLING_LEFT = -1;
/**
* Denoting the Scroll-line not moving.
*/
public static final int SCROLLING_NOT = 10;
/**
* The last position (actual time) held by the Scroll-line.
*/
private double lastPosition;
/**
* The last index selected in teh seriesBox.
*/
private int lastIndex;
/**
* Amount to increment by.
*/
private double amount = 1000.0;
/**
* The Scroll-line.
*/
private Line2D scrollLine = null;
/**
* Flag stating whether scrolling or not.
*/
private boolean scrollMode = false;
/**
* IsDragging flag.
*/
private boolean isDragging;
/**
* X versus Y flag.
*/
private boolean XvsY = false;
/**
* Flag used to denote arrow enabled status.
*/
private boolean someArrowsDisabled = false;
/**
* List of XYSeriesInfos displayed on an XvsY plot.
*/
private XYSeriesInfo[] scrollXYSeriesList;
/**
* Array of indexes to XYSeriesInfos.
*/
private int[] indexes;
/**
* An array of shapes to be used to store the translated Shapes.
*/
private Shape[] points;
/**
* Point used to obtaing the scaled Data Area.
*/
private Point2D scrollPoint = null;
/**
* The plotpanel that owns this ChartPanel.
*/
private PlotPanel panel = null;
/**
* Constructor.
* @param chart JFreeChart The chart this panel holds.
* @param plotPanel PlotPanel The plotpanel that owns this ChartPanel.
*/
public FastChartPanel(JFreeChart chart, PlotPanel plotPanel) {
this(chart, false, plotPanel);
}
/**
* Constructor.
* @param chart JFreeChart The chart this panel holds.
* @param buffering boolean Wether to use buffering or not.
* @param plotPanel PlotPanel The plotpanel that owns this ChartPanel.
*/
public FastChartPanel(JFreeChart chart, boolean buffering,
PlotPanel plotPanel) {
super(chart, buffering, false);
panel = plotPanel;
}
/**
* Sets the ScrollMode flag.
* @param flag boolean the flag state.
*/
public void setScrollMode(final boolean flag) {
if (!XvsY) {
scrollMode = flag;
}
}
public void setAmount(final double x) {
amount = x;
}
/**
* Receives the new location of the Scroll-line, and updates the LRVPanel
* and the clock with the new information.
* @param x double the pixel location of the Scroll-line.
* @param dataArea Rectangle2D the rectangle that 'x' is located within.
*/
public void passOffX(double x, final Rectangle2D dataArea) {
double time = this.getChart().getXYPlot().getDomainAxis().
java2DToValue(x, dataArea, RectangleEdge.BOTTOM);
if (lastPosition == 0) {
lastPosition = time;
}
if (time < lastPosition) {
updateLRVS(time, SCROLLING_LEFT);
} else if (time > lastPosition) {
updateLRVS(time, SCROLLING_RIGHT);
} else {
updateLRVS(time, SCROLLING_NOT);
}
lastPosition = time;
double mjd = time / MSECS_PER_DAY + MJD_OFFSET;
Timetag tag = new Timetag(mjd);
clockString(tag.getDateTime());
}
/**
* Convenience method to create the string that is displayed in the 'clock'.
* Milliseconds are not displayed.
* @param tag short[] the Timetag to be displayed.
*/
public static void clockString(final short[] tag) {
if (tag != null) {
MainWindow.setClock(tag[0] + ":" + tag[1] + ":" + tag[2] + ":"
+ tag[3] + ":" + tag[4]);
} else {
MainWindow.setClock("");
}
}
/**
* Calls methods in PlotPanel that update the LRVPanel at the Scroll-line
* position.
* @param time double The actual time position of the Scroll-line in m-secs.
* @param direction int the direction the Scroll-line was dragged.
*/
private void updateLRVS(final double time, final int direction) {
panel.findIndexes(time, direction);
panel.updateScrollLRVS();
}
/**
* Directs a click on scroll arrow to either movePoint or moveLine.
* @param direction boolean the direction moved.
*/
public void move(final boolean direction) {
if (XvsY) {
movePoints(direction);
} else {
moveLine(direction);
}
}
/**
* In response to clicks on the scrolling arrows, it moves the scroll-point
* along the selected series.
* @param direction boolean the direction moved.
*/
private void movePoints(final boolean direction) {
//erase the old point
Graphics2D g2 = (Graphics2D) getGraphics();
g2.setPaint(java.awt.Color.white);
g2.setXORMode(java.awt.Color.black);
g2.fill(points[lastIndex]);
g2.draw(points[lastIndex]);
//increment the index in the appropriate direction and calculate the new
//point position. NOTE: True = RIGHT;
if (direction) {
if (someArrowsDisabled) {
MainWindow.enableLeftArrows(true);
someArrowsDisabled = false;
}
if ((indexes[lastIndex] + amount)
< scrollXYSeriesList[lastIndex].getSeries().getItemCount()) {
indexes[lastIndex] += amount;
} else {
indexes[lastIndex] = scrollXYSeriesList[lastIndex].getSeries().
getItemCount() - 1;
MainWindow.enableRightArrows(false);
someArrowsDisabled = true;
}
} else {
if (someArrowsDisabled) {
MainWindow.enableRightArrows(true);
someArrowsDisabled = false;
}
if ((indexes[lastIndex] - amount) > 0) {
indexes[lastIndex] -= amount;
} else {
indexes[lastIndex] = 0;
MainWindow.enableLeftArrows(false);
someArrowsDisabled = true;
}
}
Rectangle2D dataArea = getScreenDataArea();
Rectangle2D scrollDataArea = getScreenDataArea((int) dataArea.
getCenterX(), (int) dataArea.getMinY() + 5);
List list = ((FastCombinedDomainXYPlot) getChart().getXYPlot()).
getSubplots();
FastXYSeries series = scrollXYSeriesList[lastIndex].getSeries();
double X1 = getChart().getXYPlot().getDomainAxis().
valueToJava2D(series.getXValue(indexes[lastIndex]),
scrollDataArea, RectangleEdge.BOTTOM);
double Y1 = ((FastXYPlot) list.get(0)).getRangeAxis().valueToJava2D(
series.getYValue(indexes[lastIndex]), scrollDataArea,
RectangleEdge.LEFT);
points[lastIndex] = ShapeUtilities.createTranslatedShape(POINT, X1, Y1);
updateClockXvsY(true);
panel.updateXYScrollLRVS(scrollXYSeriesList, indexes);
//now draw the new points;
g2.fill(points[lastIndex]);
g2.draw(points[lastIndex]);
g2.setPaintMode();
}
/**
* Updates the clock in the case of a point-scrolling XvsY plot.
* @param flag boolean false if there are no points in the series.
*/
private void updateClockXvsY(final boolean flag) {
if (flag) {
double pointTime = ((Double) scrollXYSeriesList[lastIndex].
getTimeList().
get(indexes[lastIndex])).doubleValue();
pointTime = pointTime / MSECS_PER_DAY + MJD_OFFSET;
Timetag tag = new Timetag(pointTime);
clockString(tag.getDateTime());
} else {
MainWindow.setClock("NO POINTS IN SERIES");
MainWindow.enableAllArrows(false);
}
}
/**
* Moves the Scroll-line in response to clicks on the 'arrow buttons'.
* @param direction boolean The direction the line is moved. True = Right.
*/
private void moveLine(final boolean direction) {
//erase the old line
Graphics2D g2 = (Graphics2D) getGraphics();
g2.setPaint(java.awt.Color.white);
g2.setXORMode(java.awt.Color.gray);
g2.draw(scrollLine);
//calculate new position by incrementing the time the Scroll-line
//is indicating by AMOUNT.
Rectangle2D dataArea = this.getScreenDataArea();
scrollPoint = new Point2D.Double(dataArea.getCenterX(),
dataArea.getMinY() + 5);
Rectangle2D scaledDataArea = this.getScreenDataArea(
(int) scrollPoint.getX(), (int) scrollPoint.getY());
//Note: direction == true means the RIGHT button has been pushed.
// otherwise the LEFT has been pushed.
double time = lastPosition;
if (direction) {
time += amount;
} else {
time -= amount;
}
time = getChart().getXYPlot().getDomainAxis().valueToJava2D(
time, scaledDataArea, RectangleEdge.BOTTOM);
if (time <= scaledDataArea.getMinX()) {
scrollLine.setLine(scaledDataArea.getMinX() + 1,
dataArea.getMinY(),
scaledDataArea.getMinX() + 1,
dataArea.getMaxY());
} else if (time >= scaledDataArea.getBounds().getMaxX()) {
scrollLine.setLine(scaledDataArea.getMaxX() - 1,
dataArea.getMinY(),
scaledDataArea.getMaxX() - 1,
dataArea.getMaxY());
} else {
scrollLine.setLine(time, dataArea.getMinY(), time,
dataArea.getMaxY());
}
//Draw the new line.
g2.draw(scrollLine);
passOffX(scrollLine.getX1(), scaledDataArea);
g2.setPaintMode();
}
/**
* Called when Scroll-line mode is exited. Resets and removes the line
* and the clock.
*/
public void eraseOldLine() {
if (XvsY) {
if (points != null) {
int index = MainWindow.getComboBoxIndex();
MainWindow.getComboBox().removeActionListener(this);
MainWindow.removeComboBoxArray();
Graphics2D g2 = (Graphics2D) getGraphics();
if ((points[index] != null) && (g2 != null)) {
g2.setPaint(java.awt.Color.white);
g2.setXORMode(java.awt.Color.black);
g2.fill(points[index]);
g2.draw(points[index]);
g2.setPaintMode();
}
points = null;
scrollXYSeriesList = null;
indexes = null;
XvsY = false;
}
} else {
if (scrollLine != null) {
Graphics2D g2 = (Graphics2D) getGraphics();
g2.setPaint(java.awt.Color.white);
g2.setXORMode(java.awt.Color.gray);
g2.draw(scrollLine);
g2.setPaintMode();
scrollLine = null;
}
}
clockString(null);
}
/**
* Returns the Scroll-line.
* @return Shape the line returned.
*/
public Shape getScrollLine() {
return scrollLine;
}
/**
* Returns the last Position of the Scroll-line.
* @return double the last Position.
*/
public double getLastPosition() {
return lastPosition;
}
/**
* Sets the Scroll-line with passed in one.
* @param line Line2D the line passed in.
*/
public void setScrollLine(final Line2D line) {
scrollLine = line;
}
// public void findXYIndexes(double time, int direction) {
// //if the scrollLine hasnt moved, do nothing. Included in case
// //something SHOULD be done in the future.
// if (direction == FastChartPanel.SCROLLING_NOT) {
// return;
// }
//
// //loop through the series in the plot
// for (int i = 0; i < scrollXYSeriesList.length; i++) {
// LinkedList list = scrollXYSeriesList[i].getTimeList();
// //if searching to the right find the first point that is
// //greater than the time asked for, and report the value of the
// //one before it.
// if (direction == FastChartPanel.SCROLLING_RIGHT) {
// if (indexes[i] != list.size() - 1) {
// int count = indexes[i];
// for (ListIterator iterator = list.listIterator(indexes[i]);
// iterator.hasNext(); ) {
// double listTime = ((Double) iterator.next()).
// doubleValue();
// if (listTime > time) {
// indexes[i] = count - 1;
// break;
// }
// count++;
// }
// if (indexes[i] <= 0) {
// indexes[i] = 0;
// } else if (indexes[i]
// >= scrollXYSeriesList[i].getSeries().
// getItemCount()) {
// indexes[i] = scrollXYSeriesList[i].getSeries().
// getItemCount()
// - 1;
// }
// }
// //if searching to the left, find the first point that is
// //equal to or less than the time asked for, and report
// //the value of that.
// } else if (direction == FastChartPanel.SCROLLING_LEFT) {
// if (indexes[i] != 0) {
// int count = indexes[i];
// for (ListIterator iterator = list.listIterator(indexes[i]);
// iterator.hasPrevious(); ) {
// double listTime = ((Double) iterator.previous()).
// doubleValue();
// if (listTime <= time) {
// indexes[i] = count;
// break;
// }
// count--;
// }
// if (indexes[i] <= 0) {
// indexes[i] = 0;
// } else if (indexes[i]
// >= scrollXYSeriesList[i].getSeries().
// getItemCount()) {
// indexes[i] = scrollXYSeriesList[i].getSeries().
// getItemCount()
// - 1;
// }
// }
// }
// }
//
// double mjd = time / MSECS_PER_DAY + MJD_OFFSET;
// Timetag tag = new Timetag(mjd);
// clockString(tag.getDateTime());
//
// }
/**
* Initialises the position and the attendant parts to the Scroll-line.
*/
public void establishInitialPositions() {
//Initialise the intial parts, and calculate a starting
//Scroll-line position.
if (!panel.getPageAttributes().getPlot(0).getPlotType().hasTimeAxis()) {
//fill the combobox and enable it
String[] comboBox = new String[panel.getPageAttributes().getPlot(0).
childCount()];
for (int i = 0; i < comboBox.length; i++) {
comboBox[i] = panel.getPageAttributes().getPlot(0).getChannel(i).
getName();
}
MainWindow.assignComboBoxArray(comboBox);
MainWindow.getComboBox().addActionListener(this);
lastIndex = MainWindow.getComboBoxIndex();
//initialise, and find initial index
//locations relevant to a time halfway between the global
//first and last times over all the XYSeriesInfos.
XvsY = true;
scrollXYSeriesList = panel.initializeXvsYPoints();
indexes = new int[scrollXYSeriesList.length];
for (int i = 0; i < scrollXYSeriesList.length; i++) {
if (scrollXYSeriesList[i].getSeries().getItemCount() > 0) {
indexes[i] = scrollXYSeriesList[i].getSeries().getItemCount()
/ 2;
} else {
indexes[i] = Integer.MIN_VALUE;
}
}
panel.updateXYScrollLRVS(scrollXYSeriesList, indexes);
//now that a time has been found draw some shapes over the points
//that best fit that time.
points = new Shape[scrollXYSeriesList.length];
Rectangle2D dataArea = getScreenDataArea();
Rectangle2D scrollDataArea = getScreenDataArea((int) dataArea.
getCenterX(), (int) dataArea.getMinY() + 5);
List list = ((FastCombinedDomainXYPlot) getChart().getXYPlot()).
getSubplots();
for (int i = 0; i < indexes.length; i++) {
if (indexes[i] != Integer.MIN_VALUE) {
FastXYSeries series = scrollXYSeriesList[i].getSeries();
double X1 = getChart().getXYPlot().getDomainAxis().
valueToJava2D(series.getXValue(indexes[i]),
scrollDataArea,
RectangleEdge.BOTTOM);
double Y1 = ((FastXYPlot) list.get(0)).getRangeAxis().
valueToJava2D(
series.getYValue(indexes[i]),
scrollDataArea,
RectangleEdge.LEFT);
points[i] = ShapeUtilities.createTranslatedShape(POINT, X1,
Y1);
} else {
points[i] = null;
}
}
if (points[lastIndex] != null) {
updateClockXvsY(true);
Graphics2D g2 = (Graphics2D) getGraphics();
g2.setPaint(java.awt.Color.white);
g2.setXORMode(java.awt.Color.black);
g2.fill(points[lastIndex]);
g2.draw(points[lastIndex]);
g2.setPaintMode();
} else {
updateClockXvsY(false);
MainWindow.enableAllArrows(false);
}
} else {
scrollLine = new Line2D.Double();
Rectangle2D dataArea = getScreenDataArea();
scrollPoint = new Point2D.Double(dataArea.getCenterX(),
(dataArea.getMinY() + 5));
scrollLine.setLine(dataArea.getCenterX(), dataArea.getMinY(),
dataArea.getCenterX(), dataArea.getMaxY());
Rectangle2D scrollDataArea = getScreenDataArea((int) scrollPoint.
getX(), (int) scrollPoint.getY());
double time = getChart().getXYPlot().getDomainAxis().java2DToValue(
scrollPoint.getX(),
scrollDataArea, RectangleEdge.BOTTOM);
panel.initalizeScrollLine(time);
lastPosition = time;
passOffX(dataArea.getCenterX(), dataArea);
//now that datastrutures are initialised, actually draw a line,
//indicating we're ready to go.
Graphics2D g2 = (Graphics2D) getGraphics();
g2.setPaint(java.awt.Color.white);
g2.setXORMode(java.awt.Color.gray);
g2.draw(scrollLine);
g2.setPaintMode();
}
}
/**
* Handles a 'mouse pressed' event.
* <P>
* This event is the popup trigger on Unix/Linux. For Windows, the popup
* trigger is the 'mouse released' event.
*
* @param e The mouse event.
*/
public void mousePressed(MouseEvent e) {
//--------------------------Added to original ChartPanel mousePressed.----------
if (scrollMode) {
//check if the mouse was pressed within 5 pixels of the scrollLine
//horizontally, and simply within the upper and lower bounds of the
//dataArea vertically.
if (e.getX() >= scrollLine.getX1() - 2
&& e.getX() <= scrollLine.getX1() + 2
&& e.getY() >= scrollLine.getY1()
&& e.getY() <= scrollLine.getY2()) {
scrollPoint = new Point2D.Double(scrollLine.getX1(),
scrollLine.getY1() + 5);
isDragging = true;
}
} else {
//----------------------------------------------------------------------
if (this.zoomRectangle == null) {
Rectangle2D screenDataArea = getScreenDataArea(e.getX(), e.getY());
if (screenDataArea != null) {
this.zoomPoint = getPointInRectangle(
e.getX(), e.getY(), screenDataArea
);
} else {
this.zoomPoint = null;
}
if (e.isPopupTrigger()) {
if (this.popup != null) {
displayPopupMenu(e.getX(), e.getY());
}
}
}
}
}
/**
* Receives notification of mouse clicks on the panel. These are
* translated and passed on to any registered chart mouse click listeners.
*
* @param event Information about the mouse event.
*/
public void mouseClicked(MouseEvent event) {
Insets insets = getInsets();
int x = (int) ((event.getX() - insets.left) / this.scaleX);
int y = (int) ((event.getY() - insets.top) / this.scaleY);
this.anchor = new java.awt.geom.Point2D.Double(x, y);
//----------------Only change in mouseClicked. Don't let it fire notifications
// this.chart.setNotify(true); // force a redraw
//---------------------------------------------------------------------------
// new entity code...
Object[] listeners = this.chartMouseListeners.getListeners(
ChartMouseListener.class);
if (listeners.length == 0) {
return;
}
ChartEntity entity = null;
if (this.info != null) {
EntityCollection entities = this.info.getEntityCollection();
if (entities != null) {
entity = entities.getEntity(x, y);
}
}
ChartMouseEvent chartEvent = new ChartMouseEvent(getChart(), event,
entity);
for (int i = listeners.length - 1; i >= 0; i -= 1) {
((ChartMouseListener) listeners[i]).chartMouseClicked(
chartEvent);
}
}
/**
* Handles a 'mouse dragged' event.
*
* @param e the mouse event.
*/
public void mouseDragged(MouseEvent e) {
//--------------Added from original ChartPanel mouseDragged.----------------
//If the mouse is dragged and the isDragging flag is up
// this means the user has 'picked-up' the Scroll-line.
if (isDragging) {
//erase the old line
Graphics2D g2 = (Graphics2D) getGraphics();
g2.setPaint(java.awt.Color.white);
g2.setXORMode(java.awt.Color.gray);
g2.draw(scrollLine);
//calculate where the new line should be
Rectangle2D dataArea = this.getScreenDataArea();
Rectangle2D scaledDataArea = this.getScreenDataArea(
(int) scrollPoint.getX(), (int) scrollPoint.getY());
if (e.getX() <= scaledDataArea.getMinX()) {
scrollLine.setLine(scaledDataArea.getMinX() + 1,
dataArea.getMinY(),
scaledDataArea.getMinX() + 1,
dataArea.getMaxY());
} else if (e.getX() >= scaledDataArea.getMaxX()) {
scrollLine.setLine(scaledDataArea.getMaxX() - 1,
dataArea.getMinY(),
scaledDataArea.getMaxX() - 1,
dataArea.getMaxY());
} else {
scrollLine.setLine(e.getX(), dataArea.getMinY(), e.getX(),
dataArea.getMaxY());
}
//and draw it again.
g2.draw(scrollLine);
passOffX(scrollLine.getX1(), scaledDataArea);
g2.setPaintMode();
} else {
//---------------------------------------------------------------------------
// if the popup menu has already been triggered, then ignore dragging...
if (this.popup != null && this.popup.isShowing()) {
return;
}
// if no initial zoom point was set, ignore dragging...
if (this.zoomPoint == null) {
return;
}
Graphics2D g2 = (Graphics2D) getGraphics();
// use XOR to erase the previous zoom rectangle (if any)...
g2.setXORMode(java.awt.Color.gray);
if (this.zoomRectangle != null) {
if (this.fillZoomRectangle) {
g2.fill(this.zoomRectangle);
} else {
g2.draw(this.zoomRectangle);
}
}
boolean hZoom = false;
boolean vZoom = false;
if (this.orientation == PlotOrientation.HORIZONTAL) {
hZoom = this.rangeZoomable;
vZoom = this.domainZoomable;
} else {
hZoom = this.domainZoomable;
vZoom = this.rangeZoomable;
}
Rectangle2D scaledDataArea = getScreenDataArea(
(int)this.zoomPoint.getX(), (int)this.zoomPoint.getY()
);
if (hZoom && vZoom) {
// selected rectangle shouldn't extend outside the data area...
double xmax = Math.min(e.getX(), scaledDataArea.getMaxX());
double ymax = Math.min(e.getY(), scaledDataArea.getMaxY());
this.zoomRectangle = new Rectangle2D.Double(
this.zoomPoint.getX(), this.zoomPoint.getY(),
xmax - this.zoomPoint.getX(),
ymax - this.zoomPoint.getY()
);
} else if (hZoom) {
double xmax = Math.min(e.getX(), scaledDataArea.getMaxX());
this.zoomRectangle = new Rectangle2D.Double(
this.zoomPoint.getX(), scaledDataArea.getMinY(),
xmax - this.zoomPoint.getX(), scaledDataArea.getHeight()
);
} else if (vZoom) {
double ymax = Math.min(e.getY(), scaledDataArea.getMaxY());
this.zoomRectangle = new Rectangle2D.Double(
scaledDataArea.getMinX(), this.zoomPoint.getY(),
scaledDataArea.getWidth(), ymax - this.zoomPoint.getY()
);
}
if (this.zoomRectangle != null) {
// use XOR to draw the new zoom rectangle...
if (this.fillZoomRectangle) {
g2.fill(this.zoomRectangle);
} else {
g2.draw(this.zoomRectangle);
}
}
g2.dispose();
}
}
/**
* Handles a 'mouse released' event. On Windows, we need to check if this
* is a popup trigger, but only if we haven't already been tracking a zoom
* rectangle.
*
* @param e information about the event.
*/
public void mouseReleased(MouseEvent e) {
//--------------------Added to original ChartPanel mouseReleased------------
if (isDragging) {
scrollPoint = null;
isDragging = false;
}
//-------------------------------------------------------------------------
if (this.zoomRectangle != null) {
boolean hZoom = false;
boolean vZoom = false;
if (this.orientation == PlotOrientation.HORIZONTAL) {
hZoom = this.rangeZoomable;
vZoom = this.domainZoomable;
} else {
hZoom = this.domainZoomable;
vZoom = this.rangeZoomable;
}
boolean zoomTrigger1 = hZoom && Math.abs(e.getX()
- this.zoomPoint.getX()) >= this.zoomTriggerDistance;
boolean zoomTrigger2 = vZoom && Math.abs(e.getY()
- this.zoomPoint.getY()) >= this.zoomTriggerDistance;
if (zoomTrigger1 || zoomTrigger2) {
if ((hZoom && (e.getX() < this.zoomPoint.getX()))
|| (vZoom && (e.getY() < this.zoomPoint.getY()))) {
restoreAutoBounds();
} else {
double x, y, w, h;
Rectangle2D screenDataArea = getScreenDataArea(
(int)this.zoomPoint.getX(),
(int)this.zoomPoint.getY()
);
// for mouseReleased event, (horizontalZoom || verticalZoom)
// will be true, so we can just test for either being false;
// otherwise both are true
if (!vZoom) {
x = this.zoomPoint.getX();
y = screenDataArea.getMinY();
w = Math.min(
this.zoomRectangle.getWidth(),
screenDataArea.getMaxX()
- this.zoomPoint.getX()
);
h = screenDataArea.getHeight();
} else if (!hZoom) {
x = screenDataArea.getMinX();
y = this.zoomPoint.getY();
w = screenDataArea.getWidth();
h = Math.min(
this.zoomRectangle.getHeight(),
screenDataArea.getMaxY()
- this.zoomPoint.getY()
);
} else {
x = this.zoomPoint.getX();
y = this.zoomPoint.getY();
w = Math.min(
this.zoomRectangle.getWidth(),
screenDataArea.getMaxX()
- this.zoomPoint.getX()
);
h = Math.min(
this.zoomRectangle.getHeight(),
screenDataArea.getMaxY()
- this.zoomPoint.getY()
);
}
Rectangle2D zoomArea = new Rectangle2D.Double(x, y, w,
h);
zoom(zoomArea);
}
this.zoomPoint = null;
this.zoomRectangle = null;
} else {
Graphics2D g2 = (Graphics2D) getGraphics();
g2.setXORMode(java.awt.Color.gray);
if (this.fillZoomRectangle) {
g2.fill(this.zoomRectangle);
} else {
g2.draw(this.zoomRectangle);
}
g2.dispose();
this.zoomPoint = null;
this.zoomRectangle = null;
}
} else if (e.isPopupTrigger()) {
if (this.popup != null) {
displayPopupMenu(e.getX(), e.getY());
}
}
}
/**
* Listens to selections on the MainWindow seriesBox. On changes in
* selection it moves the point-scrolling from one XYSeriesInfo to another.
* Because we extend ChartPanel, the first line is a call to the ChartPanel
* actionListener.
* @param e ActionEvent the event recieved.
*/
public void actionPerformed(ActionEvent e) {
super.actionPerformed(e);
if (e.getActionCommand().equals("comboBoxChanged")) {
Graphics2D g2 = (Graphics2D) getGraphics();
g2.setPaint(java.awt.Color.white);
g2.setXORMode(java.awt.Color.black);
if (points[lastIndex] != null) {
g2.fill(points[lastIndex]);
g2.draw(points[lastIndex]);
}
lastIndex = MainWindow.getComboBoxIndex();
Rectangle2D dataArea = getScreenDataArea();
Rectangle2D scrollDataArea = getScreenDataArea((int) dataArea.
getCenterX(), (int) dataArea.getMinY() + 5);
List list = ((FastCombinedDomainXYPlot) getChart().getXYPlot()).
getSubplots();
FastXYSeries series = scrollXYSeriesList[lastIndex].getSeries();
if (series.getItemCount() != 0) {
double X1 = getChart().getXYPlot().getDomainAxis().
valueToJava2D(series.getXValue(indexes[lastIndex]),
scrollDataArea, RectangleEdge.BOTTOM);
double Y1 = ((FastXYPlot) list.get(0)).getRangeAxis().
valueToJava2D(
series.getYValue(indexes[lastIndex]),
scrollDataArea,
RectangleEdge.LEFT);
points[lastIndex] = ShapeUtilities.createTranslatedShape(POINT,
X1,
Y1);
updateClockXvsY(true);
MainWindow.enableAllArrows(true);
//now draw the new points;
g2.fill(points[lastIndex]);
g2.draw(points[lastIndex]);
} else {
points[lastIndex] = null;
updateClockXvsY(false);
MainWindow.enableAllArrows(false);
}
g2.setPaintMode();
}
}
/**
* Paints the component by drawing the chart to fill the entire component,
* but allowing for the insets (which will be non-zero if a border has been
* set for this component). To increase performance (at the expense of
* memory), an off-screen buffer image can be used.
* In the off-screen buffer image is used, no resize, and the
* <code>drawAppended</code> flag is set, then the <code>drawAppendedData</code>
* method is called in {@link JFreeChart}
* In the redrawing the chart each time and the <code>drawAppended</code>
* flag is set, then the <code>drawAppendedData</code> method is called in
* {@link JFreeChart}
*
* @param g the graphics device for drawing on.
*/
/**
* Note: The functionality of drawAppended has been added to this method.
* Depending on the status of the drawAppended flag, one of two routes
* ending at the plot's renderer is chosen. If the flag is false, then the
* traditional route is followed which repaints the entire contents of the
* plot. If the flag is true, only the last undrawn entries in the given
* series are drawn, greatly improving speed/efficiency.
*
*/
public void paintComponent(Graphics g) {
//these three lines replace super.paintComponent(g);
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(getForeground());
if (this.chart == null) {
return;
}
Graphics2D g2 = (Graphics2D) g.create();
// first determine the size of the chart rendering area...
Dimension size = getSize();
Insets insets = getInsets();
Rectangle2D available = new Rectangle2D.Double(
insets.left, insets.top,
size.getWidth() - insets.left - insets.right,
size.getHeight() - insets.top - insets.bottom
);
// work out if scaling is required...
boolean scale = false;
double drawWidth = available.getWidth();
double drawHeight = available.getHeight();
this.scaleX = 1.0;
this.scaleY = 1.0;
if (drawWidth < this.minimumDrawWidth) {
this.scaleX = drawWidth / this.minimumDrawWidth;
drawWidth = this.minimumDrawWidth;
scale = true;
} else if (drawWidth > this.maximumDrawWidth) {
this.scaleX = drawWidth / this.maximumDrawWidth;
drawWidth = this.maximumDrawWidth;
scale = true;
}
if (drawHeight < this.minimumDrawHeight) {
this.scaleY = drawHeight / this.minimumDrawHeight;
drawHeight = this.minimumDrawHeight;
scale = true;
} else if (drawHeight > this.maximumDrawHeight) {
this.scaleY = drawHeight / this.maximumDrawHeight;
drawHeight = this.maximumDrawHeight;
scale = true;
}
Rectangle2D chartArea = new Rectangle2D.Double(
0.0, 0.0, drawWidth, drawHeight
);
// are we using the chart buffer?
if (this.useBuffer) {
// do we need to resize the buffer?
////////////////// Added to support appended draw:Start ////////////////////////
boolean resize = (this.chartBufferWidth != available.getWidth())
|| (this.chartBufferHeight != available.getHeight());
////////////////// Added to support appended draw:End ////////////////////////
if ((this.chartBuffer == null || resize)
|| (this.chartBufferWidth != available.getWidth())
|| (this.chartBufferHeight != available.getHeight())
) {
this.chartBufferWidth = (int) available.getWidth();
this.chartBufferHeight = (int) available.getHeight();
this.chartBuffer = createImage(
this.chartBufferWidth, this.chartBufferHeight
);
this.refreshBuffer = true;
}
// do we need to redraw the buffer?
if (this.refreshBuffer) {
Rectangle2D bufferArea = new Rectangle2D.Double(
0, 0, this.chartBufferWidth, this.chartBufferHeight
);
Graphics2D bufferG2
= (Graphics2D)this.chartBuffer.getGraphics();
AffineTransform saved = bufferG2.getTransform();
////////////////// Added/Modified to support appended draw:Start ////////////////////////
this.info.setInitTranslation(saved); //@todo Ask Jamie about this.
if (scale) {
AffineTransform st = AffineTransform.getScaleInstance(
this.scaleX, this.scaleY
);
bufferG2.transform(st);
}
// Start of the minimal draw route. Triggered by flag being
// set to true;
if (dataAppended && !resize) {
this.chart.drawAppendedData(bufferG2, chartArea,
this.anchor, this.info);
dataAppended = false;
} else {
// Start of the full draw route. Triggered by flag being set
// to false;
this.chart.draw(bufferG2, chartArea, this.anchor,
this.info);
}
if (scale) {
bufferG2.setTransform(saved);
}
this.refreshBuffer = false;
}
//----------Added to support the Scroll-Line. NOT NEEDED for AppendedDraw------
//On a repaint, redraw the Scroll-line in the position indicated
//by the time it represents.
//Get it into the chartBuffer before it gets 'zapped'
//----------------------------------------------------------------------------
// zap the buffer onto the panel...
g2.drawImage(this.chartBuffer, insets.left, insets.right, this);
if (scrollLine != null) {
Rectangle2D dataArea = getScreenDataArea();
Rectangle2D scaledDataArea = getScreenDataArea((int)
dataArea.
getCenterX(), (int) dataArea.getMinY() + 5);
double time = lastPosition;
time = getChart().getXYPlot().getDomainAxis().valueToJava2D(
time, scaledDataArea, RectangleEdge.BOTTOM);
time = Math.rint(time);
if (time >= scaledDataArea.getMaxX() - 1) {
time = Math.rint(scaledDataArea.getMaxX() - 2);
lastPosition = getChart().getXYPlot().getDomainAxis().
java2DToValue(
time, scaledDataArea,
RectangleEdge.BOTTOM);
} else if (time <= scaledDataArea.getMinX() + 2) {
time = Math.rint(scaledDataArea.getMinX() + 1);
lastPosition = getChart().getXYPlot().getDomainAxis().
java2DToValue(
time, scaledDataArea,
RectangleEdge.BOTTOM);
}
scrollLine.setLine(time, dataArea.getMinY(), time,
dataArea.getMaxY());
g2.setPaint(java.awt.Color.white);
g2.setXORMode(java.awt.Color.gray);
g2.draw(scrollLine);
g2.setPaintMode();
passOffX(time, dataArea);
} else if (points != null) {
Rectangle2D dataArea = getScreenDataArea();
Rectangle2D scrollDataArea = getScreenDataArea((int)
dataArea.
getCenterX(), (int) dataArea.getMinY() + 5);
List list = ((FastCombinedDomainXYPlot) getChart().
getXYPlot()).
getSubplots();
for (int i = 0; i < scrollXYSeriesList.length; i++) {
FastXYSeries series = scrollXYSeriesList[i].getSeries();
if (series.getItemCount() != 0) {
double X1 = getChart().getXYPlot().getDomainAxis().
valueToJava2D(series.getXValue(indexes[i]),
scrollDataArea,
RectangleEdge.BOTTOM);
double Y1 = ((FastXYPlot) list.get(0)).getRangeAxis().
valueToJava2D(
series.getYValue(indexes[i]),
scrollDataArea,
RectangleEdge.LEFT);
points[i] = ShapeUtilities.createTranslatedShape(POINT,
X1, Y1);
} else {
points[i] = null;
}
}
if (points[lastIndex] != null) {
g2.setPaint(java.awt.Color.white);
g2.setXORMode(java.awt.Color.black);
g2.fill(points[lastIndex]);
g2.draw(points[lastIndex]);
g2.setPaintMode();
} else {
updateClockXvsY(false);
}
}
// or redrawing the chart every time...
} else {
AffineTransform saved = g2.getTransform();
this.info.setInitTranslation(saved);
g2.translate(insets.left, insets.top);
if (scale) {
AffineTransform st = AffineTransform.getScaleInstance(
this.scaleX, this.scaleY
);
g2.transform(st);
}
// Start of the minimal draw route. Triggered by flag being
// set to true;
// NOTE: not used by our application, so not sure if this works.
if (dataAppended) {
this.chart.drawAppendedData(g2, chartArea, this.anchor,
this.info);
dataAppended = false;
}
// Start of the full draw route. Triggered by flag being set
// to false;
else {
this.chart.draw(g2, chartArea, this.anchor, this.info);
}
g2.setTransform(saved);
}
this.anchor = null;
this.verticalTraceLine = null;
this.horizontalTraceLine = null;
////////////////// Added/Modified to support appended draw:End ////////////////////////
}
}