/* * FigTreeApplication.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. */ /** * TracerApp.java * * Title: Tracer * Description: An application for analysing MCMC trace files. * @author Andrew Rambaut * @author Alexei Drummond * @version $Id$ */ package figtree.application; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; import com.itextpdf.text.pdf.PdfContentByte; import com.itextpdf.text.pdf.PdfTemplate; import com.itextpdf.text.pdf.PdfWriter; import figtree.application.preferences.*; import figtree.treeviewer.ExtendedTreeViewer; import jam.framework.*; import jam.controlpalettes.BasicControlPalette; import jam.controlpalettes.ControlPalette; import jam.mac.Utils; import java.awt.*; import java.awt.image.BufferedImage; import java.io.*; import java.net.URL; import java.util.*; import java.util.List; import jebl.evolution.io.ImportException; import jebl.evolution.io.NewickImporter; import jebl.evolution.trees.Tree; import javax.imageio.ImageIO; import javax.swing.*; import ch.randelshofer.quaqua.QuaquaManager; import org.apache.batik.dom.GenericDOMImplementation; import org.apache.batik.svggen.SVGGraphics2D; import org.w3c.dom.DOMImplementation; /** * Application class for FigTree including main() method for invoking it. * Uses JAM Application classes to create a MultiDoc Application. * * @author Andrew Rambaut * @version $Id$ * * $HeadURL$ * * $LastChangedBy$ * $LastChangedDate$ * $LastChangedRevision$ */ public class FigTreeApplication extends MultiDocApplication { public static final String VERSION = "1.4.3"; public static final String DATES = "2006-2016"; public static FigTreeApplication application; public FigTreeApplication(MenuBarFactory menuBarFactory, String nameString, String titleString, String aboutString, Icon icon, String websiteURLString, String helpURLString) { super(menuBarFactory, nameString, titleString, aboutString, icon, websiteURLString, helpURLString); // addPreferencesSection(new GeneralPreferencesSection()); addPreferencesSection(new AppearancePreferencesSection()); addPreferencesSection(new FontsPreferencesSection()); // addPreferencesSection(new AdvancedPreferencesSection()); } public DocumentFrame doOpenFile(File file) { DocumentFrame documentFrame = getUpperDocumentFrame(); if (documentFrame != null && documentFrame.getFile() == null) { documentFrame.openFile(file); return documentFrame; } else { return super.doOpenFile(file); } } public void doPaste() { } static public void createGraphic(String graphicFormat, int width, int height, String treeFileName, String graphicFileName) { try { BufferedReader bufferedReader = new BufferedReader(new FileReader(treeFileName)); String line = bufferedReader.readLine(); while (line != null && line.length() == 0) { line = bufferedReader.readLine(); } bufferedReader.close(); boolean isNexus = (line != null && line.toUpperCase().contains("#NEXUS")); Reader reader = new FileReader(treeFileName); Map<String, Object> settings = new HashMap<String, Object>(); ExtendedTreeViewer treeViewer = new ExtendedTreeViewer(); ControlPalette controlPalette = new BasicControlPalette(FigTreePanel.CONTROL_PALETTE_WIDTH, BasicControlPalette.DisplayMode.ONLY_ONE_OPEN); FigTreePanel figTreePanel = new FigTreePanel(null, treeViewer, controlPalette); // First of all, fully populate the settings map so that // all the settings have defaults controlPalette.getSettings(settings); List<Tree> trees = new ArrayList<Tree>(); if (isNexus) { FigTreeNexusImporter importer = new FigTreeNexusImporter(reader); trees.add(importer.importNextTree()); // 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); } } catch (EOFException ex) { break; } } } else { NewickImporter importer = new NewickImporter(reader, true); trees.add(importer.importNextTree()); } if (trees.size() == 0) { throw new ImportException("This file contained no trees."); } treeViewer.setTrees(trees); controlPalette.setSettings(settings); treeViewer.getContentPane().setSize(width, height); OutputStream stream; if (graphicFileName != null) { stream = new FileOutputStream(graphicFileName); } else { stream = System.out; } GraphicFormat format = null; if (graphicFormat.equals("PDF")) { format = GraphicFormat.PDF; } else if (graphicFormat.equals("SVG")) { format = GraphicFormat.SVG; } else if (graphicFormat.equals("GIF")) { format = GraphicFormat.GIF; } else if (graphicFormat.equals("PNG")) { format = GraphicFormat.PNG; } else if (graphicFormat.equals("JPEG")) { format = GraphicFormat.JPEG; } else { throw new RuntimeException("Unknown graphic format"); } if (graphicFileName != null) { System.out.println("Creating " + graphicFormat + " graphic: " + graphicFileName); } FigTreeFrame.exportGraphics(format, treeViewer.getContentPane(), stream); } catch(ImportException ie) { throw new RuntimeException("Error writing graphic file: " + ie.getMessage()); } catch(IOException ioe) { throw new RuntimeException("Error writing graphic file: " + ioe.getMessage()); } catch (DocumentException de) { throw new RuntimeException("Error writing graphic file: " + de.getMessage()); } } public static void centreLine(String line, int pageWidth) { int n = pageWidth - line.length(); int n1 = n / 2; for (int i = 0; i < n1; i++) { System.out.print(" "); } System.out.println(line); } public static void printTitle() { System.out.println(); centreLine("FigTree v" + VERSION + ", " + DATES, 60); centreLine("Tree Figure Drawing Tool", 60); centreLine("Andrew Rambaut", 60); System.out.println(); centreLine("Institute of Evolutionary Biology", 60); centreLine("University of Edinburgh", 60); centreLine("a.rambaut@ed.ac.uk", 60); System.out.println(); centreLine("http://tree.bio.ed.ac.uk/", 60); centreLine("Uses the Java Evolutionary Biology 2 Library (JEBL2)", 60); centreLine("http://jebl2.googlecode.com/", 60); centreLine("Thanks to Alexei Drummond, Joseph Heled, Philippe Lemey, ", 60); centreLine("Tulio de Oliveira, Oliver Pybus, Beth Shapiro & Marc Suchard", 60); System.out.println(); } public static void printUsage(Arguments arguments) { arguments.printUsage("figtree", "[<tree-file-name>] [<graphic-file-name>]"); System.out.println(); System.out.println(" Example: figtree test.tree"); System.out.println(" Example: figtree -graphic PDF test.tree test.pdf"); System.out.println(" Example: figtree -graphic GIF -width 320 -height 320 test.tree test.gif"); System.out.println(); } private static boolean lafLoaded = false; // Main entry point static public void main(String[] args) { // There is a major issue with languages that use the comma as a decimal separator. // To ensure compatibility between programs in the package, enforce the US locale. //Locale.setDefault(Locale.US); Arguments arguments = new Arguments( new Arguments.Option[] { new Arguments.StringOption("graphic", new String[] { "PDF", "SVG", // "SWF", "PS", "EMF", "PNG", // "GIF", "JPEG" }, false, "produce a graphic with the given format"), new Arguments.IntegerOption("width", "the width of the graphic in pixels"), new Arguments.IntegerOption("height", "the height of the graphic in pixels"), new Arguments.Option("url", "the input file is a URL"), new Arguments.Option("help", "option to print this message") }); try { arguments.parseArguments(args); } catch (Arguments.ArgumentException ae) { System.out.println(); System.out.println(ae.getMessage()); System.out.println(); printTitle(); printUsage(arguments); System.exit(1); } if (arguments.hasOption("help")) { printTitle(); printUsage(arguments); System.exit(0); } if (arguments.hasOption("graphic")) { int width = 800; int height = 600; if (arguments.hasOption("width")) { width = arguments.getIntegerOption("width"); } if (arguments.hasOption("height")) { height = arguments.getIntegerOption("height"); } // command line version... String graphicFormat = arguments.getStringOption("graphic"); String[] args2 = arguments.getLeftoverArguments(); if (args2.length == 0) { // no tree file specified printTitle(); printUsage(arguments); System.exit(0); } else if (args2.length == 1) { // no graphic file specified - write to stdout createGraphic(graphicFormat, width, height, args2[0], (args2.length > 1 ? args2[1] : null)); System.exit(0); } else { printTitle(); createGraphic(graphicFormat, width, height, args2[0], (args2.length > 1 ? args2[1] : null)); System.exit(0); } } if (Utils.isMacOSX()) { if (Utils.getMacOSXMajorVersionNumber() >= 5) { System.setProperty("apple.awt.brushMetalLook","true"); } System.setProperty("apple.laf.useScreenMenuBar","true"); System.setProperty("apple.awt.draggableWindowBackground","true"); System.setProperty("apple.awt.showGrowBox","true"); System.setProperty("apple.awt.graphics.UseQuartz","true"); try { // set the Quaqua Look and Feel in the UIManager javax.swing.SwingUtilities.invokeAndWait(new Runnable() { public void run() { try { // Only override the UI's necessary for ColorChooser and // FileChooser: Set includes = new HashSet(); includes.add("ColorChooser"); includes.add("FileChooser"); includes.add("Component"); includes.add("Browser"); includes.add("Tree"); includes.add("SplitPane"); includes.add("TitledBorder"); try { QuaquaManager.setIncludedUIs(includes); } catch (java.lang.NoClassDefFoundError ncdfe) { // this is to protect against the figtree.jar being // run on Mac OS without Quaqua on the classpath } UIManager.setLookAndFeel( "ch.randelshofer.quaqua.QuaquaLookAndFeel" ); lafLoaded = true; } catch (Exception e) { } } }); } catch (Exception e) { } UIManager.put("SystemFont", new Font("Lucida Grande", Font.PLAIN, 13)); UIManager.put("SmallSystemFont", new Font("Lucida Grande", Font.PLAIN, 11)); } if (!lafLoaded) { UIManager.LookAndFeelInfo[] lafs = UIManager.getInstalledLookAndFeels(); for (UIManager.LookAndFeelInfo laf : lafs) { System.out.println(laf); } try { // set the System Look and Feel in the UIManager javax.swing.SwingUtilities.invokeAndWait(new Runnable() { public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); // UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); } catch (Exception e) { e.printStackTrace(); } } }); } catch (Exception e) { e.printStackTrace(); } } java.net.URL url = FigTreeApplication.class.getResource("images/figtreeLogo.png"); Icon icon = null; if (url != null) { icon = new ImageIcon(url); } final String nameString = "FigTree"; String titleString = "<html>" + "<div style=\"font-family:'Helvetica Neue', Helvetica, Arial, 'Lucida Grande',sans-serif\">" + "<p style=\"font-weight: 100; font-size: 36px\">FigTree</p>" + "<p style=\"font-weight: 200; font-size: 14px\">Tree Figure Drawing Tool</p>" + "<p style=\"font-weight: 300; font-size: 12px\">Version " + VERSION + "</p>" + "</div></html>"; String aboutString = "<html>" + "<div style=\"font-family:'Helvetica Neue', Helvetica, Arial, 'Lucida Grande',sans-serif\">" + "<center>"+ DATES + ", Andrew Rambaut<br>" + "Institute of Evolutionary Biology, University of Edinburgh.<br>" + "<a href=\"http://tree.bio.ed.ac.uk/\">http://tree.bio.ed.ac.uk/</a><br><br>" + "Source code available from:<br>" + "<a href=\"https://figtree.googlecode.com/\">http://figtree.googlecode.com/</a><br><br>" + "Uses the Java Evolutionary Biology 2 Library (JEBL2)<br>" + "<a href=\"https://jebl2.googlecode.com/\">http://jebl2.googlecode.com/</a><br><br>" + "Thanks to Alexei Drummond, Joseph Heled, Philippe Lemey, <br>Tulio de Oliveira, Oliver Pybus, Beth Shapiro & Marc Suchard</center>" + "</div></html>"; String websiteURLString = "http://tree.bio.ed.ac.uk/software/figtree/"; String helpURLString = "http://tree.bio.ed.ac.uk/software/figtree/"; FigTreeApplication.application = new FigTreeApplication(new FigTreeMenuBarFactory(), nameString, titleString, aboutString, icon, websiteURLString, helpURLString); application.setDocumentFrameFactory(new DocumentFrameFactory() { public DocumentFrame createDocumentFrame(Application app, MenuBarFactory menuBarFactory) { return new FigTreeFrame(nameString + " v" + VERSION); } }); application.initialize(); boolean useURLs = arguments.hasOption("url"); String[] leftoverArguments = arguments.getLeftoverArguments(); if (leftoverArguments.length > 0) { for (String arg : leftoverArguments) { if (useURLs) { FigTreeFrame frame = (FigTreeFrame)application.doNew(); try { frame.readFromURL(new URL(arg)); } catch (IOException e) { } } else { application.doOpen(arg); } } } // if (!jam.mac.Utils.isMacOSX() && application.getUpperDocumentFrame() == null) { // // If we haven't opened any files by now, prompt for one... // application.doOpen(); // } if (application.getUpperDocumentFrame() == null) { // If we haven't opened any files by now, open a blank window... application.doNew(); } } }