/*
* ModelsPanel.java
*
* Copyright (C) 2002-2009 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.beauti.clockModelsPanel;
import dr.app.beauti.BeautiFrame;
import dr.app.beauti.BeautiPanel;
import dr.app.beauti.ComboBoxRenderer;
import dr.app.beauti.options.*;
import dr.app.beauti.types.ClockType;
import dr.app.beauti.util.PanelUtils;
import dr.app.gui.components.RealNumberField;
import dr.app.gui.table.RealNumberCellEditor;
import dr.app.gui.table.TableEditorStopper;
import jam.framework.Exportable;
import jam.panels.OptionsPanel;
import jam.table.TableRenderer;
import javax.swing.*;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.plaf.BorderUIResource;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
/**
* @author Andrew Rambaut
* @author Alexei Drummond
* @author Walter Xie
* @version $Id: ModelPanel.java,v 1.17 2006/09/05 13:29:34 rambaut Exp $
* @deprecated
*/
public class ClockModelsPanel extends BeautiPanel implements Exportable {
public final static boolean DEBUG = false;
private static final long serialVersionUID = 2778103564318492601L;
private static final int MINIMUM_TABLE_WIDTH = 140;
private final String[] columnToolTips = {null, "Molecular clock model",
"Decide whether to estimate molecular clock model",
"Provide the rate if it is fixed"};
JTable clockTable = null;
ClockTableModel clockTableModel = null;
BeautiOptions options = null;
JPanel modelPanelParent;
PartitionClockModel currentModel = null;
Map<PartitionClockModel, PartitionClockModelPanel> modelPanels = new HashMap<PartitionClockModel, PartitionClockModelPanel>();
TitledBorder modelBorder;
JCheckBox fixedMeanRateCheck = new JCheckBox("Fix mean rate of molecular clock model to: ");
RealNumberField meanRateField = new RealNumberField(Double.MIN_VALUE, Double.MAX_VALUE);
BeautiFrame frame = null;
// CreateModelDialog createModelDialog = null;
boolean settingOptions = false;
public ClockModelsPanel(BeautiFrame parent) {
super();
this.frame = parent;
clockTableModel = new ClockTableModel();
clockTable = new JTable(clockTableModel); // {
//Implement table header tool tips.
// protected JTableHeader createDefaultTableHeader() {
// return new JTableHeader(columnModel) {
// public String getToolTipText(MouseEvent e) {
// Point p = e.getPoint();
// int index = columnModel.getColumnIndexAtX(p.x);
// int realIndex = columnModel.getColumn(index).getModelIndex();
// return columnToolTips[realIndex];
// }
// };
// }
// };
initTable(clockTable);
clockTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent evt) {
selectionChanged();
}
});
JScrollPane scrollPane = new JScrollPane(clockTable,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPane.setOpaque(false);
// ActionPanel actionPanel1 = new ActionPanel(false);
// actionPanel1.setAddAction(addModelAction);
// actionPanel1.setRemoveAction(removeModelAction);
JPanel controlPanel1 = new JPanel(new FlowLayout(FlowLayout.LEFT));
controlPanel1.setOpaque(false);
// controlPanel1.add(actionPanel1);
JPanel panel = new JPanel(new BorderLayout(0, 0));
panel.setOpaque(false);
panel.add(scrollPane, BorderLayout.CENTER);
// panel.add(controlPanel1, BorderLayout.SOUTH);
panel.setMinimumSize(new Dimension(MINIMUM_TABLE_WIDTH, 0));
modelPanelParent = new JPanel(new FlowLayout(FlowLayout.CENTER));
modelPanelParent.setOpaque(false);
modelBorder = new TitledBorder("Substitution Model");
modelPanelParent.setBorder(modelBorder);
setCurrentModel(null);
JScrollPane scrollPane2 = new JScrollPane(modelPanelParent, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane2.setOpaque(false);
scrollPane2.setBorder(null);
scrollPane2.getViewport().setOpaque(false);
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, panel, scrollPane2);
splitPane.setDividerLocation(0.5);
splitPane.setContinuousLayout(true);
splitPane.setBorder(BorderFactory.createEmptyBorder());
splitPane.setOpaque(false);
PanelUtils.setupComponent(fixedMeanRateCheck);
fixedMeanRateCheck.setSelected(false); // default to FixRateType.ESTIMATE
fixedMeanRateCheck.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent ev) {
meanRateField.setEnabled(fixedMeanRateCheck.isSelected());
// if (fixedMeanRateCheck.isSelected()) {
// options.clockModelOptions.fixMeanRate();
// } else {
// options.clockModelOptions.fixRateOfFirstClockPartition();
// }
clockTableModel.fireTableDataChanged();
fireModelsChanged();
}
});
fixedMeanRateCheck.setToolTipText("<html>Select this option to fix the mean substitution rate,<br>"
+ "rather than try to infer it. If this option is turned off, then<br>"
+ "either the sequences should have dates or the tree should have<br>"
+ "sufficient calibration informations specified as priors.<br>"
+ "In addition, it is only available for multi-clock partitions." + "</html>");// TODO Alexei
PanelUtils.setupComponent(meanRateField);
meanRateField.setEnabled(fixedMeanRateCheck.isSelected());
meanRateField.setValue(1.0);
meanRateField.addKeyListener(new java.awt.event.KeyAdapter() {
public void keyTyped(java.awt.event.KeyEvent ev) {
frame.setDirty();
}
});
meanRateField.setToolTipText("<html>Enter the fixed mean rate here.</html>");
meanRateField.setColumns(10);
OptionsPanel panel2 = new OptionsPanel(12, 12);
panel2.addComponents(fixedMeanRateCheck, meanRateField);
setOpaque(false);
setBorder(new BorderUIResource.EmptyBorderUIResource(new Insets(12, 12, 12, 12)));
setLayout(new BorderLayout(0, 0));
add(splitPane, BorderLayout.CENTER);
add(panel2, BorderLayout.SOUTH);
}
private void initTable(JTable clockTable){
// clockTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
clockTable.getTableHeader().setReorderingAllowed(false);
// clockTable.getTableHeader().setDefaultRenderer(new HeaderRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4)));
TableColumn col = clockTable.getColumnModel().getColumn(0);
col.setCellRenderer(new ClockTableCellRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4)));
// col.setMinWidth(80);
col = clockTable.getColumnModel().getColumn(1);
ComboBoxRenderer comboBoxRenderer = new ComboBoxRenderer();
comboBoxRenderer.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
// col.setMinWidth(260);
col = clockTable.getColumnModel().getColumn(2);
col.setMinWidth(60);
col.setMaxWidth(60);
col = clockTable.getColumnModel().getColumn(3);
col.setCellRenderer(new ClockTableCellRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4)));
col.setCellEditor(new RealNumberCellEditor(0, Double.POSITIVE_INFINITY));
// col.setMinWidth(80);
TableEditorStopper.ensureEditingStopWhenTableLosesFocus(clockTable);
}
private void modelsChanged() {
TableColumn col = clockTable.getColumnModel().getColumn(1);
col.setCellEditor(new DefaultCellEditor(new JComboBox(EnumSet.range(ClockType.STRICT_CLOCK, ClockType.RANDOM_LOCAL_CLOCK).toArray())));
}
private void resetPanel() {
if (!options.hasData()) {
currentModel = null;
modelPanels.clear();
modelPanelParent.removeAll();
modelBorder.setTitle("Clock Model");
return;
}
}
public void setOptions(BeautiOptions options) {
this.options = options;
resetPanel();
settingOptions = true;
// fixedMeanRateCheck.setSelected(options.clockModelOptions.getRateOptionClockModel() == FixRateType.FIX_MEAN);
// fixedMeanRateCheck.setEnabled(!(options.clockModelOptions.getRateOptionClockModel() == FixRateType.TIP_CALIBRATED
// || options.clockModelOptions.getRateOptionClockModel() == FixRateType.NODE_CALIBRATED
// || options.clockModelOptions.getRateOptionClockModel() == FixRateType.RATE_CALIBRATED));
// meanRateField.setValue(options.clockModelOptions.getMeanRelativeRate());
int selRow = clockTable.getSelectedRow();
clockTableModel.fireTableDataChanged();
if (options.getPartitionSubstitutionModels().size() > 0) {
if (selRow < 0) {
selRow = 0;
}
clockTable.getSelectionModel().setSelectionInterval(selRow, selRow);
}
if (currentModel == null && options.getPartitionClockModels().size() > 0) {
clockTable.getSelectionModel().setSelectionInterval(0, 0);
}
modelsChanged();
settingOptions = false;
}
public void getOptions(BeautiOptions options) {
if (settingOptions) return;
// options.clockModelOptions.setMeanRelativeRate(meanRateField.getValue());
}
private void fireModelsChanged() {
options.updatePartitionAllLinks();
frame.setDirty();
}
private void selectionChanged() {
int selRow = clockTable.getSelectedRow();
if (selRow >= options.getPartitionClockModels().size()) {
selRow = 0;
clockTable.getSelectionModel().setSelectionInterval(selRow, selRow);
}
if (selRow >= 0) {
setCurrentModel(options.getPartitionClockModels().get(selRow));
// frame.modelSelectionChanged(!isUsed(selRow));
}
}
/**
* Sets the current model that this model panel is displaying
*
* @param model the new model to display
*/
private void setCurrentModel(PartitionClockModel model) {
if (model != null) {
if (currentModel != null) modelPanelParent.removeAll();
PartitionClockModelPanel panel = modelPanels.get(model);
if (panel == null) {
panel = new PartitionClockModelPanel(model);
modelPanels.put(model, panel);
}
currentModel = model;
modelPanelParent.add(panel);
updateBorder();
}
}
private void updateBorder() {
modelBorder.setTitle("Clock Model - " + currentModel.getName());
repaint();
}
private boolean isUsed(int row) {
PartitionClockModel model = options.getPartitionClockModels().get(row);
for (AbstractPartitionData partition : options.dataPartitions) {
if (partition.getPartitionClockModel() == model) {
return true;
}
}
return false;
}
public JComponent getExportableComponent() {
return this;
}
class ModelTableModel extends AbstractTableModel {
/**
*
*/
private static final long serialVersionUID = -6707994233020715574L;
String[] columnNames = {"Clock Model"};
public ModelTableModel() {
}
public int getColumnCount() {
return columnNames.length;
}
public int getRowCount() {
if (options == null) return 0;
return options.getPartitionClockModels().size();
}
public Object getValueAt(int row, int col) {
PartitionClockModel model = options.getPartitionClockModels().get(row);
switch (col) {
case 0:
return model.getName();
default:
throw new IllegalArgumentException("unknown column, " + col);
}
}
public boolean isCellEditable(int row, int col) {
return true;
}
public void setValueAt(Object value, int row, int col) {
String name = ((String) value).trim();
if (name.length() > 0) {
PartitionClockModel model = options.getPartitionClockModels().get(row);
model.setName(name); //TODO: update every same model in diff PD?
updateBorder();
fireModelsChanged();
}
}
public String getColumnName(int column) {
return columnNames[column];
}
public Class getColumnClass(int c) {
if (getRowCount() == 0) {
return Object.class;
}
return getValueAt(0, c).getClass();
}
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 ModelsTableCellRenderer extends TableRenderer {
public ModelsTableCellRenderer(int alignment, Insets insets) {
super(alignment, insets);
}
public Component getTableCellRendererComponent(JTable aTable,
Object value,
boolean aIsSelected,
boolean aHasFocus,
int aRow, int aColumn) {
if (value == null) return this;
Component renderer = super.getTableCellRendererComponent(aTable,
value,
aIsSelected,
aHasFocus,
aRow, aColumn);
if (!isUsed(aRow))
renderer.setForeground(Color.gray);
else
renderer.setForeground(Color.black);
return this;
}
}
class ClockTableModel extends AbstractTableModel {
private static final long serialVersionUID = -2852144669936634910L;
// String[] columnNames = {"Clock Model Name", "Molecular Clock Model"};
String[] columnNames = {"Name", "Model", "Estimate", "Rate"};
public ClockTableModel() {
}
public int getColumnCount() {
// if (estimateRelatieRateCheck.isSelected()) {
// return columnNames2.length;
// } else {
return columnNames.length;
// }
}
public int getRowCount() {
if (options == null) return 0;
if (options.getPartitionClockModels().size() < 2) {
fixedMeanRateCheck.setEnabled(false);
} else {
fixedMeanRateCheck.setEnabled(true);
}
return options.getPartitionClockModels().size();
}
public Object getValueAt(int row, int col) {
PartitionClockModel model = options.getPartitionClockModels().get(row);
switch (col) {
case 0:
return model.getName();
case 1:
return model.getClockType();
case 2:
return model.isEstimatedRate();
case 3:
return model.getRate();
}
return null;
}
public void setValueAt(Object aValue, int row, int col) {
PartitionClockModel model = options.getPartitionClockModels().get(row);
switch (col) {
case 0:
String name = ((String) aValue).trim();
if (name.length() > 0) {
model.setName(name);
}
break;
case 1:
model.setClockType((ClockType) aValue);
break;
case 2:
model.setEstimatedRate((Boolean) aValue);
// if (options.clockModelOptions.getRateOptionClockModel() == FixRateType.RElATIVE_TO) {
// if (!options.clockModelOptions.validateRelativeTo()) {
// JOptionPane.showMessageDialog(frame, "It must have at least one clock rate to be fixed !",
// "Validation Of Relative To ?th Rate", JOptionPane.WARNING_MESSAGE);
// model.setEstimatedRate(false);
// }
// }
break;
case 3:
model.setRate((Double) aValue, true);
options.selectParameters();
break;
default:
throw new IllegalArgumentException("unknown column, " + col);
}
fireModelsChanged();
}
public boolean isCellEditable(int row, int col) {
switch (col) {
case 2:// Check box
return !fixedMeanRateCheck.isSelected();
case 3:
return !fixedMeanRateCheck.isSelected() && !((Boolean) getValueAt(row, 2));
default:
return true;
}
}
public String getColumnName(int column) {
return columnNames[column];
}
public Class getColumnClass(int c) {
if (getRowCount() == 0) {
return Object.class;
}
return getValueAt(0, c).getClass();
}
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 ClockTableCellRenderer extends TableRenderer {
public ClockTableCellRenderer(int alignment, Insets insets) {
super(alignment, insets);
}
public Component getTableCellRendererComponent(JTable aTable,
Object value,
boolean aIsSelected,
boolean aHasFocus,
int aRow, int aColumn) {
if (value == null) return this;
Component renderer = super.getTableCellRendererComponent(aTable,
value,
aIsSelected,
aHasFocus,
aRow, aColumn);
if (fixedMeanRateCheck.isSelected() && aColumn > 1) {
renderer.setForeground(Color.gray);
} else if (!fixedMeanRateCheck.isSelected() && aColumn == 3 && (Boolean) aTable.getValueAt(aRow, 2)) {
renderer.setForeground(Color.gray);
} else {
renderer.setForeground(Color.black);
}
return this;
}
}
// Action addModelAction = new AbstractAction("+") {
// public void actionPerformed(ActionEvent ae) {
// createModel();
// }
// };
}