/* * TipDatesPanel.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.tipdatepanel; import dr.app.beauti.BeautiFrame; import dr.app.beauti.BeautiPanel; import dr.app.beauti.components.tipdatesampling.TipDateSamplingComponentOptions; import dr.app.beauti.options.*; import dr.app.beauti.types.FixRateType; import dr.app.beauti.types.TipDateSamplingType; import dr.app.beauti.util.BEAUTiImporter; import dr.app.beauti.util.PanelUtils; import dr.app.gui.table.DateCellEditor; import dr.app.gui.table.TableEditorStopper; import dr.app.gui.table.TableSorter; import dr.app.util.Utils; import dr.evolution.util.*; import dr.evolution.util.Date; import dr.evoxml.util.DateUnitsType; import dr.util.DataTable; import jam.framework.Exportable; import jam.table.HeaderRenderer; import jam.table.TableRenderer; import javax.swing.*; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.plaf.BorderUIResource; import javax.swing.table.AbstractTableModel; import java.awt.*; import java.awt.event.*; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.*; import java.util.List; /** * @author Andrew Rambaut * @author Alexei Drummond * @author Tommy Lam * @version $Id: DataPanel.java,v 1.17 2006/09/05 13:29:34 rambaut Exp $ */ public class TipDatesPanel extends BeautiPanel implements Exportable { /** * */ private static final long serialVersionUID = 5283922195494563924L; JScrollPane scrollPane = new JScrollPane(); JTable dataTable = null; DataTableModel dataTableModel = null; SetDatesAction setDatesAction = new SetDatesAction(); ClearDatesAction clearDatesAction = new ClearDatesAction(); GuessDatesAction guessDatesAction = new GuessDatesAction(); ImportDatesAction importDatesAction = new ImportDatesAction(); SetPrecisionAction setPrecisionAction = new SetPrecisionAction(); JCheckBox usingTipDates = new JCheckBox("Use tip dates"); JCheckBox specifyOriginDate = new JCheckBox("Specify origin date:"); JTextField originDateText = new JTextField(20); JLabel originDateLabel = new JLabel(""); JComboBox unitsCombo = new JComboBox(EnumSet.range(DateUnitsType.YEARS, DateUnitsType.DAYS).toArray()); JComboBox directionCombo = new JComboBox(EnumSet.range(DateUnitsType.FORWARDS, DateUnitsType.BACKWARDS).toArray()); // JComboBox tipDateSamplingCombo = new JComboBox( TipDateSamplingType.values() ); JComboBox tipDateSamplingCombo = new JComboBox(new TipDateSamplingType[] { TipDateSamplingType.NO_SAMPLING, TipDateSamplingType.SAMPLE_INDIVIDUALLY, // TipDateSamplingType.SAMPLE_JOINT, TipDateSamplingType.SAMPLE_PRECISION }); JComboBox tipDateTaxonSetCombo = new JComboBox(); BeautiFrame frame = null; BeautiOptions options = null; double[] heights = null; private GuessDatesDialog guessDatesDialog = null; private SetValueDialog dateValueDialog = null; private SetValueDialog precisionValueDialog = null; public TipDatesPanel(BeautiFrame parent) { this.frame = parent; dataTableModel = new DataTableModel(); TableSorter sorter = new TableSorter(dataTableModel); dataTable = new JTable(sorter); sorter.setTableHeader(dataTable.getTableHeader()); dataTable.getTableHeader().setReorderingAllowed(false); // dataTable.getTableHeader().setDefaultRenderer( // new HeaderRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4))); dataTable.getTableHeader().setReorderingAllowed(false); dataTable.getTableHeader().setDefaultRenderer( new HeaderRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4))); dataTable.getColumnModel().getColumn(0).setCellRenderer( new TableRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4))); dataTable.getColumnModel().getColumn(0).setPreferredWidth(80); dataTable.getColumnModel().getColumn(1).setCellRenderer( new TableRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4))); dataTable.getColumnModel().getColumn(1).setPreferredWidth(80); dataTable.getColumnModel().getColumn(1).setCellEditor( new DateCellEditor()); dataTable.getColumnModel().getColumn(2).setCellRenderer( new TableRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4))); dataTable.getColumnModel().getColumn(2).setPreferredWidth(80); dataTable.getColumnModel().getColumn(2).setCellEditor( new DateCellEditor()); dataTable.getColumnModel().getColumn(3).setCellRenderer( new TableRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4))); dataTable.getColumnModel().getColumn(3).setPreferredWidth(80); TableEditorStopper.ensureEditingStopWhenTableLosesFocus(dataTable); dataTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent evt) { selectionChanged(); } }); scrollPane = new JScrollPane(dataTable, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); scrollPane.setOpaque(false); PanelUtils.setupComponent(unitsCombo); PanelUtils.setupComponent(directionCombo); JToolBar toolBar1 = new JToolBar(); toolBar1.setFloatable(false); toolBar1.setOpaque(false); toolBar1.setLayout(new FlowLayout(java.awt.FlowLayout.LEFT, 0, 0)); JButton button = new JButton(guessDatesAction); PanelUtils.setupComponent(button); toolBar1.add(button); button = new JButton(importDatesAction); PanelUtils.setupComponent(button); toolBar1.add(button); button = new JButton(setDatesAction); PanelUtils.setupComponent(button); toolBar1.add(button); button = new JButton(clearDatesAction); PanelUtils.setupComponent(button); toolBar1.add(button); button = new JButton(setPrecisionAction); PanelUtils.setupComponent(button); toolBar1.add(button); toolBar1.add(new JToolBar.Separator(new Dimension(12, 12))); final JLabel unitsLabel = new JLabel("Dates specified as "); toolBar1.add(unitsLabel); toolBar1.add(unitsCombo); toolBar1.add(directionCombo); JToolBar toolBar3 = new JToolBar(); toolBar3.setFloatable(false); toolBar3.setOpaque(false); toolBar3.setLayout(new FlowLayout(java.awt.FlowLayout.LEFT, 0, 0)); toolBar3.add(specifyOriginDate); toolBar3.add(originDateText); toolBar3.add(originDateLabel); JPanel panel2 = new JPanel(); panel2.setLayout(new BoxLayout(panel2, BoxLayout.PAGE_AXIS)); panel2.setOpaque(false); panel2.add(toolBar1); panel2.add(toolBar3); JPanel panel1 = new JPanel(new BorderLayout(0, 0)); panel1.setOpaque(false); panel1.add(panel2, "North"); panel1.add(scrollPane, "Center"); JToolBar toolBar2 = new JToolBar(); toolBar2.setFloatable(false); toolBar2.setOpaque(false); toolBar2.setLayout(new FlowLayout(java.awt.FlowLayout.LEFT, 0, 0)); PanelUtils.setupComponent(tipDateSamplingCombo); tipDateSamplingCombo.setToolTipText("<html>Select whether to allow sampling<br>" + "of all or individual tip dates.</html>"); // substitutionRateField.setToolTipText("<html>Enter the substitution rate here.</html>"); // substitutionRateField.setEnabled(true); final JLabel tipDateSamplingLabel = new JLabel("Tip date sampling:"); toolBar2.add(tipDateSamplingLabel); toolBar2.add(tipDateSamplingCombo); final JLabel tipDateTaxonSetLabel = new JLabel("Apply to taxon set:"); toolBar2.add(tipDateTaxonSetLabel); toolBar2.add(tipDateTaxonSetCombo); setOpaque(false); setBorder(new BorderUIResource.EmptyBorderUIResource(new java.awt.Insets(12, 12, 12, 12))); setLayout(new BorderLayout(0, 0)); add(usingTipDates, BorderLayout.NORTH); add(panel1, BorderLayout.CENTER); add(toolBar2, BorderLayout.SOUTH); tipDateSamplingCombo.addItemListener( new java.awt.event.ItemListener() { public void itemStateChanged(java.awt.event.ItemEvent ev) { boolean samplingOn = tipDateSamplingCombo.getSelectedItem() != TipDateSamplingType.NO_SAMPLING; tipDateTaxonSetLabel.setEnabled(samplingOn); tipDateTaxonSetCombo.setEnabled(samplingOn); fireModelsChanged(); } } ); specifyOriginDate.addItemListener( new java.awt.event.ItemListener() { public void itemStateChanged(java.awt.event.ItemEvent ev) { boolean enabled = usingTipDates.isSelected(); originDateText.setEnabled(enabled && specifyOriginDate.isSelected()); originDateLabel.setEnabled(enabled && specifyOriginDate.isSelected()); timeScaleChanged(); } } ); originDateText.addFocusListener( new FocusAdapter() { @Override public void focusLost(FocusEvent focusEvent) { timeScaleChanged(); } } ); clearDatesAction.setEnabled(false); guessDatesAction.setEnabled(false); importDatesAction.setEnabled(false); setDatesAction.setEnabled(false); setPrecisionAction.setEnabled(false); directionCombo.setEnabled(false); unitsLabel.setEnabled(false); unitsCombo.setEnabled(false); scrollPane.setEnabled(false); dataTable.setEnabled(false); tipDateSamplingLabel.setEnabled(false); tipDateSamplingCombo.setEnabled(false); tipDateTaxonSetLabel.setEnabled(false); tipDateTaxonSetCombo.setEnabled(false); specifyOriginDate.setEnabled(false); originDateText.setEnabled(false); originDateLabel.setEnabled(false); usingTipDates.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent ev) { boolean enabled = usingTipDates.isSelected(); clearDatesAction.setEnabled(enabled); guessDatesAction.setEnabled(enabled); importDatesAction.setEnabled(enabled); setDatesAction.setEnabled(enabled); setPrecisionAction.setEnabled(enabled); unitsLabel.setEnabled(enabled); unitsCombo.setEnabled(enabled); directionCombo.setEnabled(enabled); scrollPane.setEnabled(enabled); dataTable.setEnabled(enabled); tipDateSamplingCombo.setEnabled(enabled); tipDateSamplingLabel.setEnabled(enabled); specifyOriginDate.setEnabled(enabled); originDateText.setEnabled(enabled && specifyOriginDate.isSelected()); originDateLabel.setEnabled(enabled && specifyOriginDate.isSelected()); if (options.taxonList != null) timeScaleChanged(); } }); ItemListener listener = new ItemListener() { public void itemStateChanged(ItemEvent ev) { timeScaleChanged(); } }; unitsCombo.addItemListener(listener); directionCombo.addItemListener(listener); } public final void timeScaleChanged() { Units.Type units = Units.Type.YEARS; switch ((DateUnitsType) unitsCombo.getSelectedItem()) { case YEARS: units = Units.Type.YEARS; break; case MONTHS: units = Units.Type.MONTHS; break; case DAYS: units = Units.Type.DAYS; break; } boolean backwards = directionCombo.getSelectedItem() == DateUnitsType.BACKWARDS; for (int i = 0; i < options.taxonList.getTaxonCount(); i++) { Date date = options.taxonList.getTaxon(i).getDate(); double d = date.getTimeValue(); Date newDate = createDate(d, units, backwards, 0.0); newDate.setPrecision(date.getPrecision()); options.taxonList.getTaxon(i).setDate(newDate); } if (specifyOriginDate.isSelected()) { String text = originDateText.getText(); DateGuesser guesser = options.dateGuesser; guessDatesDialog.setupGuesser(guesser); try { options.originDate = guesser.parseDate(text); } catch (GuessDatesException e) { options.originDate = null; } } else { options.originDate = null; } if ( options.originDate != null) { originDateLabel.setText(" date value: " + Double.toString(options.originDate.getTimeValue())); } else { originDateLabel.setText(" unable to parse date"); } calculateHeights(); if (options.clockModelOptions.isTipCalibrated()) { // todo correct? for (PartitionTreeModel treeModel : options.getPartitionTreeModels()) { treeModel.setTipCalibrations(true); } } dataTableModel.fireTableDataChanged(); frame.setDirty(); } private Date createDate(double timeValue, Units.Type units, boolean backwards, double origin) { if (backwards) { return Date.createTimeAgoFromOrigin(timeValue, units, origin); } else { return Date.createTimeSinceOrigin(timeValue, units, origin); } } public void setOptions(BeautiOptions options) { this.options = options; setupTable(); unitsCombo.setSelectedItem(options.datesUnits); directionCombo.setSelectedItem(options.datesDirection); calculateHeights(); usingTipDates.setSelected(options.clockModelOptions.isTipCalibrated()); dataTableModel.fireTableDataChanged(); Object item = tipDateTaxonSetCombo.getSelectedItem(); tipDateTaxonSetCombo.removeAllItems(); tipDateTaxonSetCombo.addItem("All taxa"); for (TaxonList taxa : options.taxonSets) { tipDateTaxonSetCombo.addItem(taxa); } if (item != null) { tipDateTaxonSetCombo.setSelectedItem(item); } } private void setupTable() { dataTableModel.fireTableDataChanged(); } public void getOptions(BeautiOptions options) { options.datesUnits = (DateUnitsType) unitsCombo.getSelectedItem(); options.datesDirection = (DateUnitsType) directionCombo.getSelectedItem(); TipDateSamplingComponentOptions comp = (TipDateSamplingComponentOptions) options.getComponentOptions(TipDateSamplingComponentOptions.class); comp.tipDateSamplingType = (TipDateSamplingType) tipDateSamplingCombo.getSelectedItem(); if (tipDateTaxonSetCombo.getSelectedItem() instanceof TaxonList) { comp.tipDateSamplingTaxonSet = (TaxonList) tipDateTaxonSetCombo.getSelectedItem(); } else { comp.tipDateSamplingTaxonSet = null; } } private void fireModelsChanged() { frame.setDirty(); } public JComponent getExportableComponent() { return dataTable; } public void selectionChanged() { // nothing to do } public void setDates() { if (options.taxonList == null) { // validation of check empty taxonList return; } int result; do { if (dateValueDialog == null) { dateValueDialog = new SetValueDialog(frame, "Set Date for Taxa"); } int[] selRows = dataTable.getSelectedRows(); if (selRows.length == 1) { precisionValueDialog.setDescription("Set date value for selected taxon"); } else if (selRows.length > 1) { dateValueDialog.setDescription("Set date values for selected taxa"); } else { dateValueDialog.setDescription("Set date values for all taxa"); } result = dateValueDialog.showDialog(); if (result == -1 || result == JOptionPane.CANCEL_OPTION) { return; } // currentTrait.guessTrait = true; // ?? no use? String value = dateValueDialog.getValue(); java.util.Date origin = new java.util.Date(0); double d = Double.parseDouble(value); Date date = Date.createTimeSinceOrigin(d, Units.Type.YEARS, origin); if (selRows.length > 0) { for (int row : selRows) { options.taxonList.getTaxon(row).setAttribute("date", date); } } else { for (Taxon taxon : options.taxonList) { taxon.setAttribute("date", date); } } // adjust the dates to the current timescale... timeScaleChanged(); dataTableModel.fireTableDataChanged(); } while (result < 0); } public void setPrecisions() { if (options.taxonList == null) { // validation of check empty taxonList return; } int result; do { if (precisionValueDialog == null) { precisionValueDialog = new SetValueDialog(frame, "Set Precision for Taxa"); } int[] selRows = dataTable.getSelectedRows(); if (selRows.length == 1) { precisionValueDialog.setDescription("Set precision value for selected taxon"); } else if (selRows.length > 1) { precisionValueDialog.setDescription("Set precision values for selected taxa"); } else { precisionValueDialog.setDescription("Set precision values for all taxa"); } result = precisionValueDialog.showDialog(); if (result == -1 || result == JOptionPane.CANCEL_OPTION) { return; } double value; try { value = Double.parseDouble(precisionValueDialog.getValue()); } catch (NumberFormatException nfe) { value = 0.0; } if (value < 0.0 || Double.isNaN(value) || Double.isInfinite(value)) { value = 0.0; } if (selRows.length > 0) { for (int row : selRows) { options.taxonList.getTaxon(row).getDate().setPrecision(value); } } else { for (Taxon taxon : options.taxonList) { taxon.setAttribute("precision", value); } } dataTableModel.fireTableDataChanged(); } while (result < 0); } public void clearDates() { for (int i = 0; i < options.taxonList.getTaxonCount(); i++) { java.util.Date origin = new java.util.Date(0); double d = 0.0; Date date = Date.createTimeSinceOrigin(d, Units.Type.YEARS, origin); options.taxonList.getTaxon(i).setAttribute("date", date); } // adjust the dates to the current timescale... timeScaleChanged(); dataTableModel.fireTableDataChanged(); } public void guessDates() { if (guessDatesDialog == null) { guessDatesDialog = new GuessDatesDialog(frame); } int[] selRows = dataTable.getSelectedRows(); if (selRows.length > 0) { guessDatesDialog.setDescription("Guess date values for selected taxa"); } else { guessDatesDialog.setDescription("Guess date values for all taxa"); } int result = guessDatesDialog.showDialog(); if (result == -1 || result == JOptionPane.CANCEL_OPTION) { return; } DateGuesser guesser = options.dateGuesser; guesser.guessDates = true; guessDatesDialog.setupGuesser(guesser); String warningMessage = null; if (selRows.length > 0) { Taxa selectedTaxa = new Taxa(); for (int row : selRows) { Taxon taxon = (Taxon) dataTable.getValueAt(row, 0); selectedTaxa.addTaxon(taxon); } guesser.guessDates(selectedTaxa); } else { guesser.guessDates(options.taxonList); } if (warningMessage != null) { JOptionPane.showMessageDialog(this, "Warning: some dates may not be set correctly - \n" + warningMessage, "Error guessing dates", JOptionPane.WARNING_MESSAGE); } // adjust the dates to the current timescale... timeScaleChanged(); dataTableModel.fireTableDataChanged(); } public void importDates() { File[] files = frame.selectImportFiles("Import Dates File...", false, new FileNameExtensionFilter[]{ new FileNameExtensionFilter("Tab-delimited text files", "txt", "tab", "dat")}); DataTable<String[]> dataTable; if (files != null && files.length != 0) { try { // Load the file as a table dataTable = DataTable.Text.parse(new FileReader(files[0])); } catch (FileNotFoundException fnfe) { JOptionPane.showMessageDialog(this, "Unable to open file: File not found", "Unable to open file", JOptionPane.ERROR_MESSAGE); return; } catch (IOException ioe) { JOptionPane.showMessageDialog(this, "Unable to read file: " + ioe.getMessage(), "Unable to read file", JOptionPane.ERROR_MESSAGE); return; } catch (Exception ex) { ex.printStackTrace(System.err); JOptionPane.showMessageDialog(this, "Fatal exception: " + ex, "Error reading file", JOptionPane.ERROR_MESSAGE); ex.printStackTrace(); return; } } else { return; } if (dataTable.getColumnCount() == 0) { // expecting at least 2 columns - labels and dates JOptionPane.showMessageDialog(frame, "Expecting a tab delimited file with at\n" + "least 2 columns (taxon labels and dates).", "Incompatible values", JOptionPane.ERROR_MESSAGE); return; } String[] columnLabels = dataTable.getColumnLabels(); String[] taxonNames = dataTable.getRowLabels(); // assume the first column contains the dates int dateColumn = 0; if (columnLabels.length > 1) { List<Integer> dateColumns = new ArrayList<Integer>(); // see if there is a column labelled 'dates' or something for (int i = 0; i < dataTable.getColumnCount(); i++) { if (columnLabels[i].toLowerCase().contains("date")) { dateColumns.add(i); } } if (dateColumns.size() > 0) { // if there are multiple date column possibilities, take the first // @todo - allow the user to select the column to use dateColumn = dateColumns.get(0); } } Map<Taxon, String> taxonDateMap = new HashMap<Taxon, String>(); int matchCount = 0; int mismatchCount = 0; String[] values = dataTable.getColumn(dateColumn); int j = 0; for (final String taxonName : taxonNames) { final int index = options.taxonList.getTaxonIndex(taxonName); if (index >= 0) { taxonDateMap.put(options.taxonList.getTaxon(index), values[j]); matchCount ++; } else { mismatchCount ++; } j++; } if (guessDatesDialog == null) { guessDatesDialog = new GuessDatesDialog(frame); } guessDatesDialog.setDescription("Parse date values from file"); int result = guessDatesDialog.showDialog(true); if (result == -1 || result == JOptionPane.CANCEL_OPTION) { return; } DateGuesser guesser = options.dateGuesser; guesser.guessDates = true; guessDatesDialog.setupGuesser(guesser); String warningMessage = null; guesser.guessDates(options.taxonList, taxonDateMap); if (warningMessage != null) { JOptionPane.showMessageDialog(this, "Warning: some dates may not be set correctly - \n" + warningMessage, "Error guessing dates", JOptionPane.WARNING_MESSAGE); } // adjust the dates to the current timescale... timeScaleChanged(); dataTableModel.fireTableDataChanged(); } public boolean isMissingValue(String value) { return (value.equals("?") || value.equals("NA") || value.length() == 0); } public class SetDatesAction extends AbstractAction { /** * */ private static final long serialVersionUID = -7281309694753868635L; public SetDatesAction() { super("Set Dates"); setToolTipText("Use this tool to set sampling date values for the selected taxa"); } public void actionPerformed(ActionEvent ae) { setDates(); } } public class ClearDatesAction extends AbstractAction { /** * */ private static final long serialVersionUID = -7281309694753868635L; public ClearDatesAction() { super("Clear Dates"); setToolTipText("Use this tool to remove sampling dates from each taxon"); } public void actionPerformed(ActionEvent ae) { clearDates(); } } public class GuessDatesAction extends AbstractAction { /** * */ private static final long serialVersionUID = 8514706149822252033L; public GuessDatesAction() { super("Guess Dates"); setToolTipText("Use this tool to guess the sampling dates from the taxon labels"); } public void actionPerformed(ActionEvent ae) { guessDates(); } } public class ImportDatesAction extends AbstractAction { /** * */ private static final long serialVersionUID = 8514706149822252033L; public ImportDatesAction() { super("Import Dates"); setToolTipText("Use this tool to import the sampling dates from a file"); } public void actionPerformed(ActionEvent ae) { importDates(); } } public class SetPrecisionAction extends AbstractAction { /** * */ private static final long serialVersionUID = -7281309694753868639L; public SetPrecisionAction() { super("Set Precision"); setToolTipText("Use this tool to set precision values for the selected taxa"); } public void actionPerformed(ActionEvent ae) { setPrecisions(); } } private void calculateHeights() { options.maximumTipHeight = 0.0; if (options.taxonList == null || options.taxonList.getTaxonCount() == 0) return; heights = null; dr.evolution.util.Date mostRecent = options.originDate; for (int i = 0; i < options.taxonList.getTaxonCount(); i++) { Date date = options.taxonList.getTaxon(i).getDate(); if ((date != null) && (mostRecent == null || date.after(mostRecent))) { mostRecent = date; } } if (mostRecent != null) { heights = new double[options.taxonList.getTaxonCount()]; TimeScale timeScale = new TimeScale(mostRecent.getUnits(), true, mostRecent.getAbsoluteTimeValue()); double time0 = timeScale.convertTime(mostRecent.getTimeValue(), mostRecent); for (int i = 0; i < options.taxonList.getTaxonCount(); i++) { Taxon taxon = options.taxonList.getTaxon(i); Date date = taxon.getDate(); if (date != null) { heights[i] = timeScale.convertTime(date.getTimeValue(), date) - time0; taxon.setAttribute("height", heights[i]); if (heights[i] > options.maximumTipHeight) options.maximumTipHeight = heights[i]; } } } frame.setStatusMessage(); } class DataTableModel extends AbstractTableModel { /** * */ private static final long serialVersionUID = -6707994233020715574L; String[] columnNames = {"Name", "Date", "Precision", "Height"}; public DataTableModel() { } public int getColumnCount() { return columnNames.length; } public int getRowCount() { if (options == null) return 0; if (options.taxonList == null) return 0; return options.taxonList.getTaxonCount(); } public Object getValueAt(int row, int col) { Date date = options.taxonList.getTaxon(row).getDate(); switch (col) { case 0: return options.taxonList.getTaxon(row); case 1: if (date != null) { return date.getTimeValue(); } else { return "-"; } case 2: if (date != null) { return date.getPrecision(); } else { return "-"; } case 3: if (heights != null) { return heights[row]; } else { return "0.0"; } } return null; } public void setValueAt(Object aValue, int row, int col) { if (col == 0) { options.taxonList.getTaxon(row).setId(aValue.toString()); } else if (col == 1) { Date date = options.taxonList.getTaxon(row).getDate(); if (date != null) { double d = (Double) aValue; Date newDate = createDate(d, date.getUnits(), date.isBackwards(), date.getOrigin()); options.taxonList.getTaxon(row).setDate(newDate); } } else if (col == 2) { Date date = options.taxonList.getTaxon(row).getDate(); if (date != null) { double d = (Double) aValue; if (d >= 0.0) { date.setPrecision(d); } } } timeScaleChanged(); } public boolean isCellEditable(int row, int col) { if (col == 0) return false; if (col == 1 || col == 2) { Date date = options.taxonList.getTaxon(row).getDate(); return (date != null); } return false; } 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(); } } }