/* * BeautiFrame.java * * (c) 2002-2005 BEAST Development Core Team * * This package may be distributed under the * Lesser Gnu Public Licence (LGPL) */ package dr.app.oldbeauti; import dr.evolution.alignment.Patterns; import dr.evolution.alignment.SimpleAlignment; import dr.evolution.distance.DistanceMatrix; import dr.evolution.distance.JukesCantorDistanceMatrix; import dr.evolution.io.Importer; import dr.evolution.io.NexusImporter; import dr.evolution.tree.Tree; import dr.evolution.util.Units; import org.jdom.Document; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; import org.jdom.output.XMLOutputter; import jam.framework.DocumentFrame; import jam.framework.Exportable; import jam.util.IconUtils; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.plaf.BorderUIResource; import java.awt.*; import java.awt.event.ActionEvent; import java.io.*; /** * @author Andrew Rambaut * @author Alexei Drummond * @version $Id: BeautiFrame.java,v 1.22 2006/09/09 16:07:06 rambaut Exp $ */ public class BeautiFrame extends DocumentFrame { /** * */ private static final long serialVersionUID = 2114148696789612509L; private final BeastGenerator beautiOptions = new BeastGenerator(); private final JTabbedPane tabbedPane = new JTabbedPane(); private final JLabel statusLabel = new JLabel("No data loaded"); private DataPanel dataPanel; private TaxaPanel taxaPanel; private ModelPanel modelPanel; private PriorsPanel priorsPanel; private OperatorsPanel operatorsPanel; private MCMCPanel mcmcPanel; // final Icon dataIcon = new ImageIcon(Utils.getImage(this, "/images/data-icon.gif")); // final Icon modelIcon = new ImageIcon(Utils.getImage(this, "/images/model-icon.gif")); // final Icon mcmcIcon = new ImageIcon(Utils.getImage(this, "/images/mcmc-icon.gif")); final Icon gearIcon = IconUtils.getIcon(this.getClass(), "images/gear.png"); public BeautiFrame(String title) { super(); setTitle(title); // Prevent the application to close in requestClose() // after a user cancel or a failure in beast file generation setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); getOpenAction().setEnabled(false); getSaveAction().setEnabled(false); getFindAction().setEnabled(false); getZoomWindowAction().setEnabled(false); } public void initializeComponents() { dataPanel = new DataPanel(this); taxaPanel = new TaxaPanel(this); modelPanel = new ModelPanel(this); priorsPanel = new PriorsPanel(this); operatorsPanel = new OperatorsPanel(this); mcmcPanel = new MCMCPanel(this); tabbedPane.addTab("Data", dataPanel); tabbedPane.addTab("Taxa", taxaPanel); tabbedPane.addTab("Model", modelPanel); tabbedPane.addTab("Priors", priorsPanel); tabbedPane.addTab("Operators", operatorsPanel); tabbedPane.addTab("MCMC", mcmcPanel); tabbedPane.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { if (tabbedPane.getSelectedComponent() == dataPanel) { dataPanel.selectionChanged(); } else { getDeleteAction().setEnabled(false); } } }); JPanel panel = new JPanel(new BorderLayout(6, 6)); panel.setBorder(new BorderUIResource.EmptyBorderUIResource(new java.awt.Insets(12, 12, 12, 12))); panel.add(tabbedPane, BorderLayout.CENTER); getExportAction().setEnabled(false); JButton generateButton = new JButton(getExportAction()); generateButton.putClientProperty("JButton.buttonType", "roundRect"); JPanel panel2 = new JPanel(new BorderLayout(6, 6)); panel2.add(statusLabel, BorderLayout.CENTER); panel2.add(generateButton, BorderLayout.EAST); panel.add(panel2, BorderLayout.SOUTH); getContentPane().setLayout(new java.awt.BorderLayout(0, 0)); getContentPane().add(panel, BorderLayout.CENTER); dataPanel.setOptions(beautiOptions); taxaPanel.setOptions(beautiOptions); modelPanel.setOptions(beautiOptions); priorsPanel.setOptions(beautiOptions); operatorsPanel.setOptions(beautiOptions); mcmcPanel.setOptions(beautiOptions); setSize(new java.awt.Dimension(800, 600)); } public final void dataChanged() { taxaPanel.setOptions(beautiOptions); modelPanel.setOptions(beautiOptions); priorsPanel.setOptions(beautiOptions); operatorsPanel.setOptions(beautiOptions); setDirty(); } public final void dataSelectionChanged(boolean isSelected) { if (isSelected) { getDeleteAction().setEnabled(true); } else { getDeleteAction().setEnabled(false); } } public void taxonSetsChanged() { priorsPanel.setOptions(beautiOptions); setDirty(); } public void doDelete() { if (tabbedPane.getSelectedComponent() == dataPanel) { dataPanel.deleteSelection(); } else { throw new RuntimeException("Delete should only be accessable from the Data panel"); } } public final void modelChanged() { modelPanel.getOptions(beautiOptions); priorsPanel.setOptions(beautiOptions); operatorsPanel.setOptions(beautiOptions); setDirty(); } public final void operatorsChanged() { setDirty(); } public final void priorsChanged() { priorsPanel.getOptions(beautiOptions); operatorsPanel.setOptions(beautiOptions); priorsPanel.setOptions(beautiOptions); setDirty(); } public final void mcmcChanged() { setDirty(); } public boolean requestClose() { if (isDirty()) { int option = JOptionPane.showConfirmDialog(this, "You have made changes but have not generated\n" + "a BEAST XML file. Do you wish to generate\n" + "before closing this window?", "Unused changes", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE); if (option == JOptionPane.YES_OPTION) { return !doGenerate(); } else if (option == JOptionPane.CANCEL_OPTION || option == JOptionPane.DEFAULT_OPTION) { return false; } return true; } return true; } public void doApplyTemplate() { FileDialog dialog = new FileDialog(this, "Apply Template", FileDialog.LOAD); dialog.setVisible(true); if (dialog.getFile() != null) { File file = new File(dialog.getDirectory(), dialog.getFile()); try { readFromFile(file); } catch (FileNotFoundException fnfe) { JOptionPane.showMessageDialog(this, "Unable to open template file: File not found", "Unable to open file", JOptionPane.ERROR_MESSAGE); } catch (IOException ioe) { JOptionPane.showMessageDialog(this, "Unable to read template file: " + ioe, "Unable to read file", JOptionPane.ERROR_MESSAGE); } } } protected boolean readFromFile(File file) throws IOException { try { SAXBuilder parser = new SAXBuilder(); Document doc = parser.build(file); beautiOptions.parse(doc); if (beautiOptions.guessDates) { beautiOptions.guessDates(); } dataPanel.setOptions(beautiOptions); taxaPanel.setOptions(beautiOptions); modelPanel.setOptions(beautiOptions); priorsPanel.setOptions(beautiOptions); operatorsPanel.setOptions(beautiOptions); mcmcPanel.setOptions(beautiOptions); getExportAction().setEnabled(beautiOptions.alignment != null); getSaveAction().setEnabled(beautiOptions.alignment != null); getSaveAsAction().setEnabled(beautiOptions.alignment != null); } catch (dr.xml.XMLParseException xpe) { JOptionPane.showMessageDialog(this, "Error reading file: This may not be a BEAUti file", "Error reading file", JOptionPane.ERROR_MESSAGE); return false; } catch (JDOMException e) { JOptionPane.showMessageDialog(this, "Unable to open file: This may not be a BEAUti file", "Unable to open file", JOptionPane.ERROR_MESSAGE); return false; } return true; } public String getDefaultFileName() { return beautiOptions.fileNameStem+".beauti"; } protected boolean writeToFile(File file) throws IOException { dataPanel.getOptions(beautiOptions); taxaPanel.getOptions(beautiOptions); modelPanel.getOptions(beautiOptions); priorsPanel.getOptions(beautiOptions); operatorsPanel.getOptions(beautiOptions); mcmcPanel.getOptions(beautiOptions); Document doc = beautiOptions.create(false, true); FileWriter fw = new FileWriter(file); XMLOutputter outputter = new XMLOutputter(org.jdom.output.Format.getPrettyFormat()); outputter.output(doc, fw); fw.close(); return true; } public final void doImport() { FileDialog dialog = new FileDialog(this, "Import NEXUS File...", FileDialog.LOAD); dialog.setVisible(true); if (dialog.getFile() != null) { File file = new File(dialog.getDirectory(), dialog.getFile()); try { importFromFile(file); setDirty(); } catch (FileNotFoundException fnfe) { JOptionPane.showMessageDialog(this, "Unable to open file: File not found", "Unable to open file", JOptionPane.ERROR_MESSAGE); } catch (IOException ioe) { JOptionPane.showMessageDialog(this, "Unable to read file: " + ioe, "Unable to read file", JOptionPane.ERROR_MESSAGE); } } } protected void importFromFile(File file) throws IOException { try { FileReader reader = new FileReader(file); NexusApplicationImporter importer = new NexusApplicationImporter(reader); boolean done = false; beautiOptions.originalAlignment = null; beautiOptions.alignment = null; beautiOptions.tree = null; beautiOptions.taxonList = null; while (!done) { try { NexusImporter.NexusBlock block = importer.findNextBlock(); if (block == NexusImporter.TAXA_BLOCK) { if (beautiOptions.taxonList != null) { throw new NexusImporter.MissingBlockException("TAXA block already defined"); } beautiOptions.taxonList = importer.parseTaxaBlock(); } else if (block == NexusImporter.CALIBRATION_BLOCK) { if (beautiOptions.taxonList == null) { throw new NexusImporter.MissingBlockException("TAXA or DATA block must be defined before a CALIBRATION block"); } importer.parseCalibrationBlock(beautiOptions.taxonList); } else if (block == NexusImporter.CHARACTERS_BLOCK) { if (beautiOptions.taxonList == null) { throw new NexusImporter.MissingBlockException("TAXA block must be defined before a CHARACTERS block"); } if (beautiOptions.originalAlignment != null) { throw new NexusImporter.MissingBlockException("CHARACTERS or DATA block already defined"); } beautiOptions.originalAlignment = (SimpleAlignment)importer.parseCharactersBlock(beautiOptions.taxonList); } else if (block == NexusImporter.DATA_BLOCK) { if (beautiOptions.originalAlignment != null) { throw new NexusImporter.MissingBlockException("CHARACTERS or DATA block already defined"); } // A data block doesn't need a taxon block before it // but if one exists then it will use it. beautiOptions.originalAlignment = (SimpleAlignment)importer.parseDataBlock(beautiOptions.taxonList); if (beautiOptions.taxonList == null) { beautiOptions.taxonList = beautiOptions.originalAlignment; } } else if (block == NexusImporter.TREES_BLOCK) { if (beautiOptions.taxonList == null) { throw new NexusImporter.MissingBlockException("TAXA or DATA block must be defined before a TREES block"); } if (beautiOptions.tree != null) { throw new NexusImporter.MissingBlockException("TREES block already defined"); } Tree[] trees = importer.parseTreesBlock(beautiOptions.taxonList); if (trees.length > 0) { beautiOptions.tree = trees[0]; } /* } else if (block == NexusApplicationImporter.PAUP_BLOCK) { importer.parsePAUPBlock(beautiOptions); } else if (block == NexusApplicationImporter.MRBAYES_BLOCK) { importer.parseMrBayesBlock(beautiOptions); } else if (block == NexusApplicationImporter.RHINO_BLOCK) { importer.parseRhinoBlock(beautiOptions); */ } else { // Ignore the block.. } } catch (EOFException ex) { done = true; } } // Allow the user to load taxa only (perhaps from a tree file) so that they can sample from a prior... if (beautiOptions.originalAlignment == null && beautiOptions.taxonList == null) { throw new NexusImporter.MissingBlockException("TAXON, DATA or CHARACTERS block is missing"); } } catch (Importer.ImportException ime) { JOptionPane.showMessageDialog(this, "Error parsing imported file: " + ime, "Error reading file", JOptionPane.ERROR_MESSAGE); return; } catch (IOException ioex) { JOptionPane.showMessageDialog(this, "File I/O Error: " + ioex, "File I/O Error", JOptionPane.ERROR_MESSAGE); return; } catch (Exception ex) { JOptionPane.showMessageDialog(this, "Fatal exception: " + ex, "Error reading file", JOptionPane.ERROR_MESSAGE); return; } // check the taxon names for invalid characters boolean foundAmp = false; for (int i = 0; i < beautiOptions.taxonList.getTaxonCount(); i++) { String name = beautiOptions.taxonList.getTaxon(i).getId(); if (name.indexOf('&') >= 0) { foundAmp = true; } } if (foundAmp) { JOptionPane.showMessageDialog(this, "One or more taxon names include an illegal character ('&').\n" + "These characters will prevent BEAST from reading the resulting XML file.\n\n" + "Please edit the taxon name(s) before generating the BEAST file.", "Illegal Taxon Name(s)", JOptionPane.WARNING_MESSAGE); } // make sure they all have dates... for (int i = 0; i < beautiOptions.taxonList.getTaxonCount(); i++) { if (beautiOptions.taxonList.getTaxonAttribute(i, "date") == null) { java.util.Date origin = new java.util.Date(0); dr.evolution.util.Date date = dr.evolution.util.Date.createTimeSinceOrigin(0.0, Units.Type.YEARS, origin); beautiOptions.taxonList.getTaxon(i).setAttribute("date", date); } } beautiOptions.fileNameStem = dr.app.util.Utils.trimExtensions(file.getName(), new String[] {"nex", "NEX", "tre", "TRE", "nexus", "NEXUS"}); beautiOptions.alignment = beautiOptions.originalAlignment; beautiOptions.alignmentReset = true; if (beautiOptions.alignment != null) { Patterns patterns = new Patterns(beautiOptions.alignment); DistanceMatrix distances = new JukesCantorDistanceMatrix(patterns); beautiOptions.meanDistance = distances.getMeanDistance(); statusLabel.setText("Alignment: " + beautiOptions.alignment.getTaxonCount() + " taxa, " + beautiOptions.alignment.getSiteCount() + " sites"); beautiOptions.dataType = beautiOptions.alignment.getDataType().getType(); } else { statusLabel.setText("Taxa only: " + beautiOptions.taxonList.getTaxonCount() + " taxa"); beautiOptions.meanDistance = 0.0; } dataPanel.setOptions(beautiOptions); taxaPanel.setOptions(beautiOptions); modelPanel.setOptions(beautiOptions); priorsPanel.setOptions(beautiOptions); operatorsPanel.setOptions(beautiOptions); mcmcPanel.setOptions(beautiOptions); getOpenAction().setEnabled(true); getSaveAction().setEnabled(true); getExportAction().setEnabled(true); } public final boolean doGenerate() { try { beautiOptions.checkOptions(); } catch(IllegalArgumentException iae) { JOptionPane.showMessageDialog(this, iae.getMessage(), "Unable to generate file", JOptionPane.ERROR_MESSAGE); return false; } FileDialog dialog = new FileDialog(this, "Generate BEAST File...", FileDialog.SAVE); dialog.setVisible(true); dialog.setFile(beautiOptions.fileNameStem + ".xml"); if (dialog.getFile() != null) { File file = new File(dialog.getDirectory(), dialog.getFile()); try { generate(file); } catch (IOException ioe) { JOptionPane.showMessageDialog(this, "Unable to generate file: " + ioe.getMessage(), "Unable to generate file", JOptionPane.ERROR_MESSAGE); return false; } } clearDirty(); return true; } protected void generate(File file) throws IOException { dataPanel.getOptions(beautiOptions); taxaPanel.getOptions(beautiOptions); modelPanel.getOptions(beautiOptions); priorsPanel.getOptions(beautiOptions); operatorsPanel.getOptions(beautiOptions); mcmcPanel.getOptions(beautiOptions); FileWriter fw = new FileWriter(file); beautiOptions.generateXML(fw); fw.close(); } public JComponent getExportableComponent() { JComponent exportable = null; Component comp = tabbedPane.getSelectedComponent(); if (comp instanceof Exportable) { exportable = ((Exportable)comp).getExportableComponent(); } else if (comp instanceof JComponent) { exportable = (JComponent)comp; } return exportable; } public boolean doSave() { return doSaveAs(); } public boolean doSaveAs() { FileDialog dialog = new FileDialog(this, "Save Template As...", FileDialog.SAVE); dialog.setVisible(true); if (dialog.getFile() == null) { // the dialog was cancelled... return false; } File file = new File(dialog.getDirectory(), dialog.getFile()); try { if (writeToFile(file)) { clearDirty(); } } catch (IOException ioe) { JOptionPane.showMessageDialog(this, "Unable to save file: " + ioe, "Unable to save file", JOptionPane.ERROR_MESSAGE); } return true; } public Action getOpenAction() { return openTemplateAction; } private final AbstractAction openTemplateAction = new AbstractAction("Apply Template...") { private static final long serialVersionUID = 2450459627280385426L; public void actionPerformed(ActionEvent ae) { doApplyTemplate(); } }; public Action getSaveAction() { return saveAsAction; } public Action getSaveAsAction() { return saveAsAction; } private final AbstractAction saveAsAction = new AbstractAction("Save Template As...") { private static final long serialVersionUID = 2424923366448459342L; public void actionPerformed(ActionEvent ae) { doSaveAs(); } }; public Action getImportAction() { return importNexusAction; } protected AbstractAction importNexusAction = new AbstractAction("Import NEXUS...") { private static final long serialVersionUID = 3217702096314745005L; public void actionPerformed(java.awt.event.ActionEvent ae) { doImport(); } }; public Action getExportAction() { return generateAction; } protected AbstractAction generateAction = new AbstractAction("Generate BEAST File...", gearIcon) { private static final long serialVersionUID = -5329102618630268783L; public void actionPerformed(java.awt.event.ActionEvent ae) { doGenerate(); } }; }