/*
* JointPriorDialog.java
*
* Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard
*
* 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
*/
/*
* PriorDialog.java
*
* @author Marc A. Suchard
*/
package dr.app.beauti.priorsPanel;
import dr.app.beauti.ComboBoxRenderer;
import dr.app.beauti.components.hpm.HierarchicalModelComponentOptions;
import dr.app.beauti.components.linkedparameters.LinkedParameter;
import dr.app.beauti.options.BeautiOptions;
import dr.app.beauti.options.Parameter;
import dr.app.beauti.options.TraitData;
import dr.app.beauti.traitspanel.CreateTraitDialog;
import dr.app.beauti.types.PriorType;
import dr.app.gui.components.RealNumberField;
import dr.app.gui.table.TableEditorStopper;
import dr.app.util.OSType;
import jam.panels.ActionPanel;
import jam.panels.OptionsPanel;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
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.ActionEvent;
import java.util.*;
import java.util.List;
/**
* A dialog which acts as a base for linking parameters together under joint priors such
* as hierarchical models.
*
* @author Andrew Rambaut
* @author Marc A. Suchard
*/
public class JointPriorDialog implements AbstractPriorDialog {
private static final int MINIMUM_TABLE_WIDTH = 120;
private static final int MINIMUM_TABLE_HEIGHT = 160;
private static final int PREFERRED_TABLE_WIDTH = 180;
private static final int PREFERRED_TABLE_HEIGHT = 320;
private JFrame frame;
private final JTable parametersTable;
private final ParametersTableModel parametersTableModel;
private JTextField nameField = new JTextField();
private JPanel panel;
private final PriorSettingsPanel priorSettingsPanel;
private Parameter parameter;
final private BeautiOptions options;
private SelectParametersDialog selectParametersDialog = null;
private List<Parameter> compatibleParameterList;
private List<Parameter> dependentParameterList;
public JointPriorDialog(JFrame frame, BeautiOptions options) {
this.frame = frame;
this.options = options;
priorSettingsPanel = new PriorSettingsPanel(frame);
nameField.setColumns(30);
parametersTableModel = new ParametersTableModel();
// TableSorter sorter = new TableSorter(traitsTableModel);
// traitsTable = new JTable(sorter);
// sorter.setTableHeader(traitsTable.getTableHeader());
parametersTable = new JTable(parametersTableModel);
parametersTable.getTableHeader().setReorderingAllowed(false);
parametersTable.getTableHeader().setResizingAllowed(false);
// traitsTable.getTableHeader().setDefaultRenderer(
// new HeaderRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4)));
parametersTable.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
parametersTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent evt) {
parametersSelectionChanged();
}
});
}
private void parametersSelectionChanged() {
int selRow = parametersTable.getSelectedRow();
if (selRow >= 0) {
removeParameterAction.setEnabled(true);
}
if (dependentParameterList.size() <= 0) {
removeParameterAction.setEnabled(false);
}
}
public boolean validateModelName() {
return validateModelName(nameField.getText());
}
private boolean validateModelName(String name) {
// System.err.println("Validating: " + modelName);
// check that the name is valid
if (name.equals(parameter.getName())) {
return true;
}
if (name.trim().length() == 0) {
Toolkit.getDefaultToolkit().beep();
return false;
}
// check that a parameter with this name doesn't exist
if (parameterExists(name)) {
JOptionPane.showMessageDialog(frame,
"A parameter with this name already exists.",
"Linked parameter error",
JOptionPane.WARNING_MESSAGE);
return false;
}
// check that a model with this name doesn't exist
if (modelExists(name)) {
JOptionPane.showMessageDialog(frame,
"A model with this name already exists.",
"Linked parameter error",
JOptionPane.WARNING_MESSAGE);
return false;
}
return true;
}
private boolean parameterExists(String name) {
for (Parameter parameter : options.selectParameters()) {
if (parameter.getName().equals(name)) {
return true;
}
}
return false;
}
private boolean modelExists(String modelName) {
HierarchicalModelComponentOptions comp = (HierarchicalModelComponentOptions)
options.getComponentOptions(HierarchicalModelComponentOptions.class);
return comp.modelExists(modelName);
}
public int showDialog() {
panel = new JPanel(new GridBagLayout());
double lower = Double.NEGATIVE_INFINITY;
double upper = Double.POSITIVE_INFINITY;
if (parameter.isZeroOne) {
lower = 0.0;
upper = 1.0;
} else if (parameter.isNonNegative) {
lower = 0.0;
}
panel = new JPanel(new GridBagLayout());
setupComponents();
JOptionPane optionPane = new JOptionPane(panel,
JOptionPane.PLAIN_MESSAGE,
JOptionPane.OK_CANCEL_OPTION,
null,
null,
null);
optionPane.setBorder(new EmptyBorder(12, 12, 12, 12));
final JDialog dialog = optionPane.createDialog(frame, "Linked Parameter Setup");
priorSettingsPanel.setDialog(dialog);
priorSettingsPanel.setParameter(parameter);
if (OSType.isMac()) {
dialog.setMinimumSize(new Dimension(dialog.getBounds().width, 300));
} else {
Toolkit tk = Toolkit.getDefaultToolkit();
Dimension d = tk.getScreenSize();
if (d.height < 700 && panel.getHeight() > 450) {
dialog.setSize(new Dimension(panel.getWidth() + 100, 550));
} else {
// setSize because optionsPanel is shrunk in dialog
dialog.setSize(new Dimension(panel.getWidth() + 100, panel.getHeight() + 100));
}
// System.out.println("panel width = " + panel.getWidth());
// System.out.println("panel height = " + panel.getHeight());
}
dialog.pack();
dialog.setResizable(true);
dialog.setVisible(true);
int result = JOptionPane.CANCEL_OPTION;
Integer value = (Integer) optionPane.getValue();
if (value != null && value != -1) {
result = value;
}
return result;
}
public String getName() {
return nameField.getText();
}
public List<Parameter> getDependentParameterList() {
return dependentParameterList;
}
private void setupComponents() {
panel.removeAll();
JScrollPane scrollPane1 = new JScrollPane(parametersTable,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
scrollPane1.setOpaque(false);
ActionPanel actionPanel1 = new ActionPanel(false);
actionPanel1.setAddAction(addParameterAction);
actionPanel1.setRemoveAction(removeParameterAction);
actionPanel1.setAddToolTipText("Use this button to add an existing parameter to the prior");
actionPanel1.setRemoveToolTipText("Use this button to remove a parameter from the prior");
removeParameterAction.setEnabled(false);
JPanel controlPanel1 = new JPanel(new FlowLayout(FlowLayout.LEFT));
controlPanel1.setOpaque(false);
controlPanel1.add(actionPanel1);
JPanel panel1 = new JPanel(new BorderLayout(0, 0));
panel1.setOpaque(false);
panel1.add(new JLabel("Linked parameters:"), BorderLayout.NORTH);
panel1.add(scrollPane1, BorderLayout.CENTER);
// removing the control panel for now. Not sure whether we really want adding and
// removing of parameteres in this dialog.
// panel1.add(controlPanel1, BorderLayout.SOUTH);
panel1.setSize(new Dimension(PREFERRED_TABLE_WIDTH, PREFERRED_TABLE_HEIGHT));
panel1.setPreferredSize(new Dimension(PREFERRED_TABLE_WIDTH, PREFERRED_TABLE_HEIGHT));
panel1.setMinimumSize(new Dimension(MINIMUM_TABLE_WIDTH, MINIMUM_TABLE_HEIGHT));
OptionsPanel optionsPanel = new OptionsPanel(0,6);
if (parameter.getName() != null) {
nameField.setText(parameter.getName());
} else {
nameField.setText("Untitled");
}
optionsPanel.addComponentWithLabel("Unique Name: ", nameField);
// optionsPanel.addComponentWithLabel("Initial Value: ", initialField);
panel.setOpaque(false);
panel.setBorder(new BorderUIResource.EmptyBorderUIResource(new Insets(12, 12, 12, 12)));
GridBagConstraints c = new GridBagConstraints();
c.gridx = 0;
c.gridy = 0;
c.weightx = 0;
c.weighty = 0;
c.anchor = GridBagConstraints.PAGE_START;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridwidth = 2;
panel.add(optionsPanel, c);
c.gridx = 0;
c.gridy = 1;
c.anchor = GridBagConstraints.PAGE_START;
c.fill = GridBagConstraints.VERTICAL;
c.gridwidth = 1;
panel.add(panel1, c);
c.gridx = 1;
c.gridy = 1;
c.weightx = 1;
c.weighty = 1;
c.fill = GridBagConstraints.BOTH;
c.anchor = GridBagConstraints.PAGE_START;
c.gridwidth = GridBagConstraints.REMAINDER;
panel.add(priorSettingsPanel, c);
}
public void getArguments(Parameter parameter) {
priorSettingsPanel.getArguments(parameter);
}
public boolean hasInvalidInput(boolean showError) {
return priorSettingsPanel.hasInvalidInput(showError);
}
public boolean addParameter() {
if (selectParametersDialog == null) {
selectParametersDialog = new SelectParametersDialog(frame);
}
List<Parameter> availableParameters = new ArrayList<Parameter>(compatibleParameterList);
availableParameters.removeAll(dependentParameterList);
int result = selectParametersDialog.showDialog("Select parameter to add to this Linked Parameter", availableParameters);
if (result == JOptionPane.OK_OPTION) {
Parameter parameter = selectParametersDialog.getSelectedParameter();
dependentParameterList.add(parameter);
parametersTableModel.fireTableDataChanged();
} else if (result == JOptionPane.CANCEL_OPTION) {
return false;
}
return true;
}
private void removeSelectedParameters() {
int[] selRows = parametersTable.getSelectedRows();
List<Parameter> parametersToRemove = new ArrayList<Parameter>();
for (int row : selRows) {
parametersToRemove.add((Parameter)parametersTable.getValueAt(row, 0));
}
removeParameters(parametersToRemove);
}
private void removeParameters(List<Parameter> parametersToRemove) {
for (Parameter parameter : parametersToRemove) {
dependentParameterList.remove(parameter);
}
parametersTableModel.fireTableDataChanged();
}
private AddParameterAction addParameterAction = new AddParameterAction();
public void setLinkedParameter(LinkedParameter linkedParameter) {
parameter = linkedParameter.getArgumentParameter();
}
public void setDependentParameterList(List<Parameter> dependentParameterList) {
this.dependentParameterList = dependentParameterList;
}
public void setCompatibleParameterList(List<Parameter> compatibleParameterList) {
this.compatibleParameterList = compatibleParameterList;
}
public class AddParameterAction extends AbstractAction {
public AddParameterAction() {
super("Add parameter");
}
public void actionPerformed(ActionEvent ae) {
addParameter();
}
}
AbstractAction removeParameterAction = new AbstractAction() {
public void actionPerformed(ActionEvent ae) {
removeSelectedParameters();
}
};
class ParametersTableModel extends AbstractTableModel {
private static final long serialVersionUID = -6707994233020715574L;
String[] columnNames = {"Parameter"};
public ParametersTableModel() {
}
public int getColumnCount() {
return columnNames.length;
}
public int getRowCount() {
if (dependentParameterList == null) {
return 0;
}
return dependentParameterList.size();
}
public Object getValueAt(int row, int col) {
switch (col) {
case 0:
return dependentParameterList.get(row);
}
return null;
}
public String getColumnName(int column) {
return columnNames[column];
}
public Class getColumnClass(int c) {
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();
}
}
}