| BarChartPanel.java |
/*
* Copyright 2002 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN
* OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR
* FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
* PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
* LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE SOFTWARE,
* EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that Software is not designed, licensed or intended
* for use in the design, construction, operation or maintenance of
* any nuclear facility.
*/
package com.sun.j2ee.blueprints.admin.client;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.swing.*;
import javax.swing.border.SoftBevelBorder;
/**
* Panel that displays a bar chart defined by the given data model.
* TODO: Refactor panel code to support both chart types.
*
* @author Joshua Outwater
*/
public class BarChartPanel extends JPanel implements PropertyChangeListener {
private BarChart barChart;
private DataSource.BarChartModel barChartModel;
private JTextField startDateTxtField;
private JTextField endDateTxtField;
private DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
private JButton submitButton;
public BarChartPanel(DataSource.BarChartModel barChartModel) {
this.barChartModel = barChartModel;
createUI();
}
private void createUI() {
setLayout(new BorderLayout());
setBorder(new SoftBevelBorder(SoftBevelBorder.LOWERED));
JPanel panel = new JPanel(new FlowLayout());
panel.add(new JLabel(PetStoreAdminClient.getString(
"BarChart.description") + " "
+ PetStoreAdminClient.getString("Chart.from")));
startDateTxtField = new JTextField(dateFormat.format(
barChartModel.getStartDate()));
panel.add(startDateTxtField);
panel.add(new JLabel(PetStoreAdminClient.getString("Chart.to")));
endDateTxtField = new JTextField(dateFormat.format(
barChartModel.getEndDate()));
panel.add(endDateTxtField);
add(panel, BorderLayout.NORTH);
submitButton =
new JButton(PetStoreAdminClient.getString("SubmitButton.text"));
panel.add(submitButton);
submitButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
updateModelDates();
}
});
barChart = new BarChart(barChartModel);
add(barChart, BorderLayout.CENTER);
}
private void updateModelDates() {
Date startDate = null;
Date endDate = null;
// Make sure that the dates entered in the text field are valid. If
// they are not prompt the user to fix them.
try {
startDate =
dateFormat.parse(startDateTxtField.getText());
endDate =
dateFormat.parse(endDateTxtField.getText());
} catch (ParseException e) {
JOptionPane.showMessageDialog(this,
PetStoreAdminClient.getString("DateFormatErrorDialog.message"),
PetStoreAdminClient.getString("DateFormatErrorDialog.title"),
JOptionPane.ERROR_MESSAGE);
return;
}
// Setting the dates will automatically retrieve the data from the
// server.
barChartModel.setDates(startDate, endDate);
}
/**
* Catch property changes for the bar chart data model and force a
* repaint.
*/
public void propertyChange(PropertyChangeEvent e) {
String property = e.getPropertyName();
if (DataSource.DISABLE_ACTIONS.equals(property)) {
submitButton.setEnabled(false);
} else if (DataSource.ENABLE_ACTIONS.equals(property)) {
submitButton.setEnabled(true);
} else if (DataSource.BAR_CHART_DATA_CHANGED.equals(property)) {
barChart.repaint();
}
}
/**
* Displays a bar chart of the toal sales (in #s) divided into sales (in #s)
* per category in a given period of days.
*/
public class BarChart extends Chart {
/** # of pixels to indent for the left and right of the bar chart. */
private int INSET_LEFT;
/** # of pixels to indent for the top and bottom of the bar chart. */
private int INSET_TOP;
/** # of tics to display on the y-axis for the range of values. */
private final int NUM_TICS =
PetStoreAdminClient.getInteger("BarChart.numTics");
private final int TIC_LEN =
PetStoreAdminClient.getInteger("BarChart.ticLen");
private final int EMPTY_SPACE =
PetStoreAdminClient.getInteger("BarChart.emptySpace");
private float maxVal = 0f;
private float barWidth = 0f;
private float valInterval = 0f;
private JToolTip toolTip = new JToolTip();
private Rectangle[] bars;
private String[] keys;
/**
* Creates a new bar chart defined by the model passed in.
*
* @param barChartModel The model to retrieve data from.
* @param width Width of the chart in pixels.
* @param height Height of the chart in pixels.
*/
public BarChart(DataSource.BarChartModel barChartModel) {
super(barChartModel);
}
/**
* Calculates all the constants needed to draw the bar chart.
*/
private void determineChartValues(Graphics2D g2) {
// Already checked for null in caller renderChart.
maxVal = 0f;
for (int i = 0; i < keys.length; i++) {
if (chartModel.getOrders(keys[i]) > maxVal) {
maxVal = chartModel.getOrders(keys[i]);
}
}
valInterval = maxVal / (NUM_TICS - 1);
float val = 0f;
FontMetrics fm = g2.getFontMetrics();
// Find the largest label on the y-axis so we can determine the
// amount to indent the left and right sides.
int maxStrLen = 0;
int currStrLen = 0;
for (int i = 0; i < keys.length; i++) {
currStrLen = fm.stringWidth(Float.toString(val));
if (currStrLen > maxStrLen) {
maxStrLen = currStrLen;
}
val += valInterval;
}
// Find the max height of the font being used to determine the
// amount to indent the top and bottom of the chart.
int maxStrHeight = fm.getHeight();
INSET_LEFT = maxStrLen + (2 * EMPTY_SPACE) + TIC_LEN;
INSET_TOP = maxStrHeight + (2 * EMPTY_SPACE) + TIC_LEN;
barWidth = ((float)getWidth() - 2 * INSET_LEFT)
/ (float)keys.length;
}
protected void drawAxes(Graphics2D g2) {
// Already checked for null in caller renderChart.
Color oldColor = g2.getColor();
int height = getHeight();
int width = getWidth();
float val = 0f;
FontMetrics fm = g2.getFontMetrics();
int maxStrHeight = fm.getHeight();
g2.setColor(Color.black);
// Draw y-axis.
g2.drawLine(INSET_LEFT, INSET_TOP, INSET_LEFT, height - INSET_TOP);
// Draw x-axis.
g2.drawLine(INSET_LEFT, height - INSET_TOP,
width - INSET_LEFT, height - INSET_TOP);
// Draw tics and labels on x-axis.
int strLen = 0;
int x = INSET_LEFT + (int)(barWidth / 2);
for (int i = 0; i < keys.length; i++) {
g2.drawLine(x, height - INSET_TOP,
x, height - INSET_TOP + TIC_LEN);
strLen = fm.stringWidth(keys[i]);
g2.drawString(keys[i], x - strLen / 2,
height - INSET_TOP + (fm.getMaxAscent()) + EMPTY_SPACE
+ TIC_LEN);
x += (int)barWidth;
}
//Draw tics and labels on y-axis.
int y = height - INSET_TOP;
x = INSET_LEFT;
int ticInterval = (height - (2 * INSET_TOP)) / (NUM_TICS - 1);
String str;
for (int i = 0; i < NUM_TICS - 1; i++) {
g2.drawLine(INSET_LEFT, y, INSET_LEFT - TIC_LEN, y);
strLen = fm.stringWidth(str = Float.toString(val));
g2.drawString(str, x - strLen - EMPTY_SPACE - TIC_LEN,
y + (maxStrHeight / 2));
y -= ticInterval;
val += valInterval;
}
// The last tic is drawn from the known axis point due to round-off
// errors in the ticInterval that may have accumulated. This
// doesn't look great... A fix for this is to spread the error by
// finding the center tic first and solve the rest by divide and
// conquer.
g2.drawLine(INSET_LEFT, INSET_TOP,
INSET_LEFT - TIC_LEN, INSET_TOP);
strLen = fm.stringWidth(str = Float.toString(val));
g2.drawString(str, x - strLen - EMPTY_SPACE - TIC_LEN,
y + (maxStrHeight / 2));
g2.setColor(oldColor);
}
public void renderChart(Graphics2D g2) {
keys = chartModel.getKeys();
if (keys == null) {
// TODO: Draw a message saying we have no data.
return;
}
calculateTotals();
bars = new Rectangle[keys.length];
determineChartValues(g2);
float x = INSET_LEFT + 1;
float y = getHeight() - INSET_TOP;
float maxHeight = getHeight() - (2 * INSET_TOP);
Color oldColor;
for (int i = 0; i < keys.length; i++) {
float barHeight = (chartModel.getOrders(keys[i]) * maxHeight)
/ maxVal;
oldColor = g2.getColor();
g2.setColor(colorList[i % keys.length]);
bars[i] = new Rectangle((int)x, (int)(maxHeight - barHeight
+ INSET_TOP), (int)barWidth, (int)barHeight);
g2.fill3DRect(bars[i].x, bars[i].y, bars[i].width,
bars[i].height, true);
x += barWidth;
g2.setColor(oldColor);
}
drawAxes(g2);
}
public JToolTip createToolTip() {
toolTip.setComponent(this);
return toolTip;
}
public boolean contains(int x, int y) {
boolean result = false;
if (bars == null) {
return result;
}
for (int i = 0; i < bars.length; i++) {
if (bars[i].contains(x, y)) {
setToolTipText(((int)chartModel.getOrders(keys[i]))
+ " orders");
result = true;
}
}
return result;
}
public void setToolTipText(String tip) {
toolTip.setTipText(tip);
super.setToolTipText(tip);
}
public Point getToolTipLocation(MouseEvent e) {
return new Point(e.getX(), e.getY() - toolTip.getHeight());
}
public String toString() {
return "Bar Chart";
}
}
}
| BarChartPanel.java |