/* * DataPanel.java * * Copyright (C) 2002-2006 Alexei Drummond and Andrew Rambaut * * 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.oldbeauti; import dr.app.gui.components.RealNumberField; import dr.evolution.alignment.ConvertAlignment; import dr.evolution.datatype.*; import dr.evolution.util.*; import dr.app.gui.table.*; import jam.framework.Exportable; import jam.panels.OptionsPanel; import jam.table.*; import javax.swing.*; import javax.swing.border.EmptyBorder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.plaf.BorderUIResource; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableModel; import java.awt.*; import java.awt.event.*; import java.util.Calendar; import java.util.GregorianCalendar; /** * @author Andrew Rambaut * @author Alexei Drummond * @version $Id: DataPanel.java,v 1.17 2006/09/05 13:29:34 rambaut Exp $ */ public class DataPanel extends JPanel implements Exportable { /** * */ private static final long serialVersionUID = 5283922195494563924L; JScrollPane scrollPane = new JScrollPane(); JTable dataTable = null; DataTableModel dataTableModel = null; ClearDatesAction clearDatesAction = new ClearDatesAction(); GuessDatesAction guessDatesAction = new GuessDatesAction(); JComboBox unitsCombo = new JComboBox(new String[] {"Years", "Months", "Days"}); JComboBox directionCombo = new JComboBox(new String[] {"Since some time in the past", "Before the present"}); //RealNumberField originField = new RealNumberField(0.0, Double.POSITIVE_INFINITY); JComboBox translationCombo = new JComboBox(); TableRenderer sequenceRenderer = null; BeautiFrame frame = null; BeautiOptions options = null; double[] heights = null; public DataPanel(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.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); TableEditorStopper.ensureEditingStopWhenTableLosesFocus(dataTable); sequenceRenderer = new TableRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4)); sequenceRenderer.setFont(new Font("Courier", Font.PLAIN, 12)); 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); clearDatesAction.setEnabled(false); guessDatesAction.setEnabled(false); setupComponent(unitsCombo); unitsCombo.setEnabled(false); setupComponent(directionCombo); directionCombo.setEnabled(false); //originField.setEnabled(false); //originField.setValue(0.0); //originField.setColumns(12); JToolBar toolBar1 = new JToolBar(); toolBar1.setFloatable(false); toolBar1.setOpaque(false); // toolBar1.setLayout(new BoxLayout(toolBar1, javax.swing.BoxLayout.X_AXIS)); toolBar1.setLayout(new FlowLayout(java.awt.FlowLayout.LEFT, 0, 0)); JButton button = new JButton(clearDatesAction); setupComponent(button); toolBar1.add(button); button = new JButton(guessDatesAction); setupComponent(button); toolBar1.add(button); toolBar1.add(new JToolBar.Separator(new Dimension(12, 12))); toolBar1.add(new JLabel("Dates specified as ")); toolBar1.add(unitsCombo); toolBar1.add(directionCombo); //toolBar.add(originField); translationCombo.setOpaque(false); setupComponent(translationCombo); translationCombo.addItem("None"); for (int i = 0; i < GeneticCode.GENETIC_CODE_DESCRIPTIONS.length; i++) { translationCombo.addItem(GeneticCode.GENETIC_CODE_DESCRIPTIONS[i]); } translationCombo.setEnabled(false); translationCombo.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { translationChanged(); } }); JToolBar toolBar2 = new JToolBar(); toolBar2.setOpaque(false); toolBar2.setFloatable(false); toolBar2.setLayout(new FlowLayout(java.awt.FlowLayout.LEFT, 0, 0)); toolBar2.add(new JLabel("Translation:")); toolBar2.add(translationCombo); setOpaque(false); setBorder(new BorderUIResource.EmptyBorderUIResource(new java.awt.Insets(12, 12, 12, 12))); setLayout(new BorderLayout(0,0)); add(toolBar1, "North"); add(scrollPane, "Center"); add(toolBar2, "South"); ItemListener listener = new ItemListener() { public void itemStateChanged(ItemEvent ev) { timeScaleChanged(); } }; unitsCombo.addItemListener(listener); directionCombo.addItemListener(listener); //originField.addKeyListener(new java.awt.event.KeyAdapter() { // public void keyTyped(java.awt.event.KeyEvent ev) { // timeScaleChanged(); // }}); } private void setupComponent(JComponent comp) { comp.setOpaque(false); //comp.setFont(UIManager.getFont("SmallSystemFont")); //comp.putClientProperty("JComponent.sizeVariant", "small"); if (comp instanceof JButton) { comp.putClientProperty("JButton.buttonType", "roundRect"); } if (comp instanceof JComboBox) { comp.putClientProperty("JComboBox.isSquare", Boolean.TRUE); } } public final void dataChanged() { calculateHeights(); frame.dataChanged(); } public final void timeScaleChanged() { Units.Type units = Units.Type.YEARS; switch (unitsCombo.getSelectedIndex()) { case 0: units = Units.Type.YEARS; break; case 1: units = Units.Type.MONTHS; break; case 2: units = Units.Type.DAYS; break; } boolean backwards = directionCombo.getSelectedIndex() == 1; //double origin = originField.getValue().doubleValue(); 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); options.taxonList.getTaxon(i).setDate(newDate); } calculateHeights(); dataTableModel.fireTableDataChanged(); frame.dataChanged(); } 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 final void translationChanged() { int index = translationCombo.getSelectedIndex() - 1; if (index < 0) { options.alignment = options.originalAlignment; } else { options.alignment = new ConvertAlignment(AminoAcids.INSTANCE, GeneticCode.GENETIC_CODES[index], options.originalAlignment); } setupTable(); frame.dataChanged(); } public void setOptions(BeautiOptions options) { this.options = options; if (options.taxonList != null) { clearDatesAction.setEnabled(true); guessDatesAction.setEnabled(true); unitsCombo.setEnabled(true); directionCombo.setEnabled(true); //originField.setEnabled(true); if (options.originalAlignment != null && options.originalAlignment.getDataType() == Nucleotides.INSTANCE) { translationCombo.setEnabled(true); translationCombo.setSelectedIndex(options.translation); } else { translationCombo.setEnabled(false); translationCombo.setSelectedIndex(0); } } setupTable(); unitsCombo.setSelectedIndex(options.datesUnits); directionCombo.setSelectedIndex(options.datesDirection); calculateHeights(); dataTableModel.fireTableDataChanged(); } private void setupTable() { dataTableModel.fireTableStructureChanged(); if (options.alignment != null) { dataTable.getColumnModel().getColumn(3).setCellRenderer(sequenceRenderer); dataTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_OFF); sequenceRenderer.setText(options.alignment.getSequence(0).getSequenceString()); int w = sequenceRenderer.getPreferredSize().width + 8; dataTable.getColumnModel().getColumn(3).setPreferredWidth(w); } } public void getOptions(BeautiOptions options) { options.datesUnits = unitsCombo.getSelectedIndex(); options.datesDirection = directionCombo.getSelectedIndex(); options.translation = translationCombo.getSelectedIndex(); } public JComponent getExportableComponent() { return dataTable; } public void selectionChanged() { int[] selRows = dataTable.getSelectedRows(); if (selRows == null || selRows.length == 0) { frame.dataSelectionChanged(false); } else { frame.dataSelectionChanged(true); } } public void deleteSelection() { int option = JOptionPane.showConfirmDialog(this, "Are you sure you wish to delete\n"+ "the selected taxa?\n"+ "This operation cannot be undone.", "Warning", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); if (option == JOptionPane.YES_OPTION) { int[] selRows = dataTable.getSelectedRows(); String[] names = new String[selRows.length]; TableModel model = dataTable.getModel(); for (int i = 0; i < names.length; i++) { names[i] = (String)model.getValueAt(selRows[i], 0); } for (int i = 0; i < names.length; i++) { if (options.originalAlignment != null) { int index = options.originalAlignment.getTaxonIndex(names[i]); options.originalAlignment.removeSequence(index); } else { // there is no alignment so options.taxonList must be a Taxa object: int index = options.taxonList.getTaxonIndex(names[i]); ((Taxa)options.taxonList).removeTaxon(options.taxonList.getTaxon(index)); } } if (options.taxonList.getTaxonCount() == 0) { // if all the sequences are deleted we may as well throw // away the alignment... options.originalAlignment = null; options.alignment = null; options.taxonList = null; } dataTableModel.fireTableDataChanged(); frame.dataChanged(); } } 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(); frame.dataChanged(); } public void guessDates() { OptionsPanel optionPanel = new OptionsPanel(12, 12); optionPanel.addLabel("The date is given by a numerical field in the taxon label that is:"); final JRadioButton orderRadio = new JRadioButton("Defined by its order", true); final JComboBox orderCombo = new JComboBox(new String[] {"first", "second", "third", "fourth", "fourth from last", "third from last", "second from last", "last"}); optionPanel.addComponents(orderRadio, orderCombo); optionPanel.addSeparator(); final JRadioButton prefixRadio = new JRadioButton("Defined by a prefix", false); final JTextField prefixText = new JTextField(16); prefixText.setEnabled(false); optionPanel.addComponents(prefixRadio, prefixText); optionPanel.addSeparator(); final JCheckBox offsetCheck = new JCheckBox("Add the following value to each: ", false); final RealNumberField offsetText = new RealNumberField(); offsetText.setValue(1900); offsetText.setColumns(16); offsetText.setEnabled(false); offsetCheck.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { offsetText.setEnabled(offsetCheck.isSelected()); } }); optionPanel.addComponents(offsetCheck, offsetText); final JCheckBox unlessCheck = new JCheckBox("...unless less than:", false); final RealNumberField unlessText = new RealNumberField(); Calendar calendar = GregorianCalendar.getInstance(); int year = calendar.get(Calendar.YEAR) - 1999; unlessText.setValue(year); unlessText.setColumns(16); unlessText.setEnabled(false); optionPanel.addComponents(unlessCheck, unlessText); final RealNumberField offset2Text = new RealNumberField(); offset2Text.setValue(2000); offset2Text.setColumns(16); offset2Text.setEnabled(false); optionPanel.addComponentWithLabel("...in which case add:", offset2Text); unlessCheck.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { unlessText.setEnabled(unlessCheck.isSelected()); offset2Text.setEnabled(unlessCheck.isSelected()); } }); ButtonGroup group = new ButtonGroup(); group.add(orderRadio); group.add(prefixRadio); ItemListener listener = new ItemListener() { public void itemStateChanged(ItemEvent e) { orderCombo.setEnabled(orderRadio.isSelected()); prefixText.setEnabled(prefixRadio.isSelected()); } }; orderRadio.addItemListener(listener); prefixRadio.addItemListener(listener); 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, "Guess Dates"); // dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); dialog.setVisible(true); if (optionPane.getValue() == null) { return; } int value = ((Integer)optionPane.getValue()).intValue(); if (value == -1 || value == JOptionPane.CANCEL_OPTION) { return; } options.guessDates = true; String warningMessage = null; for (int i = 0; i < options.taxonList.getTaxonCount(); i++) { java.util.Date origin = new java.util.Date(0); double d = 0.0; try { if (orderRadio.isSelected()) { options.guessDateFromOrder = true; options.order = orderCombo.getSelectedIndex(); options.fromLast = false; if (options.order > 3) { options.fromLast = true; options.order = 8 - options.order - 1; } d = options.guessDateFromOrder(options.taxonList.getTaxonId(i), options.order, options.fromLast); } else { options.guessDateFromOrder = false; options.prefix = prefixText.getText(); d = options.guessDateFromPrefix(options.taxonList.getTaxonId(i), options.prefix); } } catch (GuessDatesException gfe) { warningMessage = gfe.getMessage(); } options.offset = 0.0; options.unlessLessThan = 0.0; if (offsetCheck.isSelected()) { options.offset = offsetText.getValue().doubleValue(); if (unlessCheck.isSelected()) { options.unlessLessThan = unlessText.getValue().doubleValue(); options.offset2 = offset2Text.getValue().doubleValue(); if (d < options.unlessLessThan) { d += options.offset2; } else { d += options.offset; } } else { d += options.offset; } } Date date = Date.createTimeSinceOrigin(d, Units.Type.YEARS, origin); options.taxonList.getTaxon(i).setAttribute("date", date); } 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(); frame.dataChanged(); } 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(); } }; private void calculateHeights() { options.maximumTipHeight = 0.0; if (options.alignment == null) return; heights = null; dr.evolution.util.Date mostRecent = null; 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++) { Date date = options.taxonList.getTaxon(i).getDate(); if (date != null) { heights[i] = timeScale.convertTime(date.getTimeValue(), date) - time0; if (heights[i] > options.maximumTipHeight) options.maximumTipHeight = heights[i]; } } } } class DataTableModel extends AbstractTableModel { /** * */ private static final long serialVersionUID = -6707994233020715574L; String[] columnNames1 = { "Name", "Date", "Height", "Sequence" }; String[] columnNames2 = { "Name", "Date", "Height" }; public DataTableModel() { } public int getColumnCount() { if (options != null && options.alignment != null) { return columnNames1.length; } else { return columnNames2.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) { switch (col) { case 0: return options.taxonList.getTaxonId(row); case 1: Date date = options.taxonList.getTaxon(row).getDate(); if (date != null) { return new Double(date.getTimeValue()); } else { return "-"; } case 2: if (heights != null) { return new Double(heights[row]); } else { return "0.0"; } case 3: return options.alignment.getAlignedSequenceString(row); } 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).doubleValue(); Date newDate = createDate(d, date.getUnits(), date.isBackwards(), date.getOrigin()); options.taxonList.getTaxon(row).setDate(newDate); } } dataChanged(); } public boolean isCellEditable(int row, int col) { if (col == 0) return true; if (col == 1) { Date date = options.taxonList.getTaxon(row).getDate(); return (date != null); } return false; } public String getColumnName(int column) { return columnNames1[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(); } }; }