package timeflow.views; import timeflow.model.*; import timeflow.views.ListView.LinkIt; import timeflow.data.db.*; import timeflow.data.time.*; import javax.swing.*; import timeflow.util.*; import java.awt.*; import java.awt.event.*; import java.util.*; public class BarGraphView extends AbstractView { BarGraph graph = new BarGraph(); JPanel controls; ArrayList bars; enum Aggregate { TOTAL, AVERAGE, COUNT }; Aggregate agg; JComboBox splitFieldChoice, numFieldChoice; public BarGraphView(TFModel model) { super(model); setLayout(new BorderLayout()); controls = new JPanel(); add(controls, BorderLayout.NORTH); controls.setLayout(null); controls.setBackground(Color.white); JScrollPane scrollPane = new JScrollPane(graph); add(scrollPane, BorderLayout.CENTER); makeTop(); } protected JComponent _getControls() { return controls; } void makeTop() { int x = 10, y = 10; int ch = 25, pad = 5, cw = 160; controls.removeAll(); TFModel model = getModel(); if (model.getDB() == null || model.getDB().size() == 0) { JLabel empty = new JLabel("Empty database"); controls.add(empty); empty.setBounds(x, y, cw, ch); return; } JLabel top = new JLabel("For each value of"); controls.add(top); top.setBounds(x, y, cw, ch); y += ch + pad; splitFieldChoice = new JComboBox(); String splitSelection = null; for (Field f : DBUtils.categoryFields(model.getDB())) { splitFieldChoice.addItem(f.getName()); if (f == graph.splitField) { splitSelection = f.getName(); } } controls.add(splitFieldChoice); splitFieldChoice.setBounds(x, y, cw, ch); y += ch + 3 * pad; if (splitSelection != null) { splitFieldChoice.setSelectedItem(splitSelection); } else if (getModel().getColorField() != null) { splitFieldChoice.setSelectedItem(getModel().getColorField().getName()); } splitFieldChoice.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { graph.redo(); } }); JLabel showLabel = new JLabel("show"); controls.add(showLabel); showLabel.setBounds(x, y, cw, ch); y += ch + pad; numFieldChoice = new JComboBox(); numFieldChoice.addItem("Number of events"); final ArrayList valueFields = new ArrayList(); for (Field f : model.getDB().getFields(Double.class)) { numFieldChoice.addItem("Total: " + f.getName()); numFieldChoice.addItem("Average: " + f.getName()); valueFields.add(f); } controls.add(numFieldChoice); numFieldChoice.setBounds(x, y, cw, ch); boolean chosen = false; for (int i = 0; i < numFieldChoice.getItemCount(); i++) { if (numFieldChoice.getItemAt(i).equals(graph.lastValueMenuChoice)) { numFieldChoice.setSelectedIndex(i); chosen = true; } } if (!chosen) { Field size = getModel().getDB().getField(VirtualField.SIZE); if (size != null) { numFieldChoice.setSelectedItem("Total: " + size.getName()); } } numFieldChoice.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { graph.redo(); } }); revalidate(); repaint(); } void reset() { makeTop(); graph.redo(); revalidate(); repaint(); } @Override protected void _note(TFEvent e) { if (e.affectsSchema()) { reset(); } else { graph.redo(); } repaint(); } @Override public String getName() { return "Bar Graph"; } @Override protected void onscreen(boolean majorChange) { reset(); } class BarData { Object thing; double num; BarData(Object thing, double num) { this.thing = thing; this.num = num; } } class BarGraph extends JPanel { int numVals = 0; int rowHeight = 30; int barHeight = 20; int labelX = 10, barLeft = 300, barRight; int topY = 45; int numX = 210; Field splitField, valueField; String lastValueMenuChoice; double min, max; void redo() { bars = new ArrayList(); splitField = getModel().getDB().getField((String) splitFieldChoice.getSelectedItem()); if (splitField != null) { int n = numFieldChoice.getSelectedIndex(); if (n == 0) { agg = Aggregate.COUNT; } else { agg = n % 2 == 1 ? Aggregate.TOTAL : Aggregate.AVERAGE; } if (agg == Aggregate.COUNT) { Bag bag = DBUtils.countValues(getModel().getActs(), splitField); for (String s : bag.list()) { bars.add(new BarData(s, bag.num(s))); } } else { lastValueMenuChoice = (String) numFieldChoice.getSelectedItem(); int colon = lastValueMenuChoice.indexOf(':'); valueField = getModel().getDB().getField(lastValueMenuChoice.substring(colon + 2)); DoubleBag bag = new DoubleBag(); for (Act a : getModel().getActs()) { if (splitField.getType() == String.class) { bag.add(a.getString(splitField), a.getValue(valueField)); } else { String[] tags = a.getTextList(splitField); for (String tag : tags) { bag.add(tag, a.getValue(valueField)); } } } boolean isSum = agg == Aggregate.TOTAL; for (String s : bag.list(isSum)) { bars.add(new BarData(s, isSum ? bag.num(s) : bag.average(s))); } } } revalidate(); repaint(); } public void paintComponent(Graphics g1) { Graphics2D g = (Graphics2D) g1; int w = getSize().width, h = getSize().height; g.setColor(Color.white); g.fillRect(0, 0, w, h); TFModel model = getModel(); Display display = model.getDisplay(); if (display.emptyMessage(g, model)) { return; } if (bars == null) { return; } if (bars.size() == 0) { g.setColor(Color.gray); g.drawString("(No data selected.)", 10, 30); return; } int n = bars.size(); max = bars.get(0).num; min = Math.min(0, bars.get(n - 1).num); barRight = w - 30; int zero = scaleX(0); boolean isInColor = (splitField != null && getModel().getColorField() == splitField); // draw header int titleY = topY - 15; g.setColor(Color.black); g.setFont(display.big()); g.drawString(splitField.getName().toUpperCase(), labelX, titleY); String aggLabel = agg.toString(); if (agg != Aggregate.COUNT) { aggLabel += " " + valueField.getName().toUpperCase(); } g.drawString(aggLabel, barLeft, titleY); g.setFont(display.plain()); FontMetrics fm = display.plainFontMetrics(); // draw bars for (int i = 0; i < n; i++) { int y = topY + i * rowHeight; int ty = y + barHeight; BarData data = bars.get(i); Color c = null; g.setColor(Color.gray); // label value boolean missing = data.thing == null || (data.thing.toString().length() == 0); String label = missing ? "[missing]" : display.format(display.toString(data.thing), 25, false); if (isInColor) { g.setColor(missing ? Color.gray : display.makeColor(data.thing.toString())); display.makeColor(label); } g.drawString(label, labelX, ty); // label number String numLabel = display.format(data.num); g.drawString(numLabel, numX + 70 - fm.stringWidth(numLabel), ty); // draw bar. g.setColor(missing ? Color.lightGray : (isInColor ? c : Display.barColor)); int x = scaleX(data.num); int a = Math.min(x, zero); int b = Math.max(x, zero); g.fillRect(a, y + 5, b - a, barHeight); } } int scaleX(double x) { if (max == min) { return barLeft; } return (int) (barLeft + (barRight - barLeft) * (x - min) / (max - min)); } public Dimension getPreferredSize() { return new Dimension(400, 100 + rowHeight * (bars == null ? 0 : bars.size())); } } }