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<BarData> 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<Field> valueFields=new ArrayList<Field>();
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<BarData>();
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<String> 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<String> bag=new DoubleBag<String>();
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()));
}
}
}