import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
+
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import tim.prune.I18nManager;
import tim.prune.config.ColourScheme;
import tim.prune.config.Config;
+import tim.prune.data.Field;
+import tim.prune.data.FieldList;
import tim.prune.data.TrackInfo;
import tim.prune.gui.GenericDisplay;
*/
public class ProfileChart extends GenericDisplay implements MouseListener
{
+ /** Inner class to handle popup menu clicks */
+ class MenuClicker implements ActionListener
+ {
+ private Field _field = null;
+ MenuClicker(Field inField) {_field = inField;}
+ /** React to menu click by changing the field */
+ public void actionPerformed(ActionEvent arg0) {
+ changeView(_field);
+ }
+ }
+
/** Current scale factor in x direction*/
private double _xScaleFactor = 0.0;
/** Data to show on chart */
private JPopupMenu _popup = null;
/** Possible scales to use */
- private static final int[] LINE_SCALES = {10000, 5000, 2000, 1000, 500, 200, 100, 50, 10, 5};
+ private static final int[] LINE_SCALES = {10000, 5000, 2000, 1000, 500, 200, 100, 50, 10, 5, 2, 1};
/** Border width around black line */
private static final int BORDER_WIDTH = 6;
/** Minimum size for profile chart in pixels */
private static final Dimension MINIMUM_SIZE = new Dimension(200, 110);
/** Colour to use for text if no data found */
private static final Color COLOR_NODATA_TEXT = Color.GRAY;
- /** Chart type */
- private static enum ChartType {ALTITUDE, SPEED};
/**
_data = new AltitudeData(inTrackInfo.getTrack());
addMouseListener(this);
setLayout(new FlowLayout(FlowLayout.LEFT));
- _label = new JLabel("Altitude");
+ _label = new JLabel("Altitude"); // text will be replaced later
add(_label);
makePopup();
}
public void paint(Graphics g)
{
super.paint(g);
+ // Set antialiasing depending on Config
+ ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ Config.getConfigBoolean(Config.KEY_ANTIALIAS) ? RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
ColourScheme colourScheme = Config.getColourScheme();
paintBackground(g, colourScheme);
if (_track != null && _track.getNumPoints() > 0)
{
- _data.init();
_label.setText(_data.getLabel());
int width = getWidth();
int height = getHeight();
// Find minimum and maximum values to plot
double minValue = _data.getMinValue();
double maxValue = _data.getMaxValue();
- if (maxValue <= minValue) {maxValue = minValue + 1;}
+ if (maxValue <= minValue) {maxValue = minValue + 1; minValue--;}
final int numPoints = _track.getNumPoints();
_xScaleFactor = 1.0 * (width - 2 * BORDER_WIDTH - 1) / numPoints;
// horizontal lines for scale - set to round numbers eg 500
int lineScale = getLineScale(minValue, maxValue);
- int scaleValue = (int) (minValue/lineScale + 1) * lineScale;
+ double scaleValue = Math.ceil(minValue/lineScale) * lineScale;
int x = 0, y = 0;
+ final int zeroY = height - BORDER_WIDTH - (int) (yScaleFactor * (0.0 - minValue));
+
double value = 0.0;
- if (lineScale > 1)
+ g.setColor(lineColour);
+ if (lineScale >= 1)
{
- g.setColor(lineColour);
while (scaleValue < maxValue)
{
y = height - BORDER_WIDTH - (int) (yScaleFactor * (scaleValue - minValue));
scaleValue += lineScale;
}
}
+ else if (minValue < 0.0)
+ {
+ // just draw zero line
+ y = zeroY;
+ g.drawLine(BORDER_WIDTH + 1, y, width - BORDER_WIDTH - 1, y);
+ }
try
{
if (_data.hasData(p))
{
value = _data.getData(p);
- y = (int) (yScaleFactor * (value - minValue));
- g.fillRect(BORDER_WIDTH+x, height-BORDER_WIDTH - y, barWidth, y);
+ // Normal case is the minimum value greater than zero
+ if (minValue >= 0)
+ {
+ y = (int) (yScaleFactor * (value - minValue));
+ g.fillRect(BORDER_WIDTH+x, height-BORDER_WIDTH - y, barWidth, y);
+ }
+ else if (value >= 0.0) {
+ // Bar upwards from the zero line
+ y = height-BORDER_WIDTH - (int) (yScaleFactor * (value - minValue));
+ g.fillRect(BORDER_WIDTH+x, y, barWidth, zeroY - y);
+ }
+ else {
+ // Bar downwards from the zero line
+ int barHeight = (int) (yScaleFactor * value);
+ g.fillRect(BORDER_WIDTH+x, zeroY, barWidth, -barHeight);
+ }
}
}
// current point (make sure it's drawn last)
- if (_data.hasData(selectedPoint))
+ if (selectedPoint >= 0)
{
x = (int) (_xScaleFactor * selectedPoint) + 1;
g.setColor(secondColour);
g.fillRect(BORDER_WIDTH + x, height-usableHeight-BORDER_WIDTH+1, barWidth, usableHeight-2);
- g.setColor(currentColour);
- value = _data.getData(selectedPoint);
- y = (int) (yScaleFactor * (value - minValue));
- g.fillRect(BORDER_WIDTH + x, height-BORDER_WIDTH - y, barWidth, y);
+ if (_data.hasData(selectedPoint))
+ {
+ g.setColor(currentColour);
+ value = _data.getData(selectedPoint);
+ y = (int) (yScaleFactor * (value - minValue));
+ g.fillRect(BORDER_WIDTH + x, height-BORDER_WIDTH - y, barWidth, y);
+ }
}
}
catch (NullPointerException npe) { // ignore, probably due to data being changed
}
// Draw numbers on top of the graph to mark scale
- if (lineScale > 1)
+ if (lineScale >= 1)
{
int textHeight = g.getFontMetrics().getHeight();
scaleValue = (int) (minValue / lineScale + 1) * lineScale;
+ if (minValue < 0.0) {scaleValue -= lineScale;}
y = 0;
g.setColor(currentColour);
while (scaleValue < maxValue)
if (y < (BORDER_WIDTH + textHeight)) {
y = BORDER_WIDTH + textHeight;
}
- g.drawString(""+scaleValue, BORDER_WIDTH + 5, y);
+ g.drawString(""+(int)scaleValue, BORDER_WIDTH + 5, y);
scaleValue += lineScale;
}
}
/**
* Make the popup menu for right-clicking the chart
*/
- private void makePopup()
+ private synchronized void makePopup()
{
_popup = new JPopupMenu();
JMenuItem altItem = new JMenuItem(I18nManager.getText("fieldname.altitude"));
altItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
- changeView(ChartType.ALTITUDE);
+ changeView(Field.ALTITUDE);
}});
_popup.add(altItem);
JMenuItem speedItem = new JMenuItem(I18nManager.getText("fieldname.speed"));
speedItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
- changeView(ChartType.SPEED);
+ changeView(Field.SPEED);
}});
_popup.add(speedItem);
+ JMenuItem vertSpeedItem = new JMenuItem(I18nManager.getText("fieldname.verticalspeed"));
+ vertSpeedItem.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e)
+ {
+ changeView(Field.VERTICAL_SPEED);
+ }});
+ _popup.add(vertSpeedItem);
+ // Go through track's master field list, see if any other fields to list
+ boolean addSeparator = true;
+ FieldList fields = _track.getFieldList();
+ for (int i=0; i<fields.getNumFields(); i++)
+ {
+ Field field = fields.getField(i);
+ if (!field.isBuiltIn())
+ {
+ if (addSeparator) {_popup.addSeparator();}
+ addSeparator = false;
+ JMenuItem item = new JMenuItem(field.getName());
+ item.addActionListener(new MenuClicker(field));
+ _popup.add(item);
+ }
+ }
}
/**
*/
private int getLineScale(double inMin, double inMax)
{
- if ((inMax - inMin) < 5 || inMax < 0) {
+ if ((inMax - inMin) < 2.0) {
return -1;
}
int numScales = LINE_SCALES.length;
*/
public void dataUpdated(byte inUpdateType)
{
- _data.init();
+ // Try not to recalculate all the values unless necessary
+ if (inUpdateType != SELECTION_CHANGED) {
+ _data.init(Config.getUnitSet());
+ }
+ // Update the menu if necessary
+ if ((inUpdateType & DATA_ADDED_OR_REMOVED) > 0) {
+ makePopup();
+ }
repaint();
}
/**
* Called by clicking on popup menu to change the view
- * @param inType selected chart type
+ * @param inField field to show
*/
- private void changeView(ChartType inType)
+ private void changeView(Field inField)
{
- if (inType == ChartType.ALTITUDE && !(_data instanceof AltitudeData))
+ if (inField == Field.ALTITUDE)
{
- _data = new AltitudeData(_track);
+ if (!(_data instanceof AltitudeData)) {
+ _data = new AltitudeData(_track);
+ }
+ }
+ else if (inField == Field.SPEED) {
+ if (!(_data instanceof SpeedData)) {
+ _data = new SpeedData(_track);
+ }
}
- else if (inType == ChartType.SPEED && !(_data instanceof SpeedData)) {
- _data = new SpeedData(_track);
+ else if (inField == Field.VERTICAL_SPEED) {
+ if (!(_data instanceof VerticalSpeedData)) {
+ _data = new VerticalSpeedData(_track);
+ }
+ }
+ else
+ {
+ if (!(_data instanceof ArbitraryData) || ((ArbitraryData)_data).getField() != inField) {
+ _data = new ArbitraryData(_track, inField);
+ }
}
- _data.init();
+ _data.init(Config.getUnitSet());
repaint();
}