/*
* ClockModelsPanel.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
*/
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 javax.swing.table.TableColumnModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
/**
* @author Andrew Rambaut
* @version $Id: ModelPanel.java,v 1.17 2006/09/05 13:29:34 rambaut Exp $
*/
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 JTable modelTable = null;
private ModelTableModel modelTableModel = null;
private 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;
private CloneModelDialog cloneModelDialog = null;
CloneModelsAction cloneModelsAction = new CloneModelsAction();
public ClockModelsPanel(BeautiFrame parent) {
super();
this.frame = parent;
modelTableModel = new ModelTableModel();
modelTable = new JTable(modelTableModel);
modelTable.getTableHeader().setReorderingAllowed(false);
modelTable.getTableHeader().setResizingAllowed(false);
// modelTable.getTableHeader().setDefaultRenderer(
// new HeaderRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4)));
final TableColumnModel model = modelTable.getColumnModel();
final TableColumn tableColumn0 = model.getColumn(0);
tableColumn0.setCellRenderer(new ModelsTableCellRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4)));
TableEditorStopper.ensureEditingStopWhenTableLosesFocus(modelTable);
modelTable.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
modelTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent evt) {
selectionChanged();
}
});
JScrollPane scrollPane = new JScrollPane(modelTable,
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));
JToolBar toolBar = new JToolBar();
toolBar.setFloatable(false);
toolBar.setOpaque(false);
toolBar.setLayout(new FlowLayout(java.awt.FlowLayout.LEFT, 0, 0));
JButton button = new JButton(cloneModelsAction);
PanelUtils.setupComponent(button);
toolBar.add(button);
panel.add(toolBar, BorderLayout.SOUTH);
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(MINIMUM_TABLE_WIDTH);
splitPane.setContinuousLayout(true);
splitPane.setBorder(BorderFactory.createEmptyBorder());
splitPane.setOpaque(false);
setOpaque(false);
setBorder(new BorderUIResource.EmptyBorderUIResource(new Insets(12, 12, 12, 12)));
setLayout(new BorderLayout(0, 0));
add(splitPane, BorderLayout.CENTER);
}
private void resetPanel() {
if (!options.hasData()) {
currentModel = null;
modelPanels.clear();
modelPanelParent.removeAll();
modelBorder.setTitle("Clock Model");
return;
}
}
public void setOptions(BeautiOptions options) {
if (DEBUG) {
Logger.getLogger("dr.app.beauti").info("ModelsPanel.setOptions");
}
this.options = options;
resetPanel();
settingOptions = true;
int selRow = modelTable.getSelectedRow();
modelTableModel.fireTableDataChanged();
if (options.getPartitionSubstitutionModels().size() > 0) {
if (selRow < 0) {
selRow = 0;
}
modelTable.getSelectionModel().setSelectionInterval(selRow, selRow);
}
if (currentModel == null && options.getPartitionClockModels().size() > 0) {
modelTable.getSelectionModel().setSelectionInterval(0, 0);
}
settingOptions = false;
validate();
repaint();
}
public void getOptions(BeautiOptions options) {
if (settingOptions) return;
// options.clockModelOptions.setMeanRelativeRate(meanRateField.getValue());
}
private void fireModelsChanged() {
options.updatePartitionAllLinks();
frame.setDirty();
}
private void selectionChanged() {
if (modelTable.getSelectedRowCount() == 1) {
int selRow = modelTable.getSelectedRow();
if (selRow >= options.getPartitionClockModels().size()) {
selRow = 0;
modelTable.getSelectionModel().setSelectionInterval(selRow, selRow);
}
if (selRow >= 0) {
setCurrentModel(options.getPartitionClockModels().get(selRow));
// frame.modelSelectionChanged(!isUsed(selRow));
}
} else {
setCurrentModels(getSelectedModels());
}
}
private java.util.List<PartitionClockModel> getSelectedModels() {
java.util.List<PartitionClockModel> models = new ArrayList<PartitionClockModel>();
for (int row : modelTable.getSelectedRows()) {
models.add(options.getPartitionClockModels().get(row));
}
if (models.size() == 0) {
models.addAll(options.getPartitionClockModels());
}
return models;
}
/**
* Sets the current model that this model panel is displaying
*
* @param model the new model to display
*/
private void setCurrentModel(PartitionClockModel model) {
modelPanelParent.removeAll();
currentModel = model;
if (currentModel != null) {
PartitionClockModelPanel panel = setPanelSettings(model);
modelPanelParent.add(panel);
} else {
}
cloneModelsAction.setEnabled(true);
updateBorder();
}
private PartitionClockModelPanel setPanelSettings(PartitionClockModel model) {
PartitionClockModelPanel panel = modelPanels.get(currentModel);
if (panel == null) {
panel = new PartitionClockModelPanel(model);
modelPanels.put(model, panel);
}
panel.setOptions();
return panel;
}
private void setCurrentModels(java.util.List<PartitionClockModel> models) {
modelPanelParent.removeAll();
currentModel = null;
updateBorder();
cloneModelsAction.setEnabled(true);
repaint();
}
private void updateBorder() {
if (currentModel != null) {
modelBorder.setTitle("Clock Model - " + currentModel.getName());
} else {
modelBorder.setTitle("Multiple clock models selected");
}
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;
}
private void cloneModelSettings() {
if (cloneModelDialog == null) {
cloneModelDialog = new CloneModelDialog(frame);
}
java.util.List<PartitionClockModel> sourceModels = new ArrayList<PartitionClockModel>();
for (PartitionClockModel model : options.getPartitionClockModels()) {
sourceModels.add(model);
}
int result = cloneModelDialog.showDialog(sourceModels);
if (result == -1 || result == JOptionPane.CANCEL_OPTION) {
return;
}
PartitionClockModel sourceModel = cloneModelDialog.getSourceModel();
for (PartitionClockModel model : getSelectedModels()) {
if (!model.equals(sourceModel)) {
model.copyFrom(sourceModel);
}
}
selectionChanged();
}
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;
}
}
public class CloneModelsAction extends AbstractAction {
public CloneModelsAction() {
super("Clone Settings...");
setToolTipText("Use this tool to copy settings to selected models");
}
public void actionPerformed(ActionEvent ae) {
cloneModelSettings();
}
}
}