/* * FigTreeFrame.java * * Copyright (C) 2006-2014 Andrew Rambaut * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package figtree.application; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; import com.itextpdf.text.pdf.DefaultFontMapper; import com.itextpdf.text.pdf.PdfContentByte; import com.itextpdf.text.pdf.PdfTemplate; import com.itextpdf.text.pdf.PdfWriter; import figtree.treeviewer.decorators.DiscreteColourDecorator; import figtree.treeviewer.decorators.HSBDiscreteColourDecorator; import figtree.treeviewer.painters.StatesPainter; import jebl.evolution.align.Output; import jebl.evolution.alignments.Alignment; import jebl.evolution.alignments.BasicAlignment; import jebl.evolution.graphs.Node; import jebl.evolution.io.*; import jebl.evolution.taxa.Taxon; import jebl.evolution.trees.RootedTree; import jebl.evolution.trees.Tree; import jebl.util.Attributable; import jam.controlpalettes.BasicControlPalette; import jam.controlpalettes.ControlPalette; import jam.framework.DocumentFrame; import jam.panels.*; import jam.toolbar.*; import jam.util.IconUtils; import figtree.application.menus.TreeMenuHandler; import figtree.application.menus.FigTreeFileMenuHandler; import figtree.treeviewer.*; import figtree.treeviewer.TreeSelectionListener; import figtree.treeviewer.annotations.*; import org.apache.batik.dom.GenericDOMImplementation; import org.apache.batik.svggen.SVGGraphics2D; import org.apache.batik.svggen.SVGGraphics2DIOException; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Element; import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; import java.awt.geom.Rectangle2D; import java.awt.datatransfer.*; import java.awt.event.*; import java.awt.image.BufferedImage; import java.io.*; import java.net.URL; import java.text.NumberFormat; import java.util.*; import java.util.List; /** * Primary window for FigTree. The window contains a toolbar, a control panel and a panel displaying the * tree. It responds to menu commands and hot keys specific to the focused window. * @author Andrew Rambaut * @version $Id: FigTreeApplication.java 232 2014-03-02 15:47:10Z rambaut $ * * $HeadURL: https://figtree.googlecode.com/svn/trunk/src/figtree/application/FigTreeApplication.java $ * * $LastChangedBy: rambaut $ * $LastChangedDate: 2014-03-02 15:47:10 +0000 (Sun, 02 Mar 2014) $ * $LastChangedRevision: 232 $ */ public class FigTreeFrame extends DocumentFrame implements FigTreeFileMenuHandler, TreeMenuHandler { private final ExtendedTreeViewer treeViewer; private final ControlPalette controlPalette; private final FigTreePanel figTreePanel; private StatusBar statusBar; private SearchPanel filterPanel; private JPopupMenu filterPopup; public FigTreeFrame(String title) { super(); setTitle(title); setImportAction(importAction); // setImportAction(importCharactersAction); setExportAction(exportTreesAction); treeViewer = new ExtendedTreeViewer(this); controlPalette = new BasicControlPalette(200, BasicControlPalette.DisplayMode.ONLY_ONE_OPEN); figTreePanel = new FigTreePanel(this, treeViewer, controlPalette); this.addWindowFocusListener(new WindowAdapter() { public void windowGainedFocus(WindowEvent e) { treeViewer.requestFocusInWindow(); } }); } public void initializeComponents() { setSize(new java.awt.Dimension(1024, 768)); Toolbar toolBar = new Toolbar(); toolBar.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.darkGray)); toolBar.setRollover(true); toolBar.setFloatable(false); Icon rerootToolIcon = IconUtils.getIcon(this.getClass(), "images/rerootTool.png"); Icon rotateToolIcon = IconUtils.getIcon(this.getClass(), "images/rotateTool.png"); Icon cartoonNodeToolIcon = IconUtils.getIcon(this.getClass(), "images/cartoonNodeTool.png"); Icon collapseNodeToolIcon = IconUtils.getIcon(this.getClass(), "images/collapseNodeTool.png"); Icon hilightToolIcon = IconUtils.getIcon(this.getClass(), "images/hilightTool.png"); Icon annotationToolIcon = IconUtils.getIcon(this.getClass(), "images/annotationTool.png"); Icon findToolIcon = IconUtils.getIcon(this.getClass(), "images/findTool.png"); Icon infoToolIcon = IconUtils.getIcon(this.getClass(), "images/infoTool.png"); Icon statisticsToolIcon = IconUtils.getIcon(this.getClass(), "images/statisticsTool.png"); Icon settingsToolIcon = IconUtils.getIcon(this.getClass(), "images/projectTool.png"); Icon colourToolIcon = IconUtils.getIcon(this.getClass(), "images/coloursTool.png"); Icon nextIcon = IconUtils.getIcon(this.getClass(), "images/next.png"); Icon prevIcon = IconUtils.getIcon(this.getClass(), "images/prev.png"); final ToolbarAction cartoonToolbarAction = new ToolbarAction("Cartoon", CARTOON_NODE, cartoonNodeToolIcon) { public void actionPerformed(ActionEvent e){ cartoonAction.actionPerformed(e); } }; ToolbarButton cartoonToolButton = new ToolbarButton(cartoonToolbarAction, true); cartoonToolButton.setFocusable(false); toolBar.addComponent(cartoonToolButton); final ToolbarAction collapseToolbarAction = new ToolbarAction("Collapse", COLLAPSE_NODE, collapseNodeToolIcon) { public void actionPerformed(ActionEvent e){ collapseAction.actionPerformed(e); } }; ToolbarButton collapseToolButton = new ToolbarButton(collapseToolbarAction, true); collapseToolButton.setFocusable(false); toolBar.addComponent(collapseToolButton); final ToolbarAction rerootToolbarAction = new ToolbarAction("Reroot", ROOT_ON_BRANCH, rerootToolIcon) { public void actionPerformed(ActionEvent e){ rerootAction.actionPerformed(e); } }; ToolbarButton rerootToolButton = new ToolbarButton(rerootToolbarAction, true); rerootToolButton.setFocusable(false); toolBar.addComponent(rerootToolButton); final ToolbarAction rotateToolbarAction = new ToolbarAction("Rotate", ROTATE_NODE, rotateToolIcon) { public void actionPerformed(ActionEvent e){ rotateAction.actionPerformed(e); } }; ToolbarButton rotateToolButton = new ToolbarButton(rotateToolbarAction, true); rotateToolButton.setFocusable(false); toolBar.addComponent(rotateToolButton); final ToolbarAction annotateToolbarAction = new ToolbarAction("Annotate", ANNOTATE, annotationToolIcon) { public void actionPerformed(ActionEvent e){ annotateAction.actionPerformed(e); } }; ToolbarButton annotationToolButton = new ToolbarButton(annotateToolbarAction, true); annotationToolButton.setFocusable(false); toolBar.addComponent(annotationToolButton); final ToolbarAction colourToolbarAction = new ToolbarAction("Colour", COLOUR, colourToolIcon) { public void actionPerformed(ActionEvent e){ colourAction.actionPerformed(e); } }; ToolbarButton colourToolButton = new ToolbarButton(colourToolbarAction, true); colourToolButton.setFocusable(false); toolBar.addComponent(colourToolButton); final ToolbarAction hilightToolbarAction = new ToolbarAction("Hilight", HILIGHT, hilightToolIcon) { public void actionPerformed(ActionEvent e){ hilightAction.actionPerformed(e); } }; ToolbarButton hilightToolButton = new ToolbarButton(hilightToolbarAction, true); hilightToolButton.setFocusable(false); toolBar.addComponent(hilightToolButton); toolBar.addSeparator(); final ToolbarAction findToolbarAction = new ToolbarAction("Find", "Find Taxa...", findToolIcon) { public void actionPerformed(ActionEvent e){ findAction.actionPerformed(e); } }; JButton findToolButton = new ToolbarButton(findToolbarAction); findToolButton.setFocusable(false); toolBar.addComponent(findToolButton); findToolButton.setEnabled(true); // final ToolbarAction infoToolbarAction = new ToolbarAction("Get Info", "Get Info...", infoToolIcon) { // public void actionPerformed(ActionEvent e){ // getInfoAction.actionPerformed(e); // } // }; // JButton infoToolButton = new ToolbarButton(infoToolbarAction); // toolBar.addComponent(infoToolButton); // JButton settingsToolButton = new ToolbarButton( // new ToolbarAction("Statistics", "Statistics...", statisticsToolIcon) { // public void actionPerformed(ActionEvent e){ // treeViewer.showStatistics(); // } // }); // settingsToolButton.putClientProperty("Quaqua.Button.style", "toolBarRollover"); // toolBar.addComponent(settingsToolButton); // settingsToolButton.setEnabled(false); toolBar.addSeparator(); Box box1 = Box.createHorizontalBox(); final JToggleButton toggle1 = new JToggleButton("Node"); toggle1.setFocusable(false); toggle1.putClientProperty("JButton.buttonType", "segmentedTextured"); toggle1.putClientProperty("JButton.segmentPosition", "first"); toggle1.putClientProperty( "Quaqua.Button.style", "toggleWest"); final JToggleButton toggle2 = new JToggleButton("Clade"); toggle2.setFocusable(false); toggle2.putClientProperty("JButton.buttonType", "segmentedTextured"); toggle2.putClientProperty("JButton.segmentPosition", "middle"); toggle2.putClientProperty( "Quaqua.Button.style", "toggleCenter"); final JToggleButton toggle3 = new JToggleButton("Taxa"); toggle3.setFocusable(false); toggle3.putClientProperty("JButton.buttonType", "segmentedTextured"); toggle3.putClientProperty("JButton.segmentPosition", "last"); toggle3.putClientProperty( "Quaqua.Button.style", "toggleEast"); ButtonGroup buttonGroup = new ButtonGroup(); buttonGroup.add(toggle1); buttonGroup.add(toggle2); buttonGroup.add(toggle3); toggle1.setSelected(true); toggle1.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { if (e.getStateChange() == ItemEvent.SELECTED) { treeViewer.setSelectionMode(TreePaneSelector.SelectionMode.NODE); } } }); toggle2.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { if (e.getStateChange() == ItemEvent.SELECTED) { treeViewer.setSelectionMode(TreePaneSelector.SelectionMode.CLADE); } } }); toggle3.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { if (e.getStateChange() == ItemEvent.SELECTED) { treeViewer.setSelectionMode(TreePaneSelector.SelectionMode.TAXA); } } }); box1.add(Box.createVerticalStrut(annotationToolIcon.getIconHeight())); box1.add(toggle1); box1.add(toggle2); box1.add(toggle3); toolBar.addComponent(new GenericToolbarItem("Selection Mode", "What aspect of the tree is selected when it is clicked", box1)); toolBar.addFlexibleSpace(); final ToolbarAction prevTreeToolbarAction = new ToolbarAction(null, "Previous Tree...", prevIcon) { public void actionPerformed(ActionEvent e){ if (treeViewer.isRootingOn() && treeViewer.getRootingType() == TreePane.RootingType.USER_ROOTING) { JOptionPane.showMessageDialog(FigTreeFrame.this, "Cannot switch trees when user rooting option is on.\n" + "Turn this option off to switch trees", "Unable to switch trees", JOptionPane.ERROR_MESSAGE); } else { treeViewer.showPreviousTree(); } } }; JButton prevTreeToolButton = new ToolbarButton(prevTreeToolbarAction, true); prevTreeToolButton.setFocusable(false); prevTreeToolButton.putClientProperty("JButton.buttonType", "segmentedTextured"); prevTreeToolButton.putClientProperty("JButton.segmentPosition", "first"); prevTreeToolButton.putClientProperty( "Quaqua.Button.style", "toggleWest"); final ToolbarAction nextTreeToolbarAction = new ToolbarAction(null, "Next Tree...", nextIcon) { public void actionPerformed(ActionEvent e){ if (treeViewer.isRootingOn() && treeViewer.getRootingType() == TreePane.RootingType.USER_ROOTING) { JOptionPane.showMessageDialog(FigTreeFrame.this, "Cannot switch trees when user rooting option is on.\n" + "Turn this option off to switch trees", "Unable to switch trees", JOptionPane.ERROR_MESSAGE); } else { treeViewer.showNextTree(); } } }; JButton nextTreeToolButton = new ToolbarButton(nextTreeToolbarAction, true); nextTreeToolButton.setFocusable(false); nextTreeToolButton.putClientProperty("JButton.buttonType", "segmentedTextured"); nextTreeToolButton.putClientProperty("JButton.segmentPosition", "last"); nextTreeToolButton.putClientProperty( "Quaqua.Button.style", "toggleEast"); nextTreeToolbarAction.setEnabled(treeViewer.getCurrentTreeIndex() < treeViewer.getTreeCount() - 1); prevTreeToolbarAction.setEnabled(treeViewer.getCurrentTreeIndex() > 0); Box box2 = Box.createHorizontalBox(); box2.add(Box.createVerticalStrut(annotationToolIcon.getIconHeight())); box2.add(prevTreeToolButton); box2.add(nextTreeToolButton); toolBar.addComponent(new GenericToolbarItem("Prev/Next", "Navigate through the trees", box2)); TreeViewerListener l = new TreeViewerListener() { public void treeChanged() { boolean nextTreeEnabled = treeViewer.getCurrentTreeIndex() < treeViewer.getTreeCount() - 1; nextTreeAction.setEnabled(nextTreeEnabled); nextTreeToolbarAction.setEnabled(nextTreeEnabled); boolean prevTreeEnabled = treeViewer.getCurrentTreeIndex() > 0; previousTreeAction.setEnabled(prevTreeEnabled); prevTreeToolbarAction.setEnabled(prevTreeEnabled); } public void treeSettingsChanged() { // nothing to do } }; treeViewer.addTreeViewerListener(l); l.treeChanged(); toolBar.addFlexibleSpace(); filterPopup = new JPopupMenu(); final ButtonGroup bg = new ButtonGroup(); boolean first = true; for (TreeViewer.TextSearchType searchType : TreeViewer.TextSearchType.values()) { final JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem(searchType.toString()); if (first) { menuItem.setSelected(true); first = false; } filterPopup.add(menuItem); bg.add(menuItem); } filterPanel = new SearchPanel("Filter", filterPopup, true); // filterPanel.getSearchText().requestFocus(); filterPanel.addSearchPanelListener(new SearchPanelListener() { /** * Called when the user requests a search by pressing return having * typed a search string into the text field. If the continuousUpdate * flag is true then this method is called when the user types into * the text field. * * @param searchString the user's search string */ public void searchStarted(String searchString) { Enumeration e = bg.getElements(); String value = null; while (e.hasMoreElements()) { AbstractButton button = (AbstractButton)e.nextElement(); if (button.isSelected()) { value = button.getText(); } } for (TreeViewer.TextSearchType searchType : TreeViewer.TextSearchType.values()) { if (searchType.toString().equals(value)) { treeViewer.selectTaxa(searchType, searchString, false); } } } /** * Called when the user presses the cancel search button or presses * escape while the search is in focus. */ public void searchStopped() { treeViewer.clearSelectedTaxa(); } }); JPanel panel3 = new JPanel(new FlowLayout()); panel3.add(filterPanel); toolBar.addComponent(panel3); statusBar = new StatusBar("FigTree"); statusBar.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createCompoundBorder( BorderFactory.createMatteBorder(1, 0, 0, 0, Color.darkGray), BorderFactory.createMatteBorder(1, 0, 0, 0, Color.lightGray)), BorderFactory.createEmptyBorder(2, 12, 2, 12))); statusBar.setOpaque(false); statusBar.setStatusProvider(treeViewer.getStatusProvider()); JPanel topPanel = new JPanel(new BorderLayout(0,0)); topPanel.add(toolBar, BorderLayout.NORTH); getContentPane().setLayout(new java.awt.BorderLayout(0, 0)); getContentPane().add(topPanel, BorderLayout.NORTH); getContentPane().add(figTreePanel, BorderLayout.CENTER); getContentPane().add(statusBar, BorderLayout.SOUTH); TreeSelectionListener l2 = new TreeSelectionListener() { public void selectionChanged() { boolean hasSelection = treeViewer.hasSelection(); cartoonToolbarAction.setEnabled(hasSelection); cartoonAction.setEnabled(hasSelection); collapseToolbarAction.setEnabled(hasSelection); collapseAction.setEnabled(hasSelection); // clearCollapsedAction.setEnabled(hasSelection); hilightToolbarAction.setEnabled(hasSelection); hilightAction.setEnabled(hasSelection); clearHilightingAction.setEnabled(hasSelection); // midpointRootAction.setEnabled(hasSelection); rerootToolbarAction.setEnabled(hasSelection); rerootAction.setEnabled(hasSelection); clearRootingAction.setEnabled(hasSelection); rotateToolbarAction.setEnabled(hasSelection); rotateAction.setEnabled(hasSelection); // increasingNodeOrderAction.setEnabled(hasSelection); // decreasingNodeOrderAction.setEnabled(hasSelection); // clearRotationsAction.setEnabled(hasSelection); annotateToolbarAction.setEnabled(hasSelection); // annotateNodesFromTipsAction.setEnabled(hasSelection); // annotateTipsFromNodesAction.setEnabled(hasSelection); annotateAction.setEnabled(hasSelection); // clearAnnotationsAction.setEnabled(hasSelection); colourToolbarAction.setEnabled(hasSelection); colourAction.setEnabled(hasSelection); clearColouringAction.setEnabled(hasSelection); } }; treeViewer.addTreeSelectionListener(l2); l2.selectionChanged(); getCutAction().setEnabled(false); getCopyAction().setEnabled(true); getDeleteAction().setEnabled(false); getSelectAllAction().setEnabled(true); getFindAction().setEnabled(true); getZoomWindowAction().setEnabled(false); } private void defineAnnotations() { Collection<AnnotationDefinition> definitions = treeViewer.getAnnotationDefinitions().values(); if (annotationDefinitionsDialog == null) { annotationDefinitionsDialog = new AnnotationDefinitionsDialog(this); } annotationDefinitionsDialog.showDialog(definitions); treeViewer.setAnnotationDefinitions(annotationDefinitionsDialog.getAnnotations()); treeViewer.fireAnnotationsChanged(); } private void annotateNodesFromTips() { List<String> annotationNames = new ArrayList<String>(); annotationNames.add("Colour"); annotationNames.addAll(treeViewer.getAnnotationDefinitions().keySet()); annotationNames.add("Name"); if (selectAnnotationDialog == null) { selectAnnotationDialog = new SelectAnnotationDialog(this); } int result = selectAnnotationDialog.showDialog(annotationNames); if (result == JOptionPane.CANCEL_OPTION || result == JOptionPane.CLOSED_OPTION) { return; } String annotationName = selectAnnotationDialog.getAnnotationName(); if (annotationName.equals("Colour")) { annotationName = "!color"; } treeViewer.annotateNodesFromTips(annotationName); setDirty(); } private void annotateTipsFromNodes() { List<String> annotationNames = new ArrayList<String>(); annotationNames.add("Colour"); annotationNames.addAll(treeViewer.getAnnotationDefinitions().keySet()); if (selectAnnotationDialog == null) { selectAnnotationDialog = new SelectAnnotationDialog(this); } if (selectAnnotationDialog.showDialog(annotationNames) != JOptionPane.CANCEL_OPTION) { String annotationName = selectAnnotationDialog.getAnnotationName(); if (annotationName.equals("Colour")) { annotationName = "!color"; } treeViewer.annotateTipsFromNodes(annotationName); setDirty(); } } private void cartoonSelected() { treeViewer.cartoonSelectedNodes(); } private void collapseSelected() { treeViewer.collapseSelectedNodes(); } private void hilightSelected() { Color color = JColorChooser.showDialog(this, "Select Colour", lastColor); if (color != null) { treeViewer.hilightSelectedNodes(color); setDirty(); lastColor = color; } } private void rerootTree() { Set<Node> nodes = treeViewer.getSelectedNodes(); if (nodes.size() != 1 ) { JOptionPane.showMessageDialog(this, "Exactly one branch must be selected to re-root the tree." , "Annotating Tree", JOptionPane.WARNING_MESSAGE); return; } treeViewer.rerootOnSelectedBranch(); } private void rotateTree() { treeViewer.rotateSelectedNode(); } private void annotateSelected() { treeViewer.setToolMode(TreePaneSelector.ToolMode.SELECT); List<AnnotationDefinition> definitions = new ArrayList<AnnotationDefinition>(); definitions.add(new AnnotationDefinition("Name", "!name", AnnotationDefinition.Type.STRING)); definitions.addAll(treeViewer.getAnnotationDefinitions().values()); if (annotationDialog == null) { annotationDialog = new AnnotationDialog(this); } Set<Node> nodes = treeViewer.getSelectedNodes(); Set<Node> tips = treeViewer.getSelectedTips(); Attributable item = null; if (nodes.size() + tips.size() == 1 ) { if (nodes.size() == 1) { item = nodes.iterator().next(); }else if (tips.size() == 1) { item = tips.iterator().next(); } } else { int result = JOptionPane.showConfirmDialog(this, "More than one node selected for annotation. This operation\n" + "may overwrite existing annotations. Do you wish to continue?" , "Annotating Tree", JOptionPane.WARNING_MESSAGE); if (result == JOptionPane.CANCEL_OPTION || result == JOptionPane.CLOSED_OPTION) { return; } } if (annotationDialog.showDialog(definitions, item) != JOptionPane.CANCEL_OPTION) { AnnotationDefinition newDefinition = annotationDialog.getDefinition(); List<AnnotationDefinition> defs = new ArrayList<AnnotationDefinition>(treeViewer.getAnnotationDefinitions().values()); if (!defs.contains(newDefinition)) { defs.add(newDefinition); treeViewer.setAnnotationDefinitions(defs); } String code = newDefinition.getCode(); Object value = annotationDialog.getValue(); treeViewer.annotateSelected(code, value); setDirty(); } } private void copySelectedAnnotations() { // todo This function is disabled as it is not completely implemented // treeViewer.setToolMode(TreePaneSelector.ToolMode.SELECT); // // List<AnnotationDefinition> definitions = new ArrayList<AnnotationDefinition>(); // definitions.add(new AnnotationDefinition("Name", "!name", AnnotationDefinition.Type.STRING)); // definitions.addAll(treeViewer.getAnnotationDefinitions().values()); // // if (copyAnnotationDialog == null) { // copyAnnotationDialog = new AnnotationDialog(this, true); // } // // Set<Node> nodes = treeViewer.getSelectedNodes(); // Set<Node> tips = treeViewer.getSelectedTips(); // // Attributable item = null; // if (nodes.size() + tips.size() == 1 ) { // if (nodes.size() == 1) { // item = nodes.iterator().next(); // }else if (tips.size() == 1) { // item = tips.iterator().next(); // } // } else { // int result = JOptionPane.showConfirmDialog(this, // "More than one node selected for annotation. This operation\n" + // "may overwrite existing annotations. Do you wish to continue?" , // "Annotating Tree", // JOptionPane.WARNING_MESSAGE); // if (result == JOptionPane.CANCEL_OPTION || result == JOptionPane.CLOSED_OPTION) { // return; // } // } // if (annotationDialog.showDialog(definitions, item) != JOptionPane.CANCEL_OPTION) { // String code = annotationDialog.getDefinition().getCode(); // String code2 = annotationDialog.getDestinationDefinition().getCode(); // // treeViewer.copySelected(code, value); // setDirty(); // } } private static Color lastColor = Color.GRAY; private void colourSelected() { treeViewer.setToolMode(TreePaneSelector.ToolMode.SELECT); Color color = JColorChooser.showDialog(this, "Select Colour", lastColor); if (color != null) { treeViewer.annotateSelected("!color", color); setDirty(); lastColor = color; } } public boolean readFromFile(File file) throws IOException { Reader reader = null; try { reader = new FileReader(file); boolean isNexus = isNexus(reader); ProgressMonitorInputStream in = new ProgressMonitorInputStream( this, "Reading " + file.getName(), new FileInputStream(file)); in.getProgressMonitor().setMillisToDecideToPopup(1000); in.getProgressMonitor().setMillisToPopup(1000); reader = new InputStreamReader(in); boolean success = readData(reader, isNexus); reader.close(); return success; } catch (IOException ioe) { if (reader != null) { reader.close(); } throw ioe; } } public boolean readFromURL(URL url) throws IOException { InputStream in = url.openStream(); Reader reader = new InputStreamReader(url.openStream()); boolean isNexus = isNexus(reader); reader.close(); reader = new InputStreamReader(url.openStream()); return readData(reader, isNexus); } private boolean isNexus(Reader reader) throws IOException { BufferedReader bufferedReader = new BufferedReader(reader); String line = bufferedReader.readLine(); while (line != null && line.length() == 0) { line = bufferedReader.readLine(); } return (line != null && line.toUpperCase().contains("#NEXUS")); } public boolean readFromString(String string) throws IOException { boolean isNexus = string.substring(0, 80).toUpperCase().contains("#NEXUS"); return readData(new StringReader(string), isNexus); } protected boolean readData(Reader reader, boolean isNexus) throws IOException { List<Tree> trees = new ArrayList<Tree>(); boolean hasSettings = false; try { Map<String, Object> settings = new HashMap<String, Object>(); // First of all, fully populate the settings map so that // all the settings have defaults controlPalette.getSettings(settings); if (isNexus) { FigTreeNexusImporter importer = new FigTreeNexusImporter(reader); while (importer.hasTree()) { Tree tree = importer.importNextTree(); trees.add(tree); } // Try to find a figtree block and if found, parse the settings while (true) { try { importer.findNextBlock(); if (importer.getNextBlockName().equalsIgnoreCase("FIGTREE")) { importer.parseFigTreeBlock(settings); hasSettings = true; } } catch (EOFException ex) { break; } } } else { NewickImporter importer = new NewickImporter(reader, true); while (importer.hasTree()) { Tree tree = importer.importNextTree(); trees.add(tree); } } if (trees.size() == 0) { throw new ImportException("This file contained no trees."); } checkLabelAttribute(trees); treeViewer.setTrees(trees); controlPalette.setSettings(settings); } catch (ImportException ie) { JOptionPane.showMessageDialog(this, "Error reading tree file: \n" + ie.getMessage(), "Import Error", JOptionPane.ERROR_MESSAGE); return false; } if (!hasSettings) { // If there weren't settings in the file then this wasn't a TreeDraw // created document so we don't want to be able to overwrite it without // explicit action of the user... setDirty(); clearFile(); } return true; } /** * This version loads the trees in a thread but this needs more thought in order * to tie in to the JAM framework correctly */ // protected boolean readData(final Reader reader, final boolean isNexus) { // // final JFrame frame = this; // Thread readThread = new Thread () { // public void run() { // try { // // final List<Tree> trees = new ArrayList<Tree>(); // // boolean hasSettings = false; // // final Map<String, Object> settings = new HashMap<String, Object>(); // // First of all, fully populate the settings map so that // // all the settings have defaults // controlPalette.getSettings(settings); // // if (isNexus) { // FigTreeNexusImporter importer = new FigTreeNexusImporter(reader); // while (importer.hasTree()) { // Tree tree = importer.importNextTree(); // trees.add(tree); // } // // Try to find a figtree block and if found, parse the settings // while (true) { // try { // importer.findNextBlock(); // if (importer.getNextBlockName().equalsIgnoreCase("FIGTREE")) { // importer.parseFigTreeBlock(settings); // hasSettings = true; // } // } catch (EOFException ex) { // break; // } // } // } else { // NewickImporter importer = new NewickImporter(reader, true); // while (importer.hasTree()) { // Tree tree = importer.importNextTree(); // trees.add(tree); // } // } // // if (trees.size() == 0) { // throw new ImportException("This file contained no trees."); // } // // final boolean hasSettings2 = hasSettings; // // EventQueue.invokeLater ( // new Runnable () { // public void run () { // treeViewer.setTrees(trees); // controlPalette.setSettings(settings); // // if (!hasSettings2) { // // If there weren't settings in the file then this wasn't a TreeDraw // // created document so we don't want to be able to overwrite it without // // explicit action of the user... // setDirty(); // clearFile(); // } // } // }); // // } catch (final ImportException ie) { // EventQueue.invokeLater ( // new Runnable () { // public void run () { // JOptionPane.showMessageDialog(frame, "Error reading tree file: " + ie.getMessage(), // "Import Error", // JOptionPane.ERROR_MESSAGE); // } // }); // } catch (final InterruptedIOException iioex) { // // The cancel dialog button was pressed - do nothing // } catch (final IOException ioex) { // EventQueue.invokeLater ( // new Runnable () { // public void run () { // JOptionPane.showMessageDialog(frame, "File I/O Error: " + ioex.getMessage(), // "File I/O Error", // JOptionPane.ERROR_MESSAGE); // } // }); // } // // } // }; // readThread.start(); // // return true; // } private void checkLabelAttribute(List<Tree> trees) { boolean hasLabel = false; for (Tree tree : trees) { for (Node node : tree.getNodes()) { if (node.getAttribute("label") != null) { hasLabel = true; } } } if (hasLabel) { String labelName = null; do { labelName = JOptionPane.showInputDialog( "The node/branches of the tree are labelled\n" + "(i.e., with bootstrap values or posterior probabilities).\n\n" + "Please select a name for these values.", "label"); if (labelName == null) { labelName = "label"; } labelName = labelName.trim(); if (labelName.length() == 0) { Toolkit.getDefaultToolkit().beep(); } } while (labelName == null || labelName.length() == 0); if (!labelName.equals("label")) { for (Tree tree : trees) { for (Node node : tree.getNodes()) { Object value = node.getAttribute("label"); if (value != null) { node.removeAttribute("label"); node.setAttribute(labelName, value); } } } } } } protected boolean writeToFile(File file) throws IOException { Map<String, Object> settings = new TreeMap<String, Object>(); controlPalette.getSettings(settings); FileWriter writer = new FileWriter(file); FigTreeNexusExporter exporter = new FigTreeNexusExporter(writer, true); exporter.exportTrees(treeViewer.getTrees(), true); exporter.writeFigTreeBlock(settings); writer.close(); return true; } public final void doImport() { FileDialog dialog = new FileDialog(this, "Import Annotations File...", FileDialog.LOAD); dialog.setVisible(true); if (dialog.getFile() != null) { File file = new File(dialog.getDirectory(), dialog.getFile()); try { Map<AnnotationDefinition, Map<Taxon, Object>> annotations = importAnnotationsFromFile(file); treeViewer.setTaxonAnnotations(annotations); // Hack to show tips states... // String[] annotationNames = new String[annotations.keySet().size()]; // DiscreteColourDecorator[] decorators = new DiscreteColourDecorator[annotations.keySet().size()]; // // int i = 0; // for (AnnotationDefinition definition: annotations.keySet()) { // Map<Taxon, Object> annotation = annotations.get(definition); // annotationNames[i] = definition.getName(); // decorators[i] = new HSBDiscreteColourDecorator(annotationNames[i], annotation.keySet()); // i++; // } // treeViewer.setTipLabelPainter(new StatesPainter(annotationNames, decorators)); } 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.getMessage(), "Unable to read file", JOptionPane.ERROR_MESSAGE); } } } protected Map<AnnotationDefinition, Map<Taxon, Object>> importAnnotationsFromFile(File file) throws IOException { BufferedReader reader = new BufferedReader(new FileReader(file)); List<String> taxa = new ArrayList<String>(); String line = reader.readLine(); String[] labels = line.split("\t"); Map<String, List<String>> columns = new HashMap<String, List<String>>(); for (int i = 1; i < labels.length; i++) { columns.put(labels[i], new ArrayList<String>()); } line = reader.readLine(); while (line != null) { String[] values = line.split("\t"); if (values.length > 0) { taxa.add(values[0]); for (int i = 1; i < values.length; i++) { if (i < labels.length) { List<String> column = columns.get(labels[i]); column.add(values[i]); } } } line = reader.readLine(); } Map<AnnotationDefinition, Map<Taxon, Object>> annotations = new TreeMap<AnnotationDefinition, Map<Taxon, Object>>(); NumberFormat nf = NumberFormat.getInstance(); for (int i = 1; i < labels.length; i++) { List<String> column = columns.get(labels[i]); boolean isInteger = true; boolean isNumber = true; boolean isBoolean = true; for (String valueString : column) { if (!valueString.equalsIgnoreCase("TRUE") && !valueString.equalsIgnoreCase("FALSE")) { isBoolean = false; try { double number = Double.parseDouble(valueString); if (Math.round(number) != number) { isInteger = false; } } catch (NumberFormatException pe) { isInteger = false; isNumber = false; } } } Map<Taxon, Object> values = new HashMap<Taxon, Object>(); AnnotationDefinition ad; int j = 0; for (String valueString : column) { Taxon taxon = Taxon.getTaxon(taxa.get(j)); if (isBoolean) { values.put(taxon, new Boolean(valueString)); } else if (isInteger) { values.put(taxon, new Integer(valueString)); } else if (isNumber) { values.put(taxon, new Double(valueString)); } else { values.put(taxon, valueString); } j++; } Set<Object> valueSet = new HashSet<Object>(values.values()); if (isBoolean) { ad = new AnnotationDefinition(labels[i], AnnotationDefinition.Type.BOOLEAN ); } else if (isInteger) { ad = new AnnotationDefinition(labels[i], AnnotationDefinition.Type.INTEGER ); } else if (isNumber) { ad = new AnnotationDefinition(labels[i], AnnotationDefinition.Type.REAL ); } else { String[] valueArray = new String[valueSet.size()]; valueSet.toArray(valueArray); ad = new AnnotationDefinition(labels[i], AnnotationDefinition.Type.STRING); //ad.setOptions(valueArray); } annotations.put(ad, values); } return annotations; } private void doImportCharacters() { FileDialog dialog = new FileDialog(this, "Import Characters File...", FileDialog.LOAD); dialog.setVisible(true); if (dialog.getFile() != null) { File file = new File(dialog.getDirectory(), dialog.getFile()); try { importCharactersFromFile(file); } 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.getMessage(), "Unable to read file", JOptionPane.ERROR_MESSAGE); } } } protected boolean importCharactersFromFile(File file) throws FileNotFoundException, IOException { final String fileName = file.getName(); SequenceImporter importer = new NexusImporter(new FileReader(file)); try { Alignment alignment = new BasicAlignment(importer.importSequences()); treeViewer.setCharacters(alignment); } catch (ImportException ie) { JOptionPane.showMessageDialog(this, "Error reading characters file: " + ie.getMessage(), "Import Error", JOptionPane.ERROR_MESSAGE); return false; } return true; } private void doImportColourScheme() { FileDialog dialog = new FileDialog(this, "Import Colour Scheme...", FileDialog.LOAD); dialog.setVisible(true); if (dialog.getFile() != null) { File file = new File(dialog.getDirectory(), dialog.getFile()); try { importColourSchemeFromFile(file); } 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.getMessage(), "Unable to read file", JOptionPane.ERROR_MESSAGE); } } } protected boolean importColourSchemeFromFile(File file) throws FileNotFoundException, IOException { final String fileName = file.getName(); SequenceImporter importer = new NexusImporter(new FileReader(file)); try { Alignment alignment = new BasicAlignment(importer.importSequences()); treeViewer.setCharacters(alignment); } catch (ImportException ie) { JOptionPane.showMessageDialog(this, "Error reading characters file: " + ie.getMessage(), "Import Error", JOptionPane.ERROR_MESSAGE); return false; } return true; } public final void doExport() { if (exportTreeDialog == null) { exportTreeDialog = new ExportTreeDialog(this); } if (exportTreeDialog.showDialog() == JOptionPane.OK_OPTION) { FileDialog dialog = new FileDialog(this, "Export Tree File...", FileDialog.SAVE); dialog.setVisible(true); if (dialog.getFile() != null) { File file = new File(dialog.getDirectory(), dialog.getFile()); FileWriter writer = null; try { writer = new FileWriter(file); writeTreeFile(writer, exportTreeDialog.getFormat(), exportTreeDialog.allTrees(), exportTreeDialog.asDisplayed(), exportTreeDialog.includeFigTreeBlock(), exportTreeDialog.includeAnnotations(), false); writer.close(); } catch (IOException ioe) { JOptionPane.showMessageDialog(this, "Error writing tree file: " + ioe.getMessage(), "Export Error", JOptionPane.ERROR_MESSAGE); } } } } public final void doExportGraphic(GraphicFormat format) { FileDialog dialog = new FileDialog(this, "Export " + format.getName() + " File...", FileDialog.SAVE); String name = this.getFile().getName() + "." + format.getName().toLowerCase(); dialog.setFile(name); dialog.setVisible(true); if (dialog.getFile() != null) { File file = new File(dialog.getDirectory(), dialog.getFile()); try { OutputStream stream = new FileOutputStream(file); exportGraphics(format, treeViewer.getContentPane(), stream); stream.flush(); stream.close(); } catch(DocumentException de) { JOptionPane.showMessageDialog(this, "Error writing PDF file: " + de, "Export PDF Error", JOptionPane.ERROR_MESSAGE); } catch (IOException ioe) { JOptionPane.showMessageDialog(this, "Error writing tree file: " + ioe.getMessage(), "Export Error", JOptionPane.ERROR_MESSAGE); } } } public final static void exportGraphics(GraphicFormat format, JComponent comp, OutputStream stream) throws IOException, DocumentException { switch (format) { case PNG: case GIF: case BMP: case JPEG: exportGraphicsFile(format, comp, stream); break; case EPS: throw new UnsupportedOperationException("EPS not handled"); case SVG: exportSVGFile(comp, stream); break; case PDF: exportPDFFile(comp, stream); break; default: throw new UnsupportedOperationException("Format not handled: " + format); } } private final static void exportGraphicsFile(GraphicFormat format, JComponent component, OutputStream stream) throws IOException { int imageType = BufferedImage.TYPE_INT_RGB; if (format == GraphicFormat.PNG) { // PNG allows an alpha channel imageType = BufferedImage.TYPE_INT_ARGB; } BufferedImage bi = new BufferedImage(component.getSize().width, component.getSize().height, imageType); Graphics g = bi.createGraphics(); if (format != GraphicFormat.PNG) { g.setColor(Color.WHITE); g.fillRect(0, 0, bi.getWidth(), bi.getHeight()); } component.paint(g); g.dispose(); ImageIO.write(bi, format.getName(), stream); } private final static void exportSVGFile(JComponent component, OutputStream stream) throws IOException { // Get a DOMImplementation and create an XML document DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation(); org.w3c.dom.Document document = domImpl.createDocument(null, "svg", null); // Create an instance of the SVG Generator SVGGraphics2D svgGenerator = new SVGGraphics2D(document); component.paint(svgGenerator); Element svgRoot = svgGenerator.getRoot(); Rectangle2D bounds = component.getBounds(); String viewBox = "0 0 " + bounds.getWidth() + " " + bounds.getHeight(); svgRoot.setAttributeNS(null, svgGenerator.SVG_VIEW_BOX_ATTRIBUTE, viewBox); svgRoot.setAttributeNS(null, svgGenerator.SVG_WIDTH_ATTRIBUTE, Double.toString(bounds.getWidth())); svgRoot.setAttributeNS(null, svgGenerator.SVG_HEIGHT_ATTRIBUTE, Double.toString(bounds.getHeight())); // Write svg file Writer out = new OutputStreamWriter(stream, "UTF-8"); svgGenerator.stream(svgRoot, out, true /* use css */, false /* escaped */); } public final static void exportPDFFile(JComponent component, OutputStream stream) throws DocumentException { Rectangle2D bounds = component.getBounds(); Document document = new Document(new com.itextpdf .text.Rectangle((float)bounds.getWidth(), (float)bounds.getHeight())); // step 2 PdfWriter writer; writer = PdfWriter.getInstance(document, stream); // step 3 document.open(); // step 4 PdfContentByte cb = writer.getDirectContent(); PdfTemplate tp = cb.createTemplate((float)bounds.getWidth(), (float)bounds.getHeight()); Graphics2D g2d = tp.createGraphics((float)bounds.getWidth(), (float)bounds.getHeight(), new DefaultFontMapper()); component.print(g2d); g2d.dispose(); cb.addTemplate(tp, 0, 0); document.close(); } public void doCopy() { StringWriter writer = new StringWriter(); try { if (treeViewer.getSelectionMode() == TreePaneSelector.SelectionMode.TAXA) { writeTaxa(writer); } else { writeTreeFile(writer, ExportTreeDialog.Format.NEXUS, true, false, false, true, true); } } catch (IOException e) { e.printStackTrace(); } Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); StringSelection selection = new StringSelection(writer.toString()); clipboard.setContents(selection, selection); } public void doPaste() { Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); Transferable transfer = clipboard.getContents(null); if (transfer.isDataFlavorSupported(DataFlavor.stringFlavor)) { try { String clip = (String)transfer.getTransferData(DataFlavor.stringFlavor); boolean isNexus = clip.substring(0, Math.min(80, clip.length())).toUpperCase().contains("#NEXUS"); Reader reader = new StringReader(clip); List<Tree> trees = new ArrayList<Tree>(); TreeImporter importer; if (isNexus) { importer = new FigTreeNexusImporter(reader); } else { importer = new NewickImporter(reader, true); } while (importer.hasTree()) { Tree tree = importer.importNextTree(); trees.add(tree); } if (trees.size() == 0) { throw new ImportException("This clipboard contained no trees."); } treeViewer.addTrees(trees); // Show the first of the new trees treeViewer.showNextTree(); setDirty(); } catch (ImportException ie) { JOptionPane.showMessageDialog(this, "Error reading trees on clipboard: " + ie.getMessage(), "Import Error", JOptionPane.ERROR_MESSAGE); } catch (IOException ioe) { JOptionPane.showMessageDialog(this, "Error reading trees on clipboard: " + ioe.getMessage(), "Import Error", JOptionPane.ERROR_MESSAGE); } catch (UnsupportedFlavorException e) { JOptionPane.showMessageDialog(this, "Clipboard data is not compatible", "Import Error", JOptionPane.ERROR_MESSAGE); } } } public void doSelectAll() { treeViewer.selectAll(); } protected void writeTaxa(Writer writer) throws IOException { PrintWriter printWriter = new PrintWriter(writer); for (Taxon taxon : treeViewer.getSelectedTaxa()) { printWriter.println(taxon.getName()); } writer.close(); } protected void writeTreeFile(Writer writer, ExportTreeDialog.Format format, boolean writeAllTrees, boolean writeAsDisplayed, boolean writeFigTreeBlock, boolean writeAnnotations, boolean writeSelectedSubtree) throws IOException { Map<String, Object> settings = null; if (writeFigTreeBlock) { settings = new TreeMap<String, Object>(); controlPalette.getSettings(settings); } List<Tree> trees = new ArrayList<Tree>(); if (writeSelectedSubtree) { RootedTree tree = treeViewer.getSelectedSubtree(); if (tree != null) { trees.add(tree); } } else { trees.addAll(treeViewer.getTreesAsViewed()); } if (trees.size() > 0) { switch (format) { case NEWICK: NewickExporter newickExporter = new NewickExporter(writer); newickExporter.exportTrees(trees); break; case NEXUS: FigTreeNexusExporter nexusExporter = new FigTreeNexusExporter(writer, writeAnnotations); nexusExporter.exportTrees(trees); if (settings != null) { nexusExporter.writeFigTreeBlock(settings); } break; case JSON: JSONTreeExporter jsonExporter = new JSONTreeExporter(writer, writeAnnotations); jsonExporter.exportTrees(trees); break; } } writer.close(); } public final void doFind() { if (findPanel == null) { findPanel = new FindPanel(findAllAction, null); findPanel.setOpaque(false); treeViewer.addAnnotationsListener(new AnnotationsListener() { public void annotationsChanged() { List<AnnotationDefinition> definitions = new ArrayList<AnnotationDefinition>( treeViewer.getAnnotationDefinitions().values()); findPanel.setup(definitions); } }); List<AnnotationDefinition> definitions = new ArrayList<AnnotationDefinition>( treeViewer.getAnnotationDefinitions().values()); findPanel.setup(definitions); } if (figTreePanel.getUtilityPanel() != findPanel) { figTreePanel.showUtilityPanel(findPanel); } else { figTreePanel.hideUtilityPanel(); } } public final void doFindAll() { FindPanel.Target target = findPanel.getSearchTarget(); String targetString = findPanel.getSearchTargetString(); if (findPanel.isNumericSearchType()) { TreeViewer.NumberSearchType searchType = findPanel.getNumberSearchType(); Number searchValue = findPanel.getSearchValue(); if (target == FindPanel.Target.TAXON_LABEL) { throw new IllegalArgumentException("Can't do numeric search on taxon labels"); } else if (target == FindPanel.Target.BRANCH_LENGTH) { treeViewer.selectNodes("!length", searchType, searchValue); } else if (target == FindPanel.Target.NODE_AGE) { treeViewer.selectNodes("!height", searchType, searchValue); } else if (target == FindPanel.Target.ANY_ANNOTATION) { throw new IllegalArgumentException("Can't do numeric search on all annotations"); } else { treeViewer.selectNodes(targetString, searchType, searchValue); } } else { TreeViewer.TextSearchType searchType = findPanel.getTextSearchType(); String searchText = findPanel.getSearchText(); boolean caseSensitive = findPanel.isCaseSensitive(); if (target == FindPanel.Target.TAXON_LABEL) { treeViewer.selectTaxa("!name", searchType, searchText, caseSensitive); } else if (target == FindPanel.Target.BRANCH_LENGTH) { throw new IllegalArgumentException("Can't do text search on branch lengths"); } else if (target == FindPanel.Target.NODE_AGE) { throw new IllegalArgumentException("Can't do text search on node ages"); } else if (target == FindPanel.Target.ANY_ANNOTATION) { treeViewer.selectNodes(null, searchType, searchText, caseSensitive); } else { treeViewer.selectNodes(targetString, searchType, searchText, caseSensitive); } } } public final void doFindNext() { } public final void doGetInfo() { // List<AnnotationDefinition> definitions = treeViewer.getAnnotationDefinitions(); // JPanel panel = new FindPanel(definitions); // panel.setOpaque(false); // figTreePanel.showUtilityPanel(panel); } public JComponent getExportableComponent() { return treeViewer.getContentPane(); } @Override public Action getImportColourSchemeAction() { return importColourSchemeAction; } @Override public Action getExportColourSchemeAction() { return exportColourSchemeAction; } public Action getExportTreesAction() { return exportTreesAction; } // public Action getExportGraphicAction() { // return exportGraphicAction; // } public Action getExportPDFAction() { return exportPDFAction; } public Action getExportPNGGraphicAction() { return exportPNGGraphicAction; } public Action getExportJPEGGraphicAction() { return exportJPEGGraphicAction; } public Action getExportSVGGraphicAction() { return exportSVGAction; } public Action getNextTreeAction() { return nextTreeAction; } public Action getPreviousTreeAction() { return previousTreeAction; } public Action getCartoonAction() { return cartoonAction; } public Action getCollapseAction() { return collapseAction; } public Action getClearCollapsedAction() { return clearCollapsedAction; } public Action getMidpointRootAction() { return midpointRootAction; } public Action getRerootAction() { return rerootAction; } public Action getClearRootingAction() { return clearRootingAction; } public Action getIncreasingNodeOrderAction() { return increasingNodeOrderAction; } public Action getDecreasingNodeOrderAction() { return decreasingNodeOrderAction; } public Action getRotateAction() { return rotateAction; } public Action getClearRotationsAction() { return clearRotationsAction; } public Action getAnnotateAction() { return annotateAction; } public Action getCopyAnnotationsAction() { return copyAnnotationsAction; } public Action getAnnotateNodesFromTipsAction() { return annotateNodesFromTipsAction; } public Action getAnnotateTipsFromNodesAction() { return annotateTipsFromNodesAction; } public AbstractAction getClearAnnotationsAction() { return clearAnnotationsAction; } public AbstractAction getDefineAnnotationsAction() { return defineAnnotationsAction; } public Action getColourAction() { return colourAction; } public Action getClearColouringAction() { return clearColouringAction; } public Action getHilightAction() { return hilightAction; } public Action getClearHilightingAction() { return clearHilightingAction; } public Action getFindAction() { return findAction; } private AbstractAction importAction = new AbstractAction("Import Annotations...") { public void actionPerformed(ActionEvent ae) { doImport(); } }; private AbstractAction importCharactersAction = new AbstractAction("Import Characters...") { public void actionPerformed(ActionEvent ae) { doImportCharacters(); } }; private AbstractAction importColourSchemeAction = new AbstractAction("Import Colour Scheme...") { public void actionPerformed(ActionEvent ae) { doImportColourScheme(); } }; private AbstractAction exportColourSchemeAction = new AbstractAction("Export Colour Scheme...") { public void actionPerformed(ActionEvent ae) { // doExportColourScheme(); } }; private AbstractAction exportTreesAction = new AbstractAction("Export Trees...") { public void actionPerformed(ActionEvent ae) { doExport(); } }; // private AbstractAction exportGraphicAction = new AbstractAction("Export Graphic...") { // public void actionPerformed(ActionEvent ae) { // doExportGraphic(GraphicFormat.PNG); // } // }; private AbstractAction exportPNGGraphicAction = new AbstractAction("Export PNG...") { public void actionPerformed(ActionEvent ae) { doExportGraphic(GraphicFormat.PNG); } }; private AbstractAction exportJPEGGraphicAction = new AbstractAction("Export JPEG...") { public void actionPerformed(ActionEvent ae) { doExportGraphic(GraphicFormat.JPEG); } }; private AbstractAction exportSVGAction = new AbstractAction("Export SVG...") { public void actionPerformed(ActionEvent ae) { doExportGraphic(GraphicFormat.SVG); } }; private AbstractAction exportPDFAction = new AbstractAction("Export PDF...") { public void actionPerformed(ActionEvent ae) { doExportGraphic(GraphicFormat.PDF); } }; private AbstractAction nextTreeAction = new AbstractAction(NEXT_TREE) { public void actionPerformed(ActionEvent e){ treeViewer.showNextTree(); } }; private AbstractAction previousTreeAction = new AbstractAction(PREVIOUS_TREE) { public void actionPerformed(ActionEvent e){ treeViewer.showPreviousTree(); } }; private AbstractAction cartoonAction = new AbstractAction(CARTOON_NODE) { public void actionPerformed(ActionEvent e){ cartoonSelected(); } }; private AbstractAction collapseAction = new AbstractAction(COLLAPSE_NODE) { public void actionPerformed(ActionEvent e){ collapseSelected(); } }; private AbstractAction clearCollapsedAction = new AbstractAction(CLEAR_COLLAPSED) { public void actionPerformed(ActionEvent e){ treeViewer.clearCollapsedNodes(); } }; private AbstractAction rerootAction = new AbstractAction(ROOT_ON_BRANCH) { public void actionPerformed(ActionEvent e){ rerootTree(); } }; private AbstractAction midpointRootAction = new AbstractAction(MIDPOINT_ROOT) { public void actionPerformed(ActionEvent e){ figTreePanel.toggleMidpointRoot(); } }; private AbstractAction clearRootingAction = new AbstractAction(CLEAR_ROOTING) { public void actionPerformed(ActionEvent e){ treeViewer.clearRooting(); } }; private AbstractAction rotateAction = new AbstractAction(ROTATE_NODE) { public void actionPerformed(ActionEvent e){ rotateTree(); } }; private AbstractAction clearRotationsAction = new AbstractAction(CLEAR_ROTATIONS) { public void actionPerformed(ActionEvent e){ treeViewer.clearRotations(); } }; private AbstractAction increasingNodeOrderAction = new AbstractAction(INCREASING_NODE_ORDER) { public void actionPerformed(ActionEvent e){ figTreePanel.toggleIncreasingNodeOrder(); } }; private AbstractAction decreasingNodeOrderAction = new AbstractAction(DECREASING_NODE_ORDER) { public void actionPerformed(ActionEvent e){ figTreePanel.toggleDecreasingNodeOrder(); } }; private AbstractAction annotateAction = new AbstractAction(ANNOTATE) { public void actionPerformed(ActionEvent ae) { annotateSelected(); } }; private AbstractAction copyAnnotationsAction = new AbstractAction(COPY_ANNOTATION_VALUES) { public void actionPerformed(ActionEvent ae) { copySelectedAnnotations(); } }; private AbstractAction annotateNodesFromTipsAction = new AbstractAction(ANNOTATE_NODES_FROM_TIPS) { public void actionPerformed(ActionEvent ae) { annotateNodesFromTips(); } }; private AbstractAction annotateTipsFromNodesAction = new AbstractAction(ANNOTATE_TIPS_FROM_NODES) { public void actionPerformed(ActionEvent ae) { annotateTipsFromNodes(); } }; private AbstractAction clearAnnotationsAction = new AbstractAction(CLEAR_ANNOTATIONS) { public void actionPerformed(ActionEvent ae) { // treeViewer.clearAnnotation(); } }; private AbstractAction defineAnnotationsAction = new AbstractAction(DEFINE_ANNOTATIONS) { public void actionPerformed(ActionEvent ae) { defineAnnotations(); } }; private AbstractAction colourAction = new AbstractAction(COLOUR) { public void actionPerformed(ActionEvent ae) { colourSelected(); } }; private AbstractAction clearColouringAction = new AbstractAction(CLEAR_COLOURING) { public void actionPerformed(ActionEvent ae) { treeViewer.clearColouring(); } }; private AbstractAction hilightAction = new AbstractAction(HILIGHT) { public void actionPerformed(ActionEvent ae) { hilightSelected(); } }; private AbstractAction clearHilightingAction = new AbstractAction(CLEAR_HILIGHTING) { public void actionPerformed(ActionEvent ae) { treeViewer.clearHilighting(); } }; private AbstractAction findAction = new AbstractAction("Find...") { public void actionPerformed(ActionEvent ae) { doFind(); } }; private AbstractAction findAllAction = new AbstractAction("Find") { public void actionPerformed(ActionEvent ae) { doFindAll(); } }; // private AbstractAction findNextAction = new AbstractAction("Find Next") { // public void actionPerformed(ActionEvent ae) { // doFindNext(); // } // }; private AbstractAction getInfoAction = new AbstractAction("Get Info...") { public void actionPerformed(ActionEvent ae) { doGetInfo(); } }; private ExportTreeDialog exportTreeDialog = null; private FindPanel findPanel = null; private AnnotationDefinitionsDialog annotationDefinitionsDialog = null; private AnnotationDialog annotationDialog = null; private AnnotationDialog copyAnnotationDialog = null; private SelectAnnotationDialog selectAnnotationDialog = null; }