/*
* PriorsPanel.java
*
* Copyright (C) 2002-2006 Alexei Drummond and Andrew Rambaut
*
* This file is part of BEAST.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership and licensing.
*
* BEAST is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* BEAST is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with BEAST; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
package dr.app.oldbeauti;
import dr.app.gui.components.RealNumberField;
import dr.app.gui.table.TableEditorStopper;
import dr.util.NumberFormatter;
import dr.app.gui.components.WholeNumberField;
import jam.framework.Exportable;
import jam.panels.OptionsPanel;
import jam.table.HeaderRenderer;
import jam.table.TableRenderer;
import javax.swing.*;
import javax.swing.plaf.BorderUIResource;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
/**
* @author Andrew Rambaut
* @author Alexei Drummond
* @version $Id: PriorsPanel.java,v 1.9 2006/09/05 13:29:34 rambaut Exp $
*/
public class PriorsPanel extends JPanel implements Exportable {
/**
*
*/
private static final long serialVersionUID = -2936049032365493416L;
JScrollPane scrollPane = new JScrollPane();
JTable priorTable = null;
PriorTableModel priorTableModel = null;
OptionsPanel treePriorPanel = new OptionsPanel();
JComboBox treePriorCombo;
JComboBox parameterizationCombo = new JComboBox(new String[]{
"Growth Rate", "Doubling Time"});
JComboBox bayesianSkylineCombo = new JComboBox(new String[]{
"Piecewise-constant", "Piecewise-linear"});
WholeNumberField groupCountField = new WholeNumberField(2, Integer.MAX_VALUE);
RealNumberField samplingProportionField = new RealNumberField(Double.MIN_VALUE, 1.0);
JCheckBox upgmaStartingTreeCheck = new JCheckBox("Use UPGMA to construct a starting tree");
public ArrayList parameters = new ArrayList();
BeautiFrame frame = null;
public PriorsPanel(BeautiFrame parent) {
this.frame = parent;
priorTableModel = new PriorTableModel();
priorTable = new JTable(priorTableModel);
priorTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_OFF);
priorTable.getTableHeader().setReorderingAllowed(false);
priorTable.getTableHeader().setDefaultRenderer(
new HeaderRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4)));
priorTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_LAST_COLUMN);
priorTable.getColumnModel().getColumn(0).setCellRenderer(
new TableRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4)));
priorTable.getColumnModel().getColumn(0).setPreferredWidth(160);
priorTable.getColumnModel().getColumn(1).setCellRenderer(
new ButtonRenderer(SwingConstants.LEFT, new Insets(0, 8, 0, 8)));
priorTable.getColumnModel().getColumn(1).setCellEditor(
new ButtonEditor(SwingConstants.LEFT, new Insets(0, 8, 0, 8)));
priorTable.getColumnModel().getColumn(1).setPreferredWidth(260);
priorTable.getColumnModel().getColumn(2).setCellRenderer(
new TableRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4)));
priorTable.getColumnModel().getColumn(2).setPreferredWidth(400);
TableEditorStopper.ensureEditingStopWhenTableLosesFocus(priorTable);
scrollPane = new JScrollPane(priorTable,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
scrollPane.setOpaque(false);
java.awt.event.ItemListener listener = new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent ev) {
if (!settingOptions) frame.priorsChanged();
}
};
// order here must match corrosponding BeautiOptions constant, i.e. BeautiOptions.CONSTANT == 0 etc
if (BeautiApp.developer) {
treePriorCombo = new JComboBox(new String[]{
"Coalescent: Constant Size",
"Coalescent: Exponential Growth",
"Coalescent: Logistic Growth",
"Coalescent: Expansion Growth",
"Coalescent: Bayesian Skyline",
"Coalescent: Extended Bayesian Skyline",
"Speciation: Yule Process",
"Speciation: Birth-Death Process"
});
} else {
treePriorCombo = new JComboBox(new String[]{
"Coalescent: Constant Size",
"Coalescent: Exponential Growth",
"Coalescent: Logistic Growth",
"Coalescent: Expansion Growth",
"Coalescent: Bayesian Skyline",
"Coalescent: Extended Bayesian Skyline",
"Speciation: Yule Process",
"Speciation: Birth-Death Process"
});
}
setupComponent(treePriorCombo);
treePriorCombo.addItemListener(
new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent ev) {
if (!settingOptions) frame.priorsChanged();
setupPanel();
}
}
);
KeyListener keyListener = new KeyAdapter() {
public void keyTyped(java.awt.event.KeyEvent ev) {
if (!settingOptions && ev.getKeyCode() == KeyEvent.VK_ENTER) {
frame.priorsChanged();
}
}
};
groupCountField.addKeyListener(keyListener);
samplingProportionField.addKeyListener(keyListener);
FocusListener focusListener = new FocusAdapter() {
public void focusLost(FocusEvent focusEvent) {
frame.priorsChanged();
}
};
samplingProportionField.addFocusListener(focusListener);
groupCountField.addFocusListener(focusListener);
setupComponent(parameterizationCombo);
parameterizationCombo.addItemListener(listener);
setupComponent(bayesianSkylineCombo);
bayesianSkylineCombo.addItemListener(listener);
setupComponent(upgmaStartingTreeCheck);
setOpaque(false);
setLayout(new BorderLayout(0, 0));
setBorder(new BorderUIResource.EmptyBorderUIResource(new java.awt.Insets(12, 12, 12, 12)));
JPanel panel = new JPanel(new BorderLayout(0, 0));
panel.setOpaque(false);
panel.add(new JLabel("Priors for model parameters and statistics:"), BorderLayout.NORTH);
panel.add(scrollPane, BorderLayout.CENTER);
panel.add(new JLabel("* Marked parameters currently have a default prior distribution. " +
"You should check that these are appropriate."), BorderLayout.SOUTH);
treePriorPanel.setBorder(null);
add(treePriorPanel, BorderLayout.NORTH);
add(panel, BorderLayout.CENTER);
}
private void setupComponent(JComponent comp) {
comp.setOpaque(false);
if (comp instanceof JButton) {
comp.putClientProperty("JButton.buttonType", "roundRect");
}
if (comp instanceof JComboBox) {
comp.putClientProperty("JComboBox.isSquare", Boolean.TRUE);
}
}
private void setupPanel() {
treePriorPanel.removeAll();
treePriorPanel.addComponentWithLabel("Tree Prior:", treePriorCombo);
if (treePriorCombo.getSelectedIndex() == 1 || // exponential
treePriorCombo.getSelectedIndex() == 2 || // logistic
treePriorCombo.getSelectedIndex() == 3) { // expansion
treePriorPanel.addComponentWithLabel("Parameterization for growth:", parameterizationCombo);
} else if (treePriorCombo.getSelectedIndex() == 4) { // bayesian skyline
groupCountField.setColumns(6);
treePriorPanel.addComponentWithLabel("Number of groups:", groupCountField);
treePriorPanel.addComponentWithLabel("Skyline Model:", bayesianSkylineCombo);
} else if (treePriorCombo.getSelectedIndex() == 6) { // birth-death
samplingProportionField.setColumns(8);
treePriorPanel.addComponentWithLabel("Proportion of taxa sampled:", samplingProportionField);
}
treePriorPanel.addComponent(upgmaStartingTreeCheck);
validate();
repaint();
}
private boolean settingOptions = false;
public void setOptions(BeautiOptions options) {
settingOptions = true;
parameters = options.selectParameters();
priorTableModel.fireTableDataChanged();
treePriorCombo.setSelectedIndex(options.nodeHeightPrior);
groupCountField.setValue(options.skylineGroupCount);
samplingProportionField.setValue(options.birthDeathSamplingProportion);
parameterizationCombo.setSelectedIndex(options.parameterization);
bayesianSkylineCombo.setSelectedIndex(options.skylineModel);
upgmaStartingTreeCheck.setSelected(options.upgmaStartingTree);
setupPanel();
settingOptions = false;
validate();
repaint();
}
private PriorDialog priorDialog = null;
private DiscretePriorDialog discretePriorDialog = null;
private void priorButtonPressed(int row) {
BeautiOptions.Parameter param = (BeautiOptions.Parameter) parameters.get(row);
if (param.isDiscrete) {
if (discretePriorDialog == null) {
discretePriorDialog = new DiscretePriorDialog(frame);
}
if (discretePriorDialog.showDialog(param) == JOptionPane.CANCEL_OPTION) {
return;
}
} else {
if (priorDialog == null) {
priorDialog = new PriorDialog(frame);
}
if (priorDialog.showDialog(param) == JOptionPane.CANCEL_OPTION) {
return;
}
}
param.priorEdited = true;
priorTableModel.fireTableDataChanged();
}
public void getOptions(BeautiOptions options) {
if (settingOptions) return;
if (treePriorCombo.getSelectedIndex() == BeautiOptions.CONSTANT) {
options.nodeHeightPrior = BeautiOptions.CONSTANT;
} else if (treePriorCombo.getSelectedIndex() == BeautiOptions.EXPONENTIAL) {
options.nodeHeightPrior = BeautiOptions.EXPONENTIAL;
} else if (treePriorCombo.getSelectedIndex() == BeautiOptions.LOGISTIC) {
options.nodeHeightPrior = BeautiOptions.LOGISTIC;
} else if (treePriorCombo.getSelectedIndex() == BeautiOptions.EXPANSION) {
options.nodeHeightPrior = BeautiOptions.EXPANSION;
} else if (treePriorCombo.getSelectedIndex() == BeautiOptions.SKYLINE) {
options.nodeHeightPrior = BeautiOptions.SKYLINE;
Integer groupCount = groupCountField.getValue();
if (groupCount != null) {
options.skylineGroupCount = groupCount;
} else {
options.skylineGroupCount = 5;
}
} else if (treePriorCombo.getSelectedIndex() == BeautiOptions.EXTENDED_SKYLINE) {
options.nodeHeightPrior = BeautiOptions.EXTENDED_SKYLINE;
} else if (treePriorCombo.getSelectedIndex() == BeautiOptions.YULE) {
options.nodeHeightPrior = BeautiOptions.YULE;
} else if (treePriorCombo.getSelectedIndex() == BeautiOptions.BIRTH_DEATH) {
options.nodeHeightPrior = BeautiOptions.BIRTH_DEATH;
Double samplingProportion = samplingProportionField.getValue();
if (samplingProportion != null) {
options.birthDeathSamplingProportion = samplingProportion;
} else {
options.birthDeathSamplingProportion = 1.0;
}
} else {
throw new RuntimeException("Unexpected value from treePriorCombo");
}
options.parameterization = parameterizationCombo.getSelectedIndex();
options.skylineModel = bayesianSkylineCombo.getSelectedIndex();
options.upgmaStartingTree = upgmaStartingTreeCheck.isSelected();
}
public JComponent getExportableComponent() {
return priorTable;
}
NumberFormatter formatter = new NumberFormatter(4);
class PriorTableModel extends AbstractTableModel {
/**
*
*/
private static final long serialVersionUID = -8864178122484971872L;
String[] columnNames = {"Parameter", "Prior", "Description"};
public PriorTableModel() {
}
public int getColumnCount() {
return columnNames.length;
}
public int getRowCount() {
return parameters.size();
}
public Object getValueAt(int row, int col) {
BeastGenerator.Parameter param = (BeastGenerator.Parameter) parameters.get(row);
switch (col) {
case 0:
return param.getName();
case 1:
return param.priorType.getPriorString(param);
case 2:
return param.getDescription();
}
return null;
}
public String getColumnName(int column) {
return columnNames[column];
}
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
public boolean isCellEditable(int row, int col) {
return col == 1;
}
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append(getColumnName(0));
for (int j = 1; j < getColumnCount(); j++) {
buffer.append("\t");
buffer.append(getColumnName(j));
}
buffer.append("\n");
for (int i = 0; i < getRowCount(); i++) {
buffer.append(getValueAt(i, 0));
for (int j = 1; j < getColumnCount(); j++) {
buffer.append("\t");
buffer.append(getValueAt(i, j));
}
buffer.append("\n");
}
return buffer.toString();
}
}
class DoubleRenderer extends TableRenderer {
/**
*
*/
private static final long serialVersionUID = -2614341608257369805L;
public DoubleRenderer(int alignment, Insets insets) {
super(true, alignment, insets);
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
String s;
if (((Double) value).isNaN()) {
s = "random";
} else {
s = formatter.format((Double) value);
}
return super.getTableCellRendererComponent(table, s, isSelected, hasFocus, row, column);
}
}
public class ButtonRenderer extends JButton implements TableCellRenderer {
/**
*
*/
private static final long serialVersionUID = -2416184092883649169L;
public ButtonRenderer(int alignment, Insets insets) {
setOpaque(true);
setHorizontalAlignment(alignment);
setMargin(insets);
}
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
setEnabled(table.isEnabled());
setFont(table.getFont());
if (isSelected) {
setForeground(table.getSelectionForeground());
setBackground(table.getSelectionBackground());
} else {
setForeground(table.getForeground());
setBackground(UIManager.getColor("Button.background"));
}
setText((value == null) ? "" : value.toString());
return this;
}
}
public class ButtonEditor extends DefaultCellEditor {
/**
*
*/
private static final long serialVersionUID = 6372738480075411674L;
protected JButton button;
private String label;
private boolean isPushed;
private int row;
public ButtonEditor(int alignment, Insets insets) {
super(new JCheckBox());
button = new JButton();
button.setOpaque(true);
button.setHorizontalAlignment(alignment);
button.setMargin(insets);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
fireEditingStopped();
}
});
}
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int row, int column) {
button.setEnabled(table.isEnabled());
button.setFont(table.getFont());
if (isSelected) {
button.setForeground(table.getSelectionForeground());
button.setBackground(table.getSelectionBackground());
} else {
button.setForeground(table.getForeground());
button.setBackground(table.getBackground());
}
label = (value == null) ? "" : value.toString();
button.setText(label);
isPushed = true;
this.row = row;
return button;
}
public Object getCellEditorValue() {
if (isPushed) {
priorButtonPressed(row);
}
isPushed = false;
return label;
}
public boolean stopCellEditing() {
isPushed = false;
return super.stopCellEditing();
}
protected void fireEditingStopped() {
super.fireEditingStopped();
}
}
}