/*
* SiteModelsPanel.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.siteModelsPanel;
import dr.app.beauti.BeautiFrame;
import dr.app.beauti.BeautiPanel;
import dr.app.beauti.components.sequenceerror.SequenceErrorModelComponentOptions;
import dr.app.beauti.options.AbstractPartitionData;
import dr.app.beauti.options.BeautiOptions;
import dr.app.beauti.options.PartitionSubstitutionModel;
import dr.app.beauti.types.SequenceErrorType;
import dr.app.beauti.util.PanelUtils;
import dr.app.gui.table.TableEditorStopper;
import dr.evolution.datatype.DataType;
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.*;
import java.util.List;
import java.util.logging.Logger;
/**
* @author Andrew Rambaut
* @author Alexei Drummond
* @version $Id: ModelPanel.java,v 1.17 2006/09/05 13:29:34 rambaut Exp $
*/
public class SiteModelsPanel 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;
private JPanel modelPanelParent;
private PartitionSubstitutionModel currentModel = null;
private Map<PartitionSubstitutionModel, PartitionModelPanel> modelPanels = new HashMap<PartitionSubstitutionModel, PartitionModelPanel>();
private TitledBorder modelBorder;
private CloneModelDialog cloneModelDialog = null;
BeautiFrame frame = null;
// CreateModelDialog createModelDialog = null;
boolean settingOptions = false;
CloneModelsAction cloneModelsAction = new CloneModelsAction();
public SiteModelsPanel(BeautiFrame parent, Action removeModelAction) {
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("Substitution Model");
// if (currentDiscreteTraitOption != null) {
// this.remove(d_splitPane);
// currentDiscreteTraitOption = null;
// }
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.getPartitionSubstitutionModels().size() > 0) {
modelTable.getSelectionModel().setSelectionInterval(0, 0);
}
settingOptions = false;
validate();
repaint();
}
public void getOptions(BeautiOptions options) {
}
private void fireModelsChanged() {
options.updatePartitionAllLinks();
frame.setDirty();
}
private void selectionChanged() {
if (modelTable.getSelectedRowCount() == 1) {
int selRow = modelTable.getSelectedRow();
if (selRow >= options.getPartitionSubstitutionModels().size()) {
selRow = 0;
modelTable.getSelectionModel().setSelectionInterval(selRow, selRow);
}
if (selRow >= 0) {
setCurrentModel(options.getPartitionSubstitutionModels().get(selRow));
// frame.modelSelectionChanged(!isUsed(selRow));
}
} else {
setCurrentModels(getSelectedModels());
}
}
private List<PartitionSubstitutionModel> getSelectedModels() {
java.util.List<PartitionSubstitutionModel> models = new ArrayList<PartitionSubstitutionModel>();
for (int row : modelTable.getSelectedRows()) {
models.add(options.getPartitionSubstitutionModels().get(row));
}
if (models.size() == 0) {
models.addAll(options.getPartitionSubstitutionModels());
}
return models;
}
/**
* Sets the current model that this model panel is displaying
*
* @param model the new model to display
*/
private void setCurrentModel(PartitionSubstitutionModel model) {
modelPanelParent.removeAll();
currentModel = model;
if (currentModel != null) {
PartitionModelPanel panel = modelPanels.get(currentModel);
if (panel == null) {
panel = new PartitionModelPanel(currentModel);
modelPanels.put(currentModel, panel);
}
panel.setOptions();
modelPanelParent.add(panel);
} else {
}
cloneModelsAction.setEnabled(true);
updateBorder();
}
private void setCurrentModels(List<PartitionSubstitutionModel> models) {
modelPanelParent.removeAll();
currentModel = null;
Set<DataType> dataTypes = new HashSet<DataType>();
for (PartitionSubstitutionModel model : models) {
dataTypes.add(model.getDataType());
}
if (dataTypes.size() == 1) {
DataType dataType = dataTypes.iterator().next();
modelBorder.setTitle("Multiple " + dataType.getName() + " substitution models selected");
} else {
modelBorder.setTitle("Multiple mixed type substitution models selected");
}
cloneModelsAction.setEnabled(dataTypes.size() == 1);
repaint();
}
private void updateBorder() {
if (currentModel != null) {
modelBorder.setTitle(currentModel.getDataType().getName() + " Substitution Model - " + currentModel.getName());
} else {
modelBorder.setTitle("Multiple substitution models selected");
}
repaint();
}
private void cloneModelSettings() {
if (cloneModelDialog == null) {
cloneModelDialog = new CloneModelDialog(frame);
}
Set<DataType> dataTypes = new HashSet<DataType>();
for (PartitionSubstitutionModel model : getSelectedModels()) {
dataTypes.add(model.getDataType());
}
if (dataTypes.size() == 1) {
DataType dataType = dataTypes.iterator().next();
List<PartitionSubstitutionModel> sourceModels = new ArrayList<PartitionSubstitutionModel>();
for (PartitionSubstitutionModel model : options.getPartitionSubstitutionModels()) {
if (model.getDataType().equals(dataType)) {
sourceModels.add(model);
}
}
int result = cloneModelDialog.showDialog(sourceModels);
if (result == -1 || result == JOptionPane.CANCEL_OPTION) {
return;
}
PartitionSubstitutionModel sourceModel = cloneModelDialog.getSourceModel();
for (PartitionSubstitutionModel model : getSelectedModels()) {
if (!model.equals(sourceModel)) {
model.copyFrom(sourceModel);
}
}
repaint();
}
}
private boolean isUsed(int row) {
PartitionSubstitutionModel model = options.getPartitionSubstitutionModels().get(row);
for (AbstractPartitionData partition : options.dataPartitions) {
if (partition.getPartitionSubstitutionModel() == model) {
return true;
}
}
return false;
}
public JComponent getExportableComponent() {
return this;
}
class ModelTableModel extends AbstractTableModel {
/**
*
*/
private static final long serialVersionUID = -6707994233020715574L;
String[] columnNames = {"Substitution Model"};
public ModelTableModel() {
}
public int getColumnCount() {
return columnNames.length;
}
public int getRowCount() {
if (options == null) return 0;
return options.getPartitionSubstitutionModels().size();
}
public Object getValueAt(int row, int col) {
PartitionSubstitutionModel model = options.getPartitionSubstitutionModels().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) {
PartitionSubstitutionModel model = options.getPartitionSubstitutionModels().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();
}
}
}