/*
* SpeciesSetPanel.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.taxonsetspanel;
import dr.app.beauti.BeautiFrame;
import dr.app.beauti.options.BeautiOptions;
import dr.app.beauti.options.TraitData;
import dr.evolution.util.Taxa;
import dr.evolution.util.Taxon;
import jam.table.TableRenderer;
import javax.swing.*;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* It is specified to *BEAST and used to replace Taxon Sets panel,
* because *BEAST calibration is only allowed in species tree
*
* @author Andrew Rambaut
* @author Alexei Drummond
* @author Walter Xie
*/
public class SpeciesSetPanel extends TaxonSetPanel {
private final String[] columnToolTips = {"The set of species defined for the calibration",
"Enforce the selected species set to be monophyletic on the specified tree"};
protected final String TAXA = "Species";
protected final String TAXON = "Species set";
protected SpeciesSetsTableModel speciesSetsTableModel = new SpeciesSetsTableModel();
public SpeciesSetPanel(BeautiFrame parent) {
super.frame = parent;
setText(true);
initTaxonSetsTable(speciesSetsTableModel, columnToolTips);
initTableColumn();
initPanel(addSpeciesSetAction, removeSpeciesSetAction);
}
protected void initTableColumn() {
final TableColumnModel tableColumnModel = taxonSetsTable.getColumnModel();
TableColumn tableColumn = tableColumnModel.getColumn(0);
tableColumn.setCellRenderer(new TableRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4)));
tableColumn.setMinWidth(20);
tableColumn = tableColumnModel.getColumn(1);
tableColumn.setPreferredWidth(10);
}
public void setOptions(BeautiOptions options) {
this.options = options;
resetPanel();
if (options.speciesSets == null) {
addSpeciesSetAction.setEnabled(false);
removeSpeciesSetAction.setEnabled(false);
} else if (options.starBEASTOptions.getEmptySpeciesIndex() < 0) {
addSpeciesSetAction.setEnabled(true);
}
}
protected void taxonSetsTableSelectionChanged() {
treeModelsChanged();
int[] rows = taxonSetsTable.getSelectedRows();
if (rows.length == 0) {
removeSpeciesSetAction.setEnabled(false);
} else if (rows.length == 1) {
currentTaxonSet = options.speciesSets.get(rows[0]);
setCurrentTaxonSet(currentTaxonSet);
removeSpeciesSetAction.setEnabled(true);
} else {
setCurrentTaxonSet(null);
removeSpeciesSetAction.setEnabled(true);
}
}
protected void taxonSetChanged() {
currentTaxonSet.removeAllTaxa();
for (Taxon anIncludedTaxa : includedTaxa) {
currentTaxonSet.addTaxon(anIncludedTaxa);
}
setupTaxonSetsComboBoxes();
if (options.speciesSetsMono.get(currentTaxonSet) != null &&
options.speciesSetsMono.get(currentTaxonSet) &&
!checkCompatibility(currentTaxonSet)) {
options.speciesSetsMono.put(currentTaxonSet, Boolean.FALSE);
}
frame.setDirty();
}
protected void resetPanel() {
if (!options.hasData() || options.speciesSets == null || options.speciesSets.size() < 1) {
setCurrentTaxonSet(null);
}
}
protected void setCurrentTaxonSet(Taxa taxonSet) {
currentTaxonSet = taxonSet;
includedTaxa.clear();
excludedTaxa.clear();
if (currentTaxonSet != null) {
for (int i = 0; i < taxonSet.getTaxonCount(); i++) {
includedTaxa.add(taxonSet.getTaxon(i));
}
Collections.sort(includedTaxa);
Set<String> allSpecies = TraitData.getStatesListOfTrait(options.taxonList, TraitData.TRAIT_SPECIES);
for (String sp : allSpecies) {
excludedTaxa.add(new Taxon(sp));
}
excludedTaxa.removeAll(includedTaxa);
Collections.sort(excludedTaxa);
}
setTaxonSetTitle();
setupTaxonSetsComboBoxes();
includedTaxaTableModel.fireTableDataChanged();
excludedTaxaTableModel.fireTableDataChanged();
}
protected void setupTaxonSetsComboBox(JComboBox comboBox, List<Taxon> availableTaxa) {
comboBox.removeAllItems();
comboBox.addItem(TAXON.toLowerCase() + "...");
for (Taxa taxa : options.speciesSets) {
if (taxa != currentTaxonSet) {
if (isCompatible(taxa, availableTaxa)) {
comboBox.addItem(taxa);
}
}
}
}
protected boolean checkCompatibility(Taxa taxa) {
for (Taxa taxa2 : options.speciesSets) {
if (taxa2 != taxa && options.speciesSetsMono.get(taxa2)) {
if (taxa.containsAny(taxa2) && !taxa.containsAll(taxa2) && !taxa2.containsAll(taxa)) {
JOptionPane.showMessageDialog(frame,
"You cannot enforce monophyly on this " + TAXON.toLowerCase() + " \n" +
"because it is not compatible with another " + TAXON.toLowerCase() + ",\n" +
taxa2.getId() + ", for which monophyly is\n" + "enforced.",
"Warning",
JOptionPane.WARNING_MESSAGE);
return false;
}
}
}
return true;
}
protected void treeModelsChanged() { }
Action addSpeciesSetAction = new AbstractAction("+") {
public void actionPerformed(ActionEvent ae) {
taxonSetCount++;
String newSpeciesSetName = "untitled" + taxonSetCount;
Taxa newSpeciesSet = new Taxa(newSpeciesSetName); // cannot use currentTaxonSet
options.speciesSets.add(newSpeciesSet);
Collections.sort(options.speciesSets);
options.speciesSetsMono.put(newSpeciesSet, Boolean.FALSE);
setCurrentTaxonSet(newSpeciesSet);
taxonSetChanged();
speciesSetsTableModel.fireTableDataChanged();
int sel = options.getSpeciesIndex(newSpeciesSetName);
if (sel < 0) {
taxonSetsTable.setRowSelectionInterval(0, 0);
} else {
taxonSetsTable.setRowSelectionInterval(sel, sel);
}
}
};
Action removeSpeciesSetAction = new AbstractAction("-") {
public void actionPerformed(ActionEvent ae) {
int row = taxonSetsTable.getSelectedRow();
if (row != -1) {
Taxa taxa = options.speciesSets.remove(row);
options.speciesSetsMono.remove(taxa);
}
taxonSetChanged();
speciesSetsTableModel.fireTableDataChanged();
if (row >= options.speciesSets.size()) {
row = options.speciesSets.size() - 1;
}
if (row >= 0) {
taxonSetsTable.setRowSelectionInterval(row, row);
} else {
setCurrentTaxonSet(null);
}
}
};
/**
* The table on the left side of panel
*/
protected class SpeciesSetsTableModel extends TaxonSetPanel.TaxonSetsTableModel {
String[] columnNames = {"Species Sets", "Monophyletic?"};
public SpeciesSetsTableModel() {
super();
}
public int getColumnCount() {
return columnNames.length;
}
public String getColumnName(int column) {
return columnNames[column];
}
public int getRowCount() {
if (options == null) return 0;
return options.speciesSets.size();
}
public Object getValueAt(int rowIndex, int columnIndex) {
Taxa taxonSet = options.speciesSets.get(rowIndex);
switch (columnIndex) {
case 0:
return taxonSet.getId();
case 1:
return options.speciesSetsMono.get(taxonSet);
default:
throw new IllegalArgumentException("unknown column, " + columnIndex);
}
}
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
Taxa taxonSet = options.speciesSets.get(rowIndex);
switch (columnIndex) {
case 0:
taxonSet.setId(aValue.toString());
options.renameTMRCAStatistic(taxonSet);
setTaxonSetTitle();
break;
case 1:
if ((Boolean) aValue) {
Taxa taxa = options.speciesSets.get(rowIndex);
if (checkCompatibility(taxa)) {
options.speciesSetsMono.put(taxonSet, (Boolean) aValue);
}
} else {
options.speciesSetsMono.put(taxonSet, (Boolean) aValue);
}
break;
default:
throw new IllegalArgumentException("unknown column, " + columnIndex);
}
}
}
}