/* * StatisticsPanel.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.treestat; import dr.app.gui.components.RealNumberField; import dr.app.gui.components.WholeNumberField; import dr.app.treestat.statistics.*; import dr.evolution.util.Taxa; import dr.evolution.util.Taxon; import jam.framework.Exportable; import jam.panels.OptionsPanel; import jam.table.TableRenderer; import jam.util.IconUtils; import javax.swing.*; import javax.swing.border.EmptyBorder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.AbstractTableModel; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.ArrayList; public class StatisticsPanel extends OptionsPanel implements Exportable { /** * */ private static final long serialVersionUID = -8026203872020056264L; TreeStatFrame frame; ArrayList<TreeSummaryStatistic.Factory> availableStatistics = new ArrayList<TreeSummaryStatistic.Factory>(); TreeStatData treeStatData = null; JScrollPane scrollPane1 = null; JScrollPane scrollPane2 = null; JTable includedStatisticsTable = null; JTable availableStatisticsTable = null; AvailableStatisticsTableModel availableStatisticsTableModel = null; IncludedStatisticsTableModel includedStatisticsTableModel = null; TreeSummaryStatisticLabel statisticLabel = new TreeSummaryStatisticLabel(null); public StatisticsPanel(TreeStatFrame frame, TreeStatData treeStatData) { this.frame = frame; this.treeStatData = treeStatData; // default //treeStatDatastics.add(TreeSummaryStatistic.Utils.createTMRCAStatistic()); // add generic tree statistics here availableStatistics.add(TreeLength.FACTORY); availableStatistics.add(TreeHeight.FACTORY); availableStatistics.add(NodeHeights.FACTORY); availableStatistics.add(InternalBranchLengths.FACTORY); availableStatistics.add(InternalBranchRates.FACTORY); availableStatistics.add(ExternalBranchRates.FACTORY); availableStatistics.add(InternalNodeAttribute.FACTORY); availableStatistics.add(RootToTipLengths.FACTORY); availableStatistics.add(TMRCASummaryStatistic.FACTORY); availableStatistics.add(CladeMRCAAttributeStatistic.FACTORY); availableStatistics.add(CladeMeanAttributeStatistic.FACTORY); availableStatistics.add(BetaTreeDiversityStatistic.FACTORY); // availableStatistics.add(MeanRootToTipLength.FACTORY); // availableStatistics.add(MedianRootToTipLength.FACTORY); availableStatistics.add(B1Statistic.FACTORY); availableStatistics.add(CollessIndex.FACTORY); availableStatistics.add(CherryStatistic.FACTORY); availableStatistics.add(SingleChildCountStatistic.FACTORY); availableStatistics.add(Nbar.FACTORY); availableStatistics.add(TreenessStatistic.FACTORY); availableStatistics.add(GammaStatistic.FACTORY); availableStatistics.add(DeltaStatistic.FACTORY); // availableStatistics.add(MonophylySummaryStatistic.FACTORY); // availableStatistics.add(ParsimonySummaryStatistic.FACTORY); availableStatistics.add(ExternalInternalRatio.FACTORY); availableStatistics.add(FuLiD.FACTORY); availableStatistics.add(RankProportionStatistic.FACTORY); availableStatistics.add(IntervalKStatistic.FACTORY); availableStatistics.add(LineageCountStatistic.FACTORY); availableStatistics.add(LineageProportionStatistic.FACTORY); availableStatistics.add(MRCAOlderThanStatistic.FACTORY); // if (treeStatDataeeStatDatanull) { // for (int i = 0; i < treeStatDataSets.size(); i++) { // availableStatistics.add(TreeSummaryStatistic.Utils.createTMRCAStatistic((TaxonList)treeStatDataSets.get(i))); // } // } setOpaque(false); Icon includeIcon = IconUtils.getIcon(this.getClass(), "images/include.png"); Icon excludeIcon = IconUtils.getIcon(this.getClass(), "images/exclude.png"); // Available statistics availableStatisticsTableModel = new AvailableStatisticsTableModel(); TableSorter sorter = new TableSorter(availableStatisticsTableModel); availableStatisticsTable = new JTable(sorter); availableStatisticsTable.getColumnModel().getColumn(0).setCellRenderer( new TableRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4))); availableStatisticsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent evt) { statisticsTableSelectionChanged(false); } }); scrollPane1 = new JScrollPane(availableStatisticsTable, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); JPanel buttonPanel1 = createAddRemoveButtonPanel( includeStatisticAction, includeIcon, null, excludeStatisticAction, excludeIcon, null, javax.swing.BoxLayout.Y_AXIS); // Included statistics includedStatisticsTableModel = new IncludedStatisticsTableModel(); sorter = new TableSorter(includedStatisticsTableModel); includedStatisticsTable = new JTable(sorter); includedStatisticsTable.getColumnModel().getColumn(0).setCellRenderer( new TableRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4))); includedStatisticsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent evt) { statisticsTableSelectionChanged(true); } }); scrollPane2 = new JScrollPane(includedStatisticsTable, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); setLayout(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints(); c.weightx = 0.5; c.weighty = 0.75; c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; c.insets = new Insets(6, 6, 6, 6); c.gridx = 0; c.gridy = 0; add(scrollPane1, c); c.weightx = 0; c.weighty = 0.75; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.CENTER; c.insets = new Insets(6, 0, 6, 0); c.gridx = 1; c.gridy = 0; add(buttonPanel1, c); c.weightx = 0.5; c.weighty = 0.75; c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; c.insets = new Insets(6, 6, 6, 6); c.gridx = 2; c.gridy = 0; add(scrollPane2, c); c.weightx = 1.0; c.weighty = 0.25; c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; c.insets = new Insets(6, 6, 6, 6); c.gridx = 0; c.gridy = 1; c.gridwidth = 3; add(statisticLabel, c); setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6)); } JPanel createAddRemoveButtonPanel(Action addAction, Icon addIcon, String addToolTip, Action removeAction, Icon removeIcon, String removeToolTip, int axis) { JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new BoxLayout(buttonPanel, axis)); buttonPanel.setOpaque(false); JButton addButton = new JButton(addAction); if (addIcon != null) { addButton.setIcon(addIcon); addButton.setText(null); } addButton.setToolTipText(addToolTip); addButton.putClientProperty("JButton.buttonType", "textured"); addButton.setOpaque(false); addAction.setEnabled(false); JButton removeButton = new JButton(removeAction); if (removeIcon != null) { removeButton.setIcon(removeIcon); removeButton.setText(null); } removeButton.setToolTipText(removeToolTip); removeButton.putClientProperty("JButton.buttonType", "textured"); removeButton.setOpaque(false); removeAction.setEnabled(false); buttonPanel.add(addButton); buttonPanel.add(new JToolBar.Separator(new Dimension(6, 6))); buttonPanel.add(removeButton); return buttonPanel; } public JComponent getExportableComponent() { return this; } public void dataChanged() { availableStatisticsTableModel.fireTableDataChanged(); includedStatisticsTableModel.fireTableDataChanged(); } private void statisticsTableSelectionChanged(boolean includedTable) { if (includedTable) { int index = includedStatisticsTable.getSelectedRow(); if (index != -1) { availableStatisticsTable.clearSelection(); SummaryStatisticDescription ssd = treeStatData.statistics.get(index); statisticLabel.setSummaryStatisticDescription(ssd); excludeStatisticAction.setEnabled(true); } else { excludeStatisticAction.setEnabled(false); } } else { int index = availableStatisticsTable.getSelectedRow(); if (index != -1) { includedStatisticsTable.clearSelection(); SummaryStatisticDescription ssd = availableStatistics.get(index); statisticLabel.setSummaryStatisticDescription(ssd); includeStatisticAction.setEnabled(true); } else { includeStatisticAction.setEnabled(false); } } } Action includeStatisticAction = new AbstractAction("->") { /** * */ private static final long serialVersionUID = -7179224487959650620L; public void actionPerformed(ActionEvent ae) { int[] indices = availableStatisticsTable.getSelectedRows(); for (int i = indices.length - 1; i >= 0; i--) { TreeSummaryStatistic.Factory ssd = availableStatistics.get(indices[i]); TreeSummaryStatistic tss = createStatistic(ssd); if (tss != null) { treeStatData.statistics.add(tss); } } frame.fireDataChanged(); dataChanged(); } }; Action excludeStatisticAction = new AbstractAction("<-") { /** * */ private static final long serialVersionUID = -3904236403703620633L; public void actionPerformed(ActionEvent ae) { int[] indices = includedStatisticsTable.getSelectedRows(); for (int i = indices.length - 1; i >= 0; i--) { treeStatData.statistics.remove(indices[i]); } frame.fireDataChanged(); dataChanged(); } }; public TreeSummaryStatistic createStatistic(TreeSummaryStatistic.Factory factory) { if (!factory.allowsTaxonList() && !factory.allowsDouble() && !factory.allowsInteger() && !factory.allowsString()) { return factory.createStatistic(); } OptionsPanel optionPanel = new OptionsPanel(); optionPanel.addSpanningComponent(new JLabel(factory.getSummaryStatisticDescription())); final JRadioButton wholeTreeRadio = new JRadioButton("For the whole tree", false); final JRadioButton taxonSetRadio = new JRadioButton("Using a given taxon set", false); final JComboBox taxonSetCombo = new JComboBox(); final JTextField valueField; if (factory.allowsTaxonList()) { for (Object taxonSet : treeStatData.taxonSets) { taxonSetCombo.addItem(taxonSet); } ButtonGroup group = new ButtonGroup(); ItemListener listener = new ItemListener() { public void itemStateChanged(ItemEvent e) { taxonSetCombo.setEnabled(taxonSetRadio.isSelected()); } }; if (factory.allowsWholeTree()) { group.add(wholeTreeRadio); wholeTreeRadio.addItemListener(listener); optionPanel.addSpanningComponent(wholeTreeRadio); optionPanel.addSeparator(); } if (factory.allowsTaxonList()) { group.add(taxonSetRadio); taxonSetRadio.addItemListener(listener); optionPanel.addSpanningComponent(taxonSetRadio); optionPanel.addComponentWithLabel("Taxon Set: ", taxonSetCombo); optionPanel.addSeparator(); } if (factory.allowsTaxonList()) { taxonSetRadio.setSelected(true); } if (factory.allowsWholeTree()) { wholeTreeRadio.setSelected(true); } } if (factory.allowsDouble() || factory.allowsInteger() || factory.allowsString()) { if (factory.allowsDouble()) { valueField = new RealNumberField(); valueField.setColumns(12); optionPanel.addComponentWithLabel(factory.getValueName(), valueField); } else if (factory.allowsInteger()) { valueField = new WholeNumberField(); valueField.setColumns(12); optionPanel.addComponentWithLabel(factory.getValueName(), valueField); } else { // allowsString valueField = new JTextField(); valueField.setColumns(24); optionPanel.addComponentWithLabel(factory.getValueName(), valueField); } } else { valueField = null; } JOptionPane optionPane = new JOptionPane(optionPanel, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION, null, null, null); optionPane.setBorder(new EmptyBorder(12, 12, 12, 12)); JDialog dialog = optionPane.createDialog(frame, factory.getSummaryStatisticName()); // dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); dialog.pack(); dialog.setVisible(true); if (optionPane.getValue() == null) { return null; } int result = (Integer) optionPane.getValue(); if (result == -1 || result == JOptionPane.CANCEL_OPTION) { return null; } TreeSummaryStatistic statistic = factory.createStatistic(); if (wholeTreeRadio.isSelected()) { statistic = factory.createStatistic(); } else if (taxonSetRadio.isSelected()) { TreeStatData.TaxonSet t = (TreeStatData.TaxonSet) taxonSetCombo.getSelectedItem(); Taxa taxa = new Taxa(); taxa.setId(t.name); //Iterator iter = t.taxa.iterator(); for (Object aTaxa : t.taxa) { String id = (String) aTaxa; Taxon taxon = new Taxon(id); taxa.addTaxon(taxon); } statistic.setTaxonList(taxa); } else { return null; } if (factory.allowsDouble()) { assert valueField instanceof RealNumberField; Double value = ((RealNumberField) valueField).getValue(); statistic.setDouble(value); } else if (factory.allowsInteger()) { assert valueField instanceof WholeNumberField; Integer value = ((WholeNumberField) valueField).getValue(); statistic.setInteger(value); } else if (factory.allowsString()) { String value = valueField.getText(); statistic.setString(value); } return statistic; } class AvailableStatisticsTableModel extends AbstractTableModel { /** * */ private static final long serialVersionUID = 86401307035717809L; public AvailableStatisticsTableModel() { } public int getColumnCount() { return 2; } public int getRowCount() { return availableStatistics.size(); } public Object getValueAt(int row, int col) { if (col == 0) return availableStatistics.get(row).getSummaryStatisticName(); return availableStatistics.get(row).getCategory(); } public boolean isCellEditable(int row, int col) { return false; } public String getColumnName(int column) { if (column == 0) return "Statistic Name"; return "Category"; } public Class getColumnClass(int c) { return getValueAt(0, c).getClass(); } } class IncludedStatisticsTableModel extends AbstractTableModel { /** * */ private static final long serialVersionUID = -7280629792388705376L; public IncludedStatisticsTableModel() { } public int getColumnCount() { return 1; } public int getRowCount() { if (treeStatData == null || treeStatData.statistics == null) return 0; return treeStatData.statistics.size(); } public Object getValueAt(int row, int col) { if (treeStatData == null || treeStatData.statistics == null) return null; if (col == 0) return treeStatData.statistics.get(row).getSummaryStatisticName(); return treeStatData.statistics.get(row).getSummaryStatisticDescription(); } public boolean isCellEditable(int row, int col) { return false; } public String getColumnName(int column) { if (column == 0) return "Statistic Name"; return "Description"; } public Class getColumnClass(int c) { return getValueAt(0, c).getClass(); } } class TreeSummaryStatisticLabel extends JLabel { /** * */ private static final long serialVersionUID = -5204925491148650874L; public TreeSummaryStatisticLabel(SummaryStatisticDescription statistic) { setSummaryStatisticDescription(statistic); setVerticalAlignment(JLabel.TOP); setHorizontalAlignment(JLabel.LEFT); } public void setSummaryStatisticDescription(SummaryStatisticDescription statistic) { String html = ""; if (statistic != null) { html = "<html><body><h3>" + statistic.getSummaryStatisticName() + "</h3>" + "<em>" + statistic.getSummaryStatisticDescription() + "</em>"; html += "<ul>"; if (!statistic.allowsNonultrametricTrees()) { html += "<li>Trees must be ultrametric.</li>"; } else if (!statistic.allowsUnrootedTrees()) { html += "<li>Trees must be rooted.</li>"; } if (!statistic.allowsPolytomies()) { html += "<li>Trees must be strictly bifurcating.</li>"; } html += "</ul>"; String ref = statistic.getSummaryStatisticReference(); if (ref != null && !ref.equals("") && !ref.equals("-")) { html += "Reference: " + ref; } html += "</body></html>"; } setText(html); } } }