Codebase list figtree / 22ea4e6
Merge tag 'upstream/1.4.3+dfsg' Upstream version 1.4.3+dfsg Fabian Klötzl 6 years ago
32 changed file(s) with 1158 addition(s) and 975 deletion(s). Raw diff Collapse all Expand all
0
1 *.class
2 build/*
3 classes/*
4 dist/*
5 out/*
6 FigTree*
7 tests/*
8 examples/*
0 _FigTree_
1
2 FigTree is designed as a graphical viewer of phylogenetic trees and as a program for producing publication-ready figures. As with most of my programs, it was written for my own needs so may not be as polished and feature-complete as a commercial program. In particular it is designed to display summarized and annotated trees produced by BEAST.
3
1111
1212 <property name="lib" location="lib"/>
1313 <property name="dist" location="dist"/>
14 <property name="packaging_tools" value="packaging_tools" />
1415
1516 <property environment="env"/>
1617
5960 </manifest>
6061 </jar>
6162
62 <jar jarfile="${dist}/figtree-pdf.jar">
63 <zipgroupfileset dir="${lib}" includes="jebl.jar"/>
64 <zipgroupfileset dir="${lib}" includes="jam.jar"/>
65 <zipgroupfileset dir="${lib}" includes="iText.jar"/>
66 <fileset dir="${build}" includes="**/*.class,**/*.properties,**/*.png,**/*.gif"/>
63 <jar jarfile="${dist}/figtreepanel.jar">
64 <fileset dir="${build}"
65 includes="figtree/panel/**/*.class,figtree/treeviewer/**/*.class,**/*.properties,**/*.png,**/*.gif"
66 />
67
6768 <manifest>
6869 <attribute name="Built-By" value="${user.name}"/>
69 <attribute name="Main-Class" value="figtree.application.FigTreePDF"/>
7070 </manifest>
7171 </jar>
72
73 <jar jarfile="${dist}/figtree-pdf.jar">
74 <fileset dir="${build}"
75 includes="figtree/panel/**/*.class,figtree/treeviewer/**/*.class,**/*.properties,**/*.png,**/*.gif"
76 />
77
78 <manifest>
79 <attribute name="Built-By" value="${user.name}"/>
80 </manifest>
81 </jar>
82
7283
7384 <war destfile="${dist}/figtree.war"
7485 webxml="WebRoot/WEB-INF/web.xml">
90101
91102 </target>
92103
93 <property name="version" value="1.4.2" />
104 <property name="version" value="1.4.3" />
105 <property name="version_number" value="1.4.3" />
94106 <property name="release_dir" value="release" />
95107 <property name="name" value="FigTree" />
96108
100112 <property name="Linux_dir" value="${release_dir}/Linux" />
101113 <property name="Windows_dir" value="${release_dir}/Windows" />
102114
103 <property name="Mac_package_dir_SL" value="${Mac_dir}/${name} v${version}" />
104115 <property name="Mac_package_dir" value="${Mac_dir}/${name} v${version}" />
105116 <property name="Linux_package_dir" value="${Linux_dir}/${name}_v${version}" />
106117 <property name="Windows_package_dir" value="${Windows_dir}/${name} v${version}" />
120131 classpath="${launch4j.dir}/launch4j.jar :${launch4j.dir}/lib/xstream.jar" />
121132
122133 <copy file="${dist}/figtree.jar" todir="${Windows_package_dir}/lib"/>
134 <!--
123135 <copy todir="${Windows_package_dir}/lib">
124136 <fileset dir="${Windows_dir}/lib"/>
125137 </copy>
138 -->
126139 <copy todir="${Windows_package_dir}">
127140 <fileset dir="${common_dir}/"/>
128141 </copy>
130143 <launch4j configFile="${Windows_dir}/FigTree_launch4j.xml"
131144 jar="${dist}/figtree.jar"
132145 outfile="${Windows_package_dir}/${name} v${version}.exe"
133 fileVersion="${version}.0"
146 fileVersion="${version_number}.0"
134147 txtFileVersion="${version}"
135 productVersion="${version}.0"
148 productVersion="${version_number}.0"
136149 txtProductVersion="${version}"
137150 />
138151
155168
156169 <copy file="${Linux_dir}/icons/figtree.png" todir="${Linux_package_dir}/images"/>
157170 <copy file="${dist}/figtree.jar" todir="${Linux_package_dir}/lib"/>
171 <!--
158172 <copy todir="${Linux_package_dir}/lib">
159173 <fileset dir="${Linux_dir}/lib"/>
160174 </copy>
175 -->
161176 <copy todir="${Linux_package_dir}">
162177 <fileset dir="${common_dir}/"/>
163178 </copy>
169184 <echo message="Linux/Unix version release is finished." />
170185 </target>
171186
172 <target name="mac_sl_release"
173 description="release Mac Snow Leopard version of FigTree">
174 <delete dir="${Mac_package_dir_SL}" />
187 <target name="mac_release"
188 description="release Mac version of FigTree">
189 <delete dir="${Mac_package_dir}" />
175190 <!-- Create the release directory -->
176 <mkdir dir="${Mac_package_dir_SL}" />
177
178 <copy file="${dist}/figtree.jar" todir="${Mac_package_dir_SL}/lib"/>
179 <copy file="${dist}/figtree-pdf.jar" todir="${Mac_package_dir_SL}/QuickLook Plugin/FigTreeQuickLookPlugin.qlgenerator/Contents/Resources"/>
180 <copy todir="${Mac_package_dir_SL}">
191 <mkdir dir="${Mac_package_dir}" />
192
193 <copy file="${dist}/figtree.jar" todir="${Mac_package_dir}/lib"/>
194 <copy file="${dist}/figtree-pdf.jar" todir="${Mac_package_dir}/QuickLook Plugin/FigTreeQuickLookPlugin.qlgenerator/Contents/Resources"/>
195 <copy todir="${Mac_package_dir}">
181196 <fileset dir="${common_dir}/"/>
182197 </copy>
183 <copy todir="${Mac_package_dir_SL}/QuickLook Plugin">
198 <copy todir="${Mac_package_dir}/QuickLook Plugin">
184199 <fileset dir="${Mac_dir}/QuickLook Plugin"/>
185200 </copy>
186201
187202 <taskdef name="jarbundler" classname="net.sourceforge.jarbundler.JarBundler"/>
188203
189204 <!-- create a jar bundle for the mac -->
190 <jarbundler dir="${Mac_package_dir_SL}"
205 <jarbundler dir="${Mac_package_dir}"
191206 name="${name} v${version}"
192207 mainclass="figtree.application.FigTreeApplication"
193208 icon="${Mac_dir}/icons/FigTree.icns"
209 stubfile="${packaging_tools}/mac/universalJavaApplicationStub"
210 useJavaXKey="true"
194211 jvmversion="1.6+"
195212 vmoptions="-Xmx1024M"
196213 arguments=""
197 version="${version} SL"
198 infostring="${name} v${version} SL, Copyright 2006-2014, Andrew Rambaut"
214 version="${version}"
215 build="1"
216 copyright="${name} v${version}, Copyright 2006-2015, Andrew Rambaut"
199217 bundleid="figtree" >
200218 <javaproperty name="apple.laf.useScreenMenuBar" value="true"/>
201219 <jarfileset dir="${dist}">
214232 </jarbundler>
215233
216234 <!-- remove code signing -->
235 <!--
217236 <exec executable="/usr/bin/codesign">
218237 <arg value="-s"/>
219238 <arg value="-"/>
220 <arg value="--force"/>
239 <arg value="- -force"/> remove space from between minus signs
221240 <arg value="${Mac_dir}/${name} v${version}/${name} v${version}.app"/>
222241 </exec>
223
242 -->
243
224244 <echo message="Building disk image." />
225245
226246 <!-- create disk image -->
Binary diff not shown
0 FigTree v1.4 2006-2012
0 FigTree v1.4.3 2006-2016
11 Andrew Rambaut
22
33 Institute of Evolutionary Biology
66
77
88 UNIX/Linux/Mac OS X (command-line) version README
9 Last updated: a.rambaut@ed.ac.uk - 8th October 2012
9 Last updated: a.rambaut@ed.ac.uk - 4th October 2016
1010
1111 Contents:
1212 1) INTRODUCTION
2626 ___________________________________________________________________________
2727 2) VERSION HISTORY
2828
29 v1.4.3 Released 4th October 2016
30
31 New features:
32 Node shape option can now show shapes for internal or external nodes or both.
33 Copying selected taxon labels when these are selected, subtree when branches are selected.
34 Selecting 'reverse axis' should automatically reverse the Time Scale scale factor. Previously the user needed to set this to -1.0.
35 When searching for text, scrolls to show highlighted tip.
36
37 Bugs fixed:
38 Issue 102: Large SVG files from figtree are broken
39 Issue 95: Changing the origin value for the Scale Axis does not work
40 Issue 94: The trait legend overlaps the tree.
41 Issue 93: Command-line PDF/SVG export options not working
42 Issue 92: Reading a file with a mix of integer and real node labels causes exception.
43 Issue 90: Export picture cuts the top of the higest tip label
44 Issue 79: Export of .SVG produces corrupt files
45
46 v1.4.2 Released 9th July 2014
47
48 New features:
49 New -url command line option allows reading of trees from URLs in pipelines.
50
51 Bugs fixed:
52 Issue 76: Scale axis should only show as many decimal places as necessary.
53 Issue 75: Export PNG & JPEG produce blank images.
54 Issue 64: Putting node bars on translates the tree to the right (now really fixed, I think).
55
56 v1.4.1 Released 14th June 2014
57
58 New features:
59 Copy selected subtrees to clipboard as NEXUS format.
60
61 New graphics export options (PDF, SVG, PNG & JPEG).
62
63 Control panel now scrolls and can be resized.
64
65 Bugs fixed:
66 Issue 23: Find bar opens slowly with big trees.
67 Issue 28: Filtering should work on currently display labels.
68 Issue 53: Option Tip Labels: "Colour By" does not render Names in colour.
69 Issue 57: Midpoint rooting not working correctly.
70 Issue 59: Clear Highlighting/Cartoon etc doesn't seem to work on individual branches.
71 Issue 62: When all clades are 'collapsed', the top triangle is clipped.
72 Issue 64: Putting node bars on translates the tree to the right.
73 Issue 69: Import annotation causes crash.
74
2975 v1.4 Released 8th October 2012.
3076
3177 New Features:
2929
3030 package figtree.application;
3131
32 import com.itextpdf.text.Document;
33 import com.itextpdf.text.DocumentException;
34 import com.itextpdf.text.pdf.PdfContentByte;
35 import com.itextpdf.text.pdf.PdfTemplate;
36 import com.itextpdf.text.pdf.PdfWriter;
3237 import figtree.application.preferences.*;
3338 import figtree.treeviewer.ExtendedTreeViewer;
3439 import jam.framework.*;
5156 import javax.swing.*;
5257
5358 import ch.randelshofer.quaqua.QuaquaManager;
59 import org.apache.batik.dom.GenericDOMImplementation;
60 import org.apache.batik.svggen.SVGGraphics2D;
61 import org.w3c.dom.DOMImplementation;
5462
5563 /**
5664 * Application class for FigTree including main() method for invoking it.
6775 */
6876 public class FigTreeApplication extends MultiDocApplication {
6977
70 public static final String VERSION = "1.4.2";
71 public static final String DATES = "2006-2014";
78 public static final String VERSION = "1.4.3";
79 public static final String DATES = "2006-2016";
7280
7381 public static FigTreeApplication application;
7482
163171
164172 GraphicFormat format = null;
165173 if (graphicFormat.equals("PDF")) {
166 if (graphicFileName != null) {
167 System.out.println("Creating PDF graphic: " + graphicFileName);
168 }
169174 format = GraphicFormat.PDF;
170 // } else if (graphicFormat.equals("PS")) {
171 // if (graphicFileName != null) {
172 // System.out.println("Creating PS graphic: " + graphicFileName);
173 // }
174 // g = new PSGraphics2D(stream, new Dimension(width, height));
175 // } else if (graphicFormat.equals("EMF")) {
176 // if (graphicFileName != null) {
177 // System.out.println("Creating EMF graphic: " + graphicFileName);
178 // }
179 // g = new EMFGraphics2D(stream, new Dimension(width, height));
180 // } else if (graphicFormat.equals("SVG")) {
181 // if (graphicFileName != null) {
182 // System.out.println("Creating SVG graphic: " + graphicFileName);
183 // }
184 // g = new SVGGraphics2D(stream, new Dimension(width, height));
185 // } else if (graphicFormat.equals("SWF")) {
186 // if (graphicFileName != null) {
187 // System.out.println("Creating SWF graphic: " + graphicFileName);
188 // }
189 // g = new SWFGraphics2D(stream, new Dimension(width, height));
175 } else if (graphicFormat.equals("SVG")) {
176 format = GraphicFormat.SVG;
190177 } else if (graphicFormat.equals("GIF")) {
191 if (graphicFileName != null) {
192 System.out.println("Creating GIF graphic: " + graphicFileName);
193 }
194178 format = GraphicFormat.GIF;
195179 } else if (graphicFormat.equals("PNG")) {
196180 format = GraphicFormat.PNG;
200184 throw new RuntimeException("Unknown graphic format");
201185 }
202186
203 JComponent comp = treeViewer.getContentPane();
204 BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
205 Graphics g = bi.createGraphics();
206 comp.paint(g);
207 g.dispose();
208 ImageIO.write(bi, format.getName(), stream);
187 if (graphicFileName != null) {
188 System.out.println("Creating " + graphicFormat + " graphic: " + graphicFileName);
189 }
190
191 FigTreeFrame.exportGraphics(format, treeViewer.getContentPane(), stream);
209192
210193 } catch(ImportException ie) {
211 throw new RuntimeException("Error writing graphic file: " + ie);
194 throw new RuntimeException("Error writing graphic file: " + ie.getMessage());
212195 } catch(IOException ioe) {
213 throw new RuntimeException("Error writing graphic file: " + ioe);
196 throw new RuntimeException("Error writing graphic file: " + ioe.getMessage());
197 } catch (DocumentException de) {
198 throw new RuntimeException("Error writing graphic file: " + de.getMessage());
214199 }
215200
216201 }
263248 new Arguments.Option[] {
264249 new Arguments.StringOption("graphic", new String[] {
265250 "PDF",
266 // "SVG",
251 "SVG",
267252 // "SWF", "PS", "EMF",
268253 "PNG",
269 "GIF",
254 // "GIF",
270255 "JPEG"
271256 }, false, "produce a graphic with the given format"),
272257 new Arguments.IntegerOption("width", "the width of the graphic in pixels"),
404389 icon = new ImageIcon(url);
405390 }
406391
407 final String nameString = "FigTree " + VERSION;
392 final String nameString = "FigTree";
408393 String titleString = "<html>" +
409394 "<div style=\"font-family:'Helvetica Neue', Helvetica, Arial, 'Lucida Grande',sans-serif\">" +
410395 "<p style=\"font-weight: 100; font-size: 36px\">FigTree</p>" +
2828 import figtree.treeviewer.decorators.DiscreteColourDecorator;
2929 import figtree.treeviewer.decorators.HSBDiscreteColourDecorator;
3030 import figtree.treeviewer.painters.StatesPainter;
31 import jebl.evolution.align.Output;
3132 import jebl.evolution.alignments.Alignment;
3233 import jebl.evolution.alignments.BasicAlignment;
3334 import jebl.evolution.graphs.Node;
5152 import org.apache.batik.svggen.SVGGraphics2D;
5253 import org.apache.batik.svggen.SVGGraphics2DIOException;
5354 import org.w3c.dom.DOMImplementation;
55 import org.w3c.dom.Element;
5456
5557 import javax.imageio.ImageIO;
5658 import javax.swing.*;
287289 public void actionPerformed(ActionEvent e){
288290 if (treeViewer.isRootingOn() && treeViewer.getRootingType() == TreePane.RootingType.USER_ROOTING) {
289291 JOptionPane.showMessageDialog(FigTreeFrame.this, "Cannot switch trees when user rooting option is on.\n" +
290 "Turn this option off to switch trees",
292 "Turn this option off to switch trees",
291293 "Unable to switch trees",
292294 JOptionPane.ERROR_MESSAGE);
293295
307309 public void actionPerformed(ActionEvent e){
308310 if (treeViewer.isRootingOn() && treeViewer.getRootingType() == TreePane.RootingType.USER_ROOTING) {
309311 JOptionPane.showMessageDialog(FigTreeFrame.this, "Cannot switch trees when user rooting option is on.\n" +
310 "Turn this option off to switch trees",
312 "Turn this option off to switch trees",
311313 "Unable to switch trees",
312314 JOptionPane.ERROR_MESSAGE);
313315
12151217 File file = new File(dialog.getDirectory(), dialog.getFile());
12161218
12171219 try {
1218 JComponent comp = treeViewer.getContentPane();
1219 switch (format) {
1220
1221 case PNG:
1222 case GIF:
1223 case BMP:
1224 case JPEG:
1225 exportGraphicsFile(format, comp, file);
1226 break;
1227 case EPS:
1228 throw new UnsupportedOperationException("EPS not handled");
1229 case SVG:
1230 exportSVGFile(comp, file);
1231 break;
1232 case PDF:
1233 exportPDFFile(comp, file);
1234 break;
1235 default:
1236 throw new UnsupportedOperationException("Format not handled: " + format);
1237
1238 }
1220 OutputStream stream = new FileOutputStream(file);
1221
1222 exportGraphics(format, treeViewer.getContentPane(), stream);
1223
1224 stream.flush();
1225 stream.close();
1226 } catch(DocumentException de) {
1227 JOptionPane.showMessageDialog(this, "Error writing PDF file: " + de,
1228 "Export PDF Error",
1229 JOptionPane.ERROR_MESSAGE);
12391230 } catch (IOException ioe) {
12401231 JOptionPane.showMessageDialog(this, "Error writing tree file: " + ioe.getMessage(),
12411232 "Export Error",
12461237
12471238 }
12481239
1249 private final void exportGraphicsFile(GraphicFormat format, JComponent component, File file) throws IOException {
1240 public final static void exportGraphics(GraphicFormat format, JComponent comp, OutputStream stream) throws IOException, DocumentException {
1241 switch (format) {
1242
1243 case PNG:
1244 case GIF:
1245 case BMP:
1246 case JPEG:
1247 exportGraphicsFile(format, comp, stream);
1248 break;
1249 case EPS:
1250 throw new UnsupportedOperationException("EPS not handled");
1251 case SVG:
1252 exportSVGFile(comp, stream);
1253 break;
1254 case PDF:
1255 exportPDFFile(comp, stream);
1256 break;
1257 default:
1258 throw new UnsupportedOperationException("Format not handled: " + format);
1259
1260 }
1261
1262 }
1263 private final static void exportGraphicsFile(GraphicFormat format, JComponent component, OutputStream stream) throws IOException {
12501264 int imageType = BufferedImage.TYPE_INT_RGB;
12511265
12521266 if (format == GraphicFormat.PNG) {
12621276 }
12631277 component.paint(g);
12641278 g.dispose();
1265 ImageIO.write(bi, format.getName(), file);
1266 }
1267
1268 private final void exportSVGFile(JComponent component, File file) throws IOException {
1279 ImageIO.write(bi, format.getName(), stream);
1280 }
1281
1282 private final static void exportSVGFile(JComponent component, OutputStream stream) throws IOException {
12691283 // Get a DOMImplementation and create an XML document
12701284 DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();
12711285 org.w3c.dom.Document document = domImpl.createDocument(null, "svg", null);
12741288 SVGGraphics2D svgGenerator = new SVGGraphics2D(document);
12751289
12761290 component.paint(svgGenerator);
1291 Element svgRoot = svgGenerator.getRoot();
1292 Rectangle2D bounds = component.getBounds();
1293 String viewBox = "0 0 " + bounds.getWidth() + " " + bounds.getHeight();
1294 svgRoot.setAttributeNS(null, svgGenerator.SVG_VIEW_BOX_ATTRIBUTE, viewBox);
1295 svgRoot.setAttributeNS(null, svgGenerator.SVG_WIDTH_ATTRIBUTE, Double.toString(bounds.getWidth()));
1296 svgRoot.setAttributeNS(null, svgGenerator.SVG_HEIGHT_ATTRIBUTE, Double.toString(bounds.getHeight()));
12771297
12781298 // Write svg file
1279 OutputStream outputStream = new FileOutputStream(file);
1280 Writer out = new OutputStreamWriter(outputStream, "UTF-8");
1281 svgGenerator.stream(out, true /* use css */);
1282 outputStream.flush();
1283 outputStream.close();
1284 }
1285
1286 public final void exportPDFFile(JComponent component, File file) {
1287 Rectangle2D bounds = treeViewer.getContentPane().getBounds();
1299 Writer out = new OutputStreamWriter(stream, "UTF-8");
1300 svgGenerator.stream(svgRoot, out, true /* use css */, false /* escaped */);
1301 }
1302
1303 public final static void exportPDFFile(JComponent component, OutputStream stream) throws DocumentException {
1304 Rectangle2D bounds = component.getBounds();
12881305 Document document = new Document(new com.itextpdf
12891306 .text.Rectangle((float)bounds.getWidth(), (float)bounds.getHeight()));
1290 try {
1291 // step 2
1292 PdfWriter writer;
1293 writer = PdfWriter.getInstance(document, new FileOutputStream(file));
1294 // step 3
1295 document.open();
1296 // step 4
1297 PdfContentByte cb = writer.getDirectContent();
1298 PdfTemplate tp = cb.createTemplate((float)bounds.getWidth(), (float)bounds.getHeight());
1299 Graphics2D g2d = tp.createGraphics((float)bounds.getWidth(), (float)bounds.getHeight(), new DefaultFontMapper());
1300 component.print(g2d);
1301 g2d.dispose();
1302 cb.addTemplate(tp, 0, 0);
1303 }
1304 catch(DocumentException de) {
1305 JOptionPane.showMessageDialog(this, "Error writing PDF file: " + de,
1306 "Export PDF Error",
1307 JOptionPane.ERROR_MESSAGE);
1308 }
1309 catch (FileNotFoundException e) {
1310 JOptionPane.showMessageDialog(this, "Error writing PDF file: " + e,
1311 "Export PDF Error",
1312 JOptionPane.ERROR_MESSAGE);
1313 }
1307 // step 2
1308 PdfWriter writer;
1309 writer = PdfWriter.getInstance(document, stream);
1310 // step 3
1311 document.open();
1312 // step 4
1313 PdfContentByte cb = writer.getDirectContent();
1314 PdfTemplate tp = cb.createTemplate((float)bounds.getWidth(), (float)bounds.getHeight());
1315 Graphics2D g2d = tp.createGraphics((float)bounds.getWidth(), (float)bounds.getHeight(), new DefaultFontMapper());
1316 component.print(g2d);
1317 g2d.dispose();
1318 cb.addTemplate(tp, 0, 0);
1319
13141320 document.close();
13151321 }
13161322
13171323 public void doCopy() {
13181324 StringWriter writer = new StringWriter();
13191325 try {
1320 writeTreeFile(writer, ExportTreeDialog.Format.NEXUS, true, false, false, true, true);
1326 if (treeViewer.getSelectionMode() == TreePaneSelector.SelectionMode.TAXA) {
1327 writeTaxa(writer);
1328 } else {
1329 writeTreeFile(writer, ExportTreeDialog.Format.NEXUS, true, false, false, true, true);
1330 }
13211331 } catch (IOException e) {
13221332 e.printStackTrace();
13231333 }
13791389
13801390 public void doSelectAll() {
13811391 treeViewer.selectAll();
1392 }
1393
1394 protected void writeTaxa(Writer writer) throws IOException {
1395 PrintWriter printWriter = new PrintWriter(writer);
1396 for (Taxon taxon : treeViewer.getSelectedTaxa()) {
1397 printWriter.println(taxon.getName());
1398 }
1399 writer.close();
13821400 }
13831401
13841402 protected void writeTreeFile(Writer writer, ExportTreeDialog.Format format,
18511869 private AnnotationDialog annotationDialog = null;
18521870 private AnnotationDialog copyAnnotationDialog = null;
18531871 private SelectAnnotationDialog selectAnnotationDialog = null;
1854 }
1872 }
+0
-205
src/figtree/application/FigTreePDF.java less more
0 /*
1 * FigTreePDF.java
2 *
3 * Copyright (C) 2006-2014 Andrew Rambaut
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20 /**
21 * TracerApp.java
22 *
23 * Title: Tracer
24 * Description: An application for analysing MCMC trace files.
25 * @author Andrew Rambaut
26 * @author Alexei Drummond
27 * @version $Id: FigTreeApplication.java,v 1.15 2007/09/10 14:52:02 rambaut Exp $
28 */
29
30 package figtree.application;
31
32 import com.itextpdf.text.Document;
33 import com.itextpdf.text.pdf.PdfContentByte;
34 import com.itextpdf.text.pdf.PdfTemplate;
35 import com.itextpdf.text.pdf.PdfWriter;
36 import figtree.treeviewer.ExtendedTreeViewer;
37 import jam.controlpalettes.BasicControlPalette;
38 import jam.controlpalettes.ControlPalette;
39
40 import java.io.*;
41 import java.util.*;
42 import java.util.List;
43 import java.awt.*;
44
45 import jebl.evolution.io.ImportException;
46 import jebl.evolution.io.NewickImporter;
47 import jebl.evolution.trees.Tree;
48
49 /**
50 * Commandline main() to generate PDF graphics from FigTree trees.
51 *
52 * @author Andrew Rambaut
53 * @version $Id$
54 *
55 * $HeadURL$
56 *
57 * $LastChangedBy$
58 * $LastChangedDate$
59 * $LastChangedRevision$
60 */
61 public class FigTreePDF {
62
63 public static final String VERSION = "1.4.1";
64 public static final String DATES = "2006-2014";
65
66 static public void createGraphic(int width, int height, String treeFileName, String graphicFileName) {
67
68 try {
69 BufferedReader bufferedReader = new BufferedReader(new FileReader(treeFileName));
70 String line = bufferedReader.readLine();
71 while (line != null && line.length() == 0) {
72 line = bufferedReader.readLine();
73 }
74
75 bufferedReader.close();
76
77 boolean isNexus = (line != null && line.toUpperCase().contains("#NEXUS"));
78
79 Reader reader = new FileReader(treeFileName);
80
81 Map<String, Object> settings = new HashMap<String, Object>();
82
83 ExtendedTreeViewer treeViewer = new ExtendedTreeViewer();
84 ControlPalette controlPalette = new BasicControlPalette(200, BasicControlPalette.DisplayMode.ONLY_ONE_OPEN);
85 FigTreePanel figTreePanel = new FigTreePanel(null, treeViewer, controlPalette);
86
87 // First of all, fully populate the settings map so that
88 // all the settings have defaults
89 controlPalette.getSettings(settings);
90
91 List<Tree> trees = new ArrayList<Tree>();
92
93 if (isNexus) {
94 FigTreeNexusImporter importer = new FigTreeNexusImporter(reader);
95 trees.add(importer.importNextTree());
96
97 // Try to find a figtree block and if found, parse the settings
98 while (true) {
99 try {
100 importer.findNextBlock();
101 if (importer.getNextBlockName().equalsIgnoreCase("FIGTREE")) {
102 importer.parseFigTreeBlock(settings);
103 }
104 } catch (EOFException ex) {
105 break;
106 }
107 }
108 } else {
109 NewickImporter importer = new NewickImporter(reader, true);
110 trees.add(importer.importNextTree());
111 }
112
113 if (trees.size() == 0) {
114 throw new ImportException("This file contained no trees.");
115 }
116
117 treeViewer.setTrees(trees);
118
119 controlPalette.setSettings(settings);
120
121 treeViewer.getContentPane().setSize(width, height);
122
123 OutputStream stream;
124 if (graphicFileName != null) {
125 stream = new FileOutputStream(graphicFileName);
126 } else {
127 stream = System.out;
128 }
129
130 Document document = new Document();
131 document.setPageSize(new com.itextpdf.text.Rectangle(width, height));
132 try {
133 PdfWriter writer = PdfWriter.getInstance(document, stream);
134 document.open();
135 PdfContentByte cb = writer.getDirectContent();
136 PdfTemplate tp = cb.createTemplate(width, height);
137 Graphics2D g2 = tp.createGraphics(width, height);
138 tp.setWidth(width);
139 tp.setHeight(height);
140 treeViewer.getContentPane().print(g2);
141 g2.dispose();
142 tp.sanityCheck(); // all the g2 content is written to tp, not cb
143 cb.addTemplate(tp, 0, 0);
144 cb.sanityCheck();
145 } catch (Exception e) {
146 System.err.println(e.getMessage());
147 }
148 document.close();
149
150 } catch(ImportException ie) {
151 throw new RuntimeException("Error writing graphic file: " + ie);
152 } catch(IOException ioe) {
153 throw new RuntimeException("Error writing graphic file: " + ioe);
154 }
155
156 }
157
158 // Main entry point
159 static public void main(String[] args) {
160
161 Arguments arguments = new Arguments(
162 new Arguments.Option[] {
163 new Arguments.IntegerOption("width", "the width of the graphic in pixels"),
164 new Arguments.IntegerOption("height", "the height of the graphic in pixels")
165 });
166
167 try {
168 arguments.parseArguments(args);
169 } catch (Arguments.ArgumentException ae) {
170 System.out.println();
171 System.out.println(ae.getMessage());
172 System.out.println();
173 System.exit(1);
174 }
175
176
177 int width = 800;
178 int height = 600;
179
180 if (arguments.hasOption("width")) {
181 width = arguments.getIntegerOption("width");
182 }
183
184 if (arguments.hasOption("height")) {
185 height = arguments.getIntegerOption("height");
186 }
187
188 // command line version...
189 String[] args2 = arguments.getLeftoverArguments();
190
191 if (args2.length == 0) {
192 // no tree file specified
193 System.exit(0);
194 } else if (args2.length == 1) {
195 // no graphic file specified - write to stdout
196 createGraphic(width, height, args2[0], (args2.length > 1 ? args2[1] : null));
197 System.exit(0);
198 } else {
199 createGraphic(width, height, args2[0], (args2.length > 1 ? args2[1] : null));
200 System.exit(0);
201 }
202 }
203 }
204
4747
4848 public final static int CONTROL_PALETTE_WIDTH = 200;
4949
50 private final static boolean SEPARATE_NODE_SHAPE_PANELS = true;
51
5052 public FigTreePanel(JFrame frame, final ExtendedTreeViewer treeViewer, ControlPalette controlPalette) {
5153
5254 this.treeViewer = treeViewer;
7981 controlPalette.addController(new LabelPainterController("Tip Labels", "tipLabels", tipLabelPainter, frame, attributeColourController, treeViewer));
8082 treeViewer.setTipLabelPainter(tipLabelPainter);
8183
84 // Create a node shape painter and its controller
85 if (SEPARATE_NODE_SHAPE_PANELS) {
86 final NodeShapePainter tipNodeShapePainter = new NodeShapePainter();
87 tipNodeShapePainter.setVisible(false);
88 controlPalette.addController(new NodeShapeController("Tip Shapes", NodeShapeController.NodeType.EXTERNAL, tipNodeShapePainter, attributeColourController, treeViewer));
89 treeViewer.setTipShapePainter(tipNodeShapePainter);
90 }
8291 // Create a node label painter and its controller
8392 final BasicLabelPainter nodeLabelPainter = new BasicLabelPainter(BasicLabelPainter.PainterIntent.NODE);
8493 nodeLabelPainter.setVisible(false);
8594 controlPalette.addController(new LabelPainterController("Node Labels", "nodeLabels", nodeLabelPainter, frame, attributeColourController, treeViewer));
8695 treeViewer.setNodeLabelPainter(nodeLabelPainter);
96
97 // Create a node shape painter and its controller
98 final NodeShapePainter nodeShapePainter = new NodeShapePainter();
99 nodeShapePainter.setVisible(false);
100 controlPalette.addController(new NodeShapeController("Node Shapes",
101 (SEPARATE_NODE_SHAPE_PANELS ? NodeShapeController.NodeType.INTERNAL : NodeShapeController.NodeType.BOTH),
102 nodeShapePainter, attributeColourController, treeViewer));
103 if (!SEPARATE_NODE_SHAPE_PANELS) {
104 treeViewer.setTipShapePainter(nodeShapePainter);
105 }
106 treeViewer.setNodeShapePainter(nodeShapePainter);
87107
88108 // Create a node bar painter and its controller
89109 final NodeBarPainter nodeBarPainter = new NodeBarPainter();
91111 nodeBarPainter.setVisible(false);
92112 controlPalette.addController(new NodeBarController("Node Bars", nodeBarPainter, treeViewer));
93113 treeViewer.setNodeBarPainter(nodeBarPainter);
94
95 // Create a node shape painter and its controller
96 final NodeShapePainter nodeShapePainter = new NodeShapePainter();
97 nodeShapePainter.setVisible(false);
98 controlPalette.addController(new NodeShapeController("Node Shapes", nodeShapePainter, attributeColourController, treeViewer));
99 treeViewer.setNodeShapePainter(nodeShapePainter);
100114
101115 // Create a branch label painter and its controller
102116 final BasicLabelPainter branchLabelPainter = new BasicLabelPainter(BasicLabelPainter.PainterIntent.BRANCH);
192206 if (utilityPanel == null) {
193207 return;
194208 }
195 slideOpenPanel.showUtilityPanel(utilityPanel);
196 }
197
198 public void hideUtilityPanel() {
199 slideOpenPanel.hideUtilityPanel();
200 }
201
202
203 public JPanel getUtilityPanel() {
204 return slideOpenPanel.getUtilityPanel();
205 }
209 slideOpenPanel.showUtilityPanel(utilityPanel);
210 }
211
212 public void hideUtilityPanel() {
213 slideOpenPanel.hideUtilityPanel();
214 }
215
216
217 public JPanel getUtilityPanel() {
218 return slideOpenPanel.getUtilityPanel();
219 }
206220
207221 public void toggleMidpointRoot() {
208222 treesController.toggleMidpointRoot();
5555 public class JSONTreeExporter implements TreeExporter {
5656 public static final String treeNameAttributeKey = "name";
5757
58 public final static Set<String> ATTRIBUTE_NAMES = new TreeSet<String>(Arrays.asList(new String[] { "location", "host", "Hx", "Nx", "posterior" }));
58 public final static Set<String> ATTRIBUTE_NAMES = new TreeSet<String>(Arrays.asList(new String[] {
59 "location", "host", "Hx", "Nx", "posterior", "country", "region" }));
5960 public final static String ORIGIN = "2013.34520547945";
6061
6162 public JSONTreeExporter(Writer writer) {
120120 treeViewer,
121121 "tipLabels", tipLabelPainter,
122122 "nodeLabels", nodeLabelPainter,
123 "branchLabels", branchLabelPainter));
123 "branchLabels", branchLabelPainter,
124 true, true));
124125
125126 controlPalette1.addController(new LabelPainterController(
126127 "tipLabels", tipLabelPainter,
138139 "tipLabels", tipLabelPainter,
139140 "nodeLabels", nodeLabelPainter,
140141 "branchLabels", branchLabelPainter,
141 true));
142 true, false));
142143
143144 controlPalette1.addController(new LabelPainterController(
144145 "tipLabels", tipLabelPainter,
159160 "tipLabels", tipLabelPainter,
160161 "nodeLabels", nodeLabelPainter,
161162 "branchLabels", branchLabelPainter,
162 true));
163 true, false));
163164
164165 controlPalette2.addController(new TreeColouringController(treeViewer, "Clustering:"));
165166 add(controlPalette2.getPanel(), BorderLayout.NORTH);
2121
2222 import figtree.treeviewer.TreeViewer;
2323 import figtree.treeviewer.painters.AttributeComboHelper;
24 import figtree.treeviewer.painters.AttributeComboHelperListener;
2425 import jam.controlpalettes.AbstractController;
2526 import jam.panels.OptionsPanel;
2627
3031 import java.util.Map;
3132
3233 import figtree.treeviewer.painters.LabelPainter;
33 import sun.jvm.hotspot.tools.FinalizerInfo;
3434
3535 /**
3636 * @author Andrew Rambaut
4747 private static final String DISPLAY_ATTRIBUTE_KEY = "displayAttribute";
4848
4949 public LabelPainterController(String tipKey,
50 final LabelPainter tipLabelPainter,
50 final SimpleLabelPainter tipLabelPainter,
5151 String nodeKey,
52 final LabelPainter nodeLabelPainter,
52 final SimpleLabelPainter nodeLabelPainter,
5353 String branchKey,
54 final LabelPainter branchLabelPainter,
54 final SimpleLabelPainter branchLabelPainter,
5555 final TreeViewer treeViewer) {
5656
5757 this.tipKey = tipKey;
6767
6868 }
6969
70 private JComboBox setupComboBox(String title, final LabelPainter labelPainter, final TreeViewer treeViewer) {
70 private JComboBox setupComboBox(String title, final SimpleLabelPainter labelPainter, final TreeViewer treeViewer) {
7171 // String[] attributes = labelPainter.getAttributes();
7272 final JComboBox displayAttributeCombo = new JComboBox();
7373 displayAttributeCombo.addItem("None");
74 new AttributeComboHelper(displayAttributeCombo, treeViewer, "None");
74 for (String attribute : labelPainter.getAttributes()) {
75 displayAttributeCombo.addItem(attribute);
76 }
77 new AttributeComboHelper(displayAttributeCombo, treeViewer, "None", labelPainter.getIntent());
7578 optionsPanel.addComponentWithLabel(title, displayAttributeCombo);
7679
7780 displayAttributeCombo.addItemListener(new ItemListener() {
7881 public void itemStateChanged(ItemEvent itemEvent) {
7982 String attribute = (String)displayAttributeCombo.getSelectedItem();
80 if (attribute.equals("none")) {
83 if (attribute == null || attribute.equalsIgnoreCase("none")) {
8184 labelPainter.setVisible(false);
8285 } else {
8386 labelPainter.setDisplayAttribute(attribute);
4949 */
5050 public class SimpleLabelPainter extends LabelPainter<Node> {
5151
52 public static final String TAXON_NAMES = "Taxon names";
53 public static final String NODE_AGES = "Node ages";
54 public static final String BRANCH_LENGTHS = "Branch lengths";
55
56 public SimpleLabelPainter(PainterIntent intent) {
52 public static final String NAMES = "Names";
53 public static final String NODE_AGES = "Node ages";
54 public static final String BRANCH_LENGTHS = "Branch lengths";
55
56 public SimpleLabelPainter(PainterIntent intent) {
5757 super(intent);
5858
59 setupAttributes(null);
60
61 if (this.displayAttribute == null) {
62 this.displayAttribute = attributes[0];
63 } else {
64 this.displayAttribute = "";
65 }
66
67 }
68
69 public void setupAttributes(Collection<? extends Tree> trees) {
70
71 List<String> attributeNames = new ArrayList<String>();
72 switch( intent ) {
73 case TIP: {
74 attributeNames.add(TAXON_NAMES);
75 attributeNames.add(NODE_AGES);
76 attributeNames.add(BRANCH_LENGTHS);
77 break;
78 }
79 case NODE: {
80 attributeNames.add(NODE_AGES);
81 attributeNames.add(BRANCH_LENGTHS);
82 break;
83 }
84 case BRANCH: {
85 attributeNames.add(BRANCH_LENGTHS);
86 attributeNames.add(NODE_AGES);
87 break;
88 }
89 }
90
91 if (trees != null) {
92 for (Tree tree : trees) {
93 Set<String> nodeAttributes = new TreeSet<String>();
94 if (intent == PainterIntent.TIP) {
95 for (Node node : tree.getExternalNodes()) {
96 nodeAttributes.addAll(node.getAttributeNames());
97 }
98 } else if (intent == PainterIntent.NODE) {
99 for (Node node : tree.getInternalNodes()) {
100 nodeAttributes.addAll(node.getAttributeNames());
101 }
102 } else {
103 for (Node node : tree.getNodes()) {
104 nodeAttributes.addAll(node.getAttributeNames());
105 }
106 }
107 for (String attributeName : nodeAttributes) {
108 if (!attributeName.startsWith("!")) {
109 attributeNames.add(attributeName);
110 }
111 }
112 }
113 }
114
115 this.attributes = new String[attributeNames.size()];
116 attributeNames.toArray(this.attributes);
117
118 firePainterSettingsChanged();
119 }
120
121 public void setTreePane(TreePane treePane) {
122 this.treePane = treePane;
123 }
124
125 public Decorator getBorderDecorator() {
126 return borderDecorator;
127 }
128
129 public void setBorderDecorator(Decorator borderDecorator) {
130 this.borderDecorator = borderDecorator;
131 }
132
133 public Decorator getTextDecorator() {
134 return textDecorator;
135 }
136
137 public void setTextDecorator(Decorator textDecorator) {
138 this.textDecorator = textDecorator;
139 }
59 setupAttributes(null);
60
61 if (this.displayAttribute == null) {
62 this.displayAttribute = attributes[0];
63 } else {
64 this.displayAttribute = "";
65 }
66
67 }
68
69 public void setupAttributes(Collection<? extends Tree> trees) {
70
71 List<String> attributeNames = new ArrayList<String>();
72 switch (getIntent()) {
73 case TIP: {
74 attributeNames.add(NAMES);
75 attributeNames.add(NODE_AGES);
76 attributeNames.add(BRANCH_LENGTHS);
77 break;
78 }
79 case NODE: {
80 attributeNames.add(NODE_AGES);
81 attributeNames.add(BRANCH_LENGTHS);
82 break;
83 }
84 case BRANCH: {
85 attributeNames.add(BRANCH_LENGTHS);
86 attributeNames.add(NODE_AGES);
87 break;
88 }
89 }
90
91 if (trees != null) {
92 for (Tree tree : trees) {
93 Set<String> nodeAttributes = new TreeSet<String>();
94 if (getIntent() == PainterIntent.TIP) {
95 for (Node node : tree.getExternalNodes()) {
96 nodeAttributes.addAll(node.getAttributeNames());
97 }
98 } else if (getIntent() == PainterIntent.NODE) {
99 for (Node node : tree.getInternalNodes()) {
100 nodeAttributes.addAll(node.getAttributeNames());
101 }
102 } else {
103 for (Node node : tree.getNodes()) {
104 nodeAttributes.addAll(node.getAttributeNames());
105 }
106 }
107 for (String attributeName : nodeAttributes) {
108 if (!attributeName.startsWith("!")) {
109 attributeNames.add(attributeName);
110 }
111 }
112 }
113 }
114
115 this.attributes = new String[attributeNames.size()];
116 attributeNames.toArray(this.attributes);
117
118 firePainterSettingsChanged();
119 }
120
121 public void setTreePane(TreePane treePane) {
122 this.treePane = treePane;
123 }
124
125 public Decorator getBorderDecorator() {
126 return borderDecorator;
127 }
128
129 public void setBorderDecorator(Decorator borderDecorator) {
130 this.borderDecorator = borderDecorator;
131 }
132
133 public Decorator getTextDecorator() {
134 return textDecorator;
135 }
136
137 public void setTextDecorator(Decorator textDecorator) {
138 this.textDecorator = textDecorator;
139 }
140140
141141 public Set<Attributable> getAttributableItems() {
142142 return null;
143143 }
144144
145145 public Tree getTree() {
146 return treePane.getTree();
147 }
148
149 protected String getLabel(Tree tree, Node node) {
150 if (displayAttribute.equalsIgnoreCase(TAXON_NAMES)) {
151 Taxon taxon = tree.getTaxon(node);
152 if (taxon != null) {
153 if (textDecorator != null) {
154 textDecorator.setItem(taxon);
155 }
156 return taxon.getName();
157 } else {
158 String name = (String)node.getAttribute("name");
159 if (name != null) {
160 return name;
161 }
162 return "unlabelled";
163 }
164 }
165
166 if ( tree instanceof RootedTree) {
167 final RootedTree rtree = (RootedTree) tree;
168
169 if (textDecorator != null) {
170 textDecorator.setItem(node);
171 }
172
173 if (displayAttribute.equalsIgnoreCase(NODE_AGES) ) {
174 return getNumberFormat().format(rtree.getHeight(node));
175 } else if (displayAttribute.equalsIgnoreCase(BRANCH_LENGTHS) ) {
176 return getNumberFormat().format(rtree.getLength(node));
177 }
178 }
179
180 return formatValue(node.getAttribute(displayAttribute));
181 }
182
183 private String formatValue(Object value) {
184 if (value != null) {
185 if (value instanceof Double) {
186 return getNumberFormat().format(value);
187 } else if (value instanceof Object[]) {
188 Object[] values = (Object[])value;
189
190 if (values.length == 0) return null;
191 if (values.length == 1) return formatValue(values[0]);
192
193 StringBuilder builder = new StringBuilder("[");
194 builder.append(formatValue(values[0]));
195 for (int i = 1; i < values.length; i++) {
196 builder.append(",");
197 builder.append(formatValue(values[i]));
198 }
199 builder.append("]");
200 return builder.toString();
201 }
202 return value.toString();
203 }
204 return null;
205 }
206
207 public Rectangle2D calibrate(Graphics2D g2, Node item) {
208 Tree tree = treePane.getTree();
209
210 String label = getLabel(tree, item);
211
212 final Font oldFont = g2.getFont();
213 if (textDecorator != null) {
214 g2.setFont(textDecorator.getFont(getFont()));
215 } else {
216 g2.setFont(getFont());
217 }
218
219 FontMetrics fm = g2.getFontMetrics();
220 preferredHeight = fm.getHeight();
221 preferredWidth = 0;
222
223 if (label != null) {
224 Rectangle2D rect = fm.getStringBounds(label, g2);
225 preferredWidth = rect.getWidth();
226 }
227
228 yOffset = (float)fm.getAscent();
229
230 g2.setFont(oldFont);
231
232 return new Rectangle2D.Double(0.0, 0.0, preferredWidth, preferredHeight);
233 }
234
235 public double getPreferredWidth() {
236 return preferredWidth;
237 }
238
239 public double getPreferredHeight() {
240 return preferredHeight;
241 }
242
243 public double getHeightBound() {
244 return preferredHeight + yOffset;
245 }
246
247 public void paint(Graphics2D g2, Node item, Justification justification, Rectangle2D bounds) {
248 Tree tree = treePane.getTree();
249
250 if (TreePane.DEBUG_OUTLINE) {
251 g2.setPaint(Color.red);
252 g2.draw(bounds);
253 }
254
255 String label = getLabel(tree, item);
256
257 Font oldFont = g2.getFont();
258
259 Paint backgroundPaint = getBackground();
260 Paint borderPaint = getBorderPaint();
261 Stroke borderStroke = getBorderStroke();
262
263 if (borderDecorator != null) {
264 backgroundPaint = borderDecorator.getPaint(backgroundPaint);
265 borderPaint = borderDecorator.getPaint(borderPaint);
266 borderStroke = borderDecorator.getStroke(borderStroke);
267 }
268
269 if (backgroundPaint != null) {
270 g2.setPaint(backgroundPaint);
271 g2.fill(bounds);
272 }
273
274 if (borderPaint != null && borderStroke != null) {
275 g2.setPaint(borderPaint);
276 g2.setStroke(borderStroke);
277 g2.draw(bounds);
278 }
279
280 if (textDecorator != null) {
281 g2.setPaint(textDecorator.getPaint(getForeground()));
282 g2.setFont(textDecorator.getFont(getFont()));
283 } else {
284 g2.setPaint(getForeground());
285 g2.setFont(getFont());
286 }
287
288 if (label != null) {
289
290 Rectangle2D rect = null;
291 if (justification == Justification.CENTER || justification == Justification.RIGHT)
292 rect = g2.getFontMetrics().getStringBounds(label, g2);
293
294 float xOffset;
295 float y = yOffset + (float) bounds.getY();
296 switch (justification) {
297 case CENTER:
298 xOffset = (float)(-rect.getWidth()/2.0);
299 y = yOffset + (float) rect.getY();
146 return treePane.getTree();
147 }
148
149 protected String getLabel(Tree tree, Node node) {
150 if (displayAttribute.equalsIgnoreCase(NAMES)) {
151 Taxon taxon = tree.getTaxon(node);
152 if (taxon != null) {
153 if (textDecorator != null) {
154 textDecorator.setItem(taxon);
155 }
156 return taxon.getName();
157 } else {
158 String name = (String)node.getAttribute("name");
159 if (name != null) {
160 return name;
161 }
162 return "unlabelled";
163 }
164 }
165
166 if ( tree instanceof RootedTree) {
167 final RootedTree rtree = (RootedTree) tree;
168
169 if (textDecorator != null) {
170 textDecorator.setItem(node);
171 }
172
173 if (displayAttribute.equalsIgnoreCase(NODE_AGES) ) {
174 return getNumberFormat().format(rtree.getHeight(node));
175 } else if (displayAttribute.equalsIgnoreCase(BRANCH_LENGTHS) ) {
176 return getNumberFormat().format(rtree.getLength(node));
177 }
178 }
179
180 return formatValue(node.getAttribute(displayAttribute));
181 }
182
183 private String formatValue(Object value) {
184 if (value != null) {
185 if (value instanceof Double) {
186 return getNumberFormat().format(value);
187 } else if (value instanceof Object[]) {
188 Object[] values = (Object[])value;
189
190 if (values.length == 0) return null;
191 if (values.length == 1) return formatValue(values[0]);
192
193 StringBuilder builder = new StringBuilder("[");
194 builder.append(formatValue(values[0]));
195 for (int i = 1; i < values.length; i++) {
196 builder.append(",");
197 builder.append(formatValue(values[i]));
198 }
199 builder.append("]");
200 return builder.toString();
201 }
202 return value.toString();
203 }
204 return null;
205 }
206
207 public Rectangle2D calibrate(Graphics2D g2, Node item) {
208 Tree tree = treePane.getTree();
209
210 String label = getLabel(tree, item);
211
212 final Font oldFont = g2.getFont();
213 if (textDecorator != null) {
214 g2.setFont(textDecorator.getFont(getFont()));
215 } else {
216 g2.setFont(getFont());
217 }
218
219 FontMetrics fm = g2.getFontMetrics();
220 preferredHeight = fm.getHeight();
221 preferredWidth = 0;
222
223 if (label != null) {
224 Rectangle2D rect = fm.getStringBounds(label, g2);
225 preferredWidth = rect.getWidth();
226 }
227
228 yOffset = (float)fm.getAscent();
229
230 g2.setFont(oldFont);
231
232 return new Rectangle2D.Double(0.0, 0.0, preferredWidth, preferredHeight);
233 }
234
235 public double getPreferredWidth() {
236 return preferredWidth;
237 }
238
239 public double getPreferredHeight() {
240 return preferredHeight;
241 }
242
243 public double getHeightBound() {
244 return preferredHeight + yOffset;
245 }
246
247 public void paint(Graphics2D g2, Node item, Justification justification, Rectangle2D bounds) {
248 Tree tree = treePane.getTree();
249
250 if (TreePane.DEBUG_OUTLINE) {
251 g2.setPaint(Color.red);
252 g2.draw(bounds);
253 }
254
255 String label = getLabel(tree, item);
256
257 Font oldFont = g2.getFont();
258
259 Paint backgroundPaint = getBackground();
260 Paint borderPaint = getBorderPaint();
261 Stroke borderStroke = getBorderStroke();
262
263 if (borderDecorator != null) {
264 backgroundPaint = borderDecorator.getPaint(backgroundPaint);
265 borderPaint = borderDecorator.getPaint(borderPaint);
266 borderStroke = borderDecorator.getStroke(borderStroke);
267 }
268
269 if (backgroundPaint != null) {
270 g2.setPaint(backgroundPaint);
271 g2.fill(bounds);
272 }
273
274 if (borderPaint != null && borderStroke != null) {
275 g2.setPaint(borderPaint);
276 g2.setStroke(borderStroke);
277 g2.draw(bounds);
278 }
279
280 if (textDecorator != null) {
281 g2.setPaint(textDecorator.getPaint(getForeground()));
282 g2.setFont(textDecorator.getFont(getFont()));
283 } else {
284 g2.setPaint(getForeground());
285 g2.setFont(getFont());
286 }
287
288 if (label != null) {
289
290 Rectangle2D rect = null;
291 if (justification == Justification.CENTER || justification == Justification.RIGHT)
292 rect = g2.getFontMetrics().getStringBounds(label, g2);
293
294 float xOffset;
295 float y = yOffset + (float) bounds.getY();
296 switch (justification) {
297 case CENTER:
298 xOffset = (float)(-rect.getWidth()/2.0);
299 y = yOffset + (float) rect.getY();
300300 //xOffset = (float) (bounds.getX() + (bounds.getWidth() - rect.getWidth()) / 2.0);
301 break;
302 case FLUSH:
303 case LEFT:
304 xOffset = (float) bounds.getX();
305 break;
306 case RIGHT:
307 xOffset = (float) (bounds.getX() + bounds.getWidth() - rect.getWidth());
308 break;
309 default:
310 throw new IllegalArgumentException("Unrecognized alignment enum option");
311 }
312
313 g2.drawString(label, xOffset, y);
314 }
315
316 g2.setFont(oldFont);
317 }
318
319 public String[] getAttributes() {
320 return attributes;
321 }
301 break;
302 case FLUSH:
303 case LEFT:
304 xOffset = (float) bounds.getX();
305 break;
306 case RIGHT:
307 xOffset = (float) (bounds.getX() + bounds.getWidth() - rect.getWidth());
308 break;
309 default:
310 throw new IllegalArgumentException("Unrecognized alignment enum option");
311 }
312
313 g2.drawString(label, xOffset, y);
314 }
315
316 g2.setFont(oldFont);
317 }
318
319 public String[] getAttributes() {
320 return attributes;
321 }
322322
323323 public String getDisplayAttribute() {
324324 return displayAttribute;
325325 }
326326
327327 public void setDisplayAttribute(String displayAttribute) {
328 this.displayAttribute = displayAttribute;
329 firePainterChanged();
330 }
331
332 private PainterIntent intent;
333
334 private double preferredWidth;
335 private double preferredHeight;
336 private float yOffset;
337
338 protected String displayAttribute;
339 protected String[] attributes;
340
341 protected TreePane treePane;
342
343 private Decorator textDecorator = null;
344 private Decorator borderDecorator = null;
328 this.displayAttribute = displayAttribute;
329 firePainterChanged();
330 }
331
332 private double preferredWidth;
333 private double preferredHeight;
334 private float yOffset;
335
336 protected String displayAttribute;
337 protected String[] attributes;
338
339 protected TreePane treePane;
340
341 private Decorator textDecorator = null;
342 private Decorator borderDecorator = null;
345343
346344 }
1919
2020 package figtree.panel;
2121
22 import jebl.evolution.trees.SortedRootedTree;
2223 import jebl.evolution.trees.Tree;
2324 import jebl.evolution.graphs.Node;
2425 import figtree.treeviewer.TreeViewer;
9394 final LabelPainter nodeLabelPainter,
9495 String branchKey,
9596 final LabelPainter branchLabelPainter) {
96 this(treeViewer, tipKey, tipLabelPainter, nodeKey, nodeLabelPainter, branchKey, branchLabelPainter, true);
97 this(treeViewer, tipKey, tipLabelPainter, nodeKey, nodeLabelPainter, branchKey, branchLabelPainter, true, false);
9798 }
9899
99100 public TreeAppearanceController(final TreeViewer treeViewer,
103104 final LabelPainter nodeLabelPainter,
104105 String branchKey,
105106 final LabelPainter branchLabelPainter,
106 boolean hideColouring) {
107 boolean hideColouring,
108 boolean ordering) {
107109 this.treeViewer = treeViewer;
108110
109111 this.hideColouring = hideColouring;
246248 }
247249 });
248250 }
249 }
251
252 if (ordering) {
253 orderCombo = new JComboBox(new String[]{"Off",
254 SortedRootedTree.BranchOrdering.INCREASING_NODE_DENSITY.toString(),
255 SortedRootedTree.BranchOrdering.DECREASING_NODE_DENSITY.toString()});
256 orderCombo.setOpaque(false);
257 orderCombo.setSelectedItem(treeViewer.isOrderBranchesOn() ?
258 treeViewer.getBranchOrdering().ordinal() + 1 : 0);
259 orderCombo.addItemListener(new ItemListener() {
260 public void itemStateChanged(ItemEvent itemEvent) {
261 if (orderCombo.getSelectedIndex() == 0) {
262 treeViewer.setOrderBranchesOn(false);
263 } else {
264 treeViewer.setOrderBranchesOn(true);
265 treeViewer.setBranchOrdering(SortedRootedTree.BranchOrdering.values()[orderCombo.getSelectedIndex() - 1]);
266 }
267 }
268 });
269
270 optionsPanel.addComponentWithLabel("Order:", orderCombo);
271 } else {
272 orderCombo = null;
273 }
274 }
250275
251276 private void setupAttributes(Collection<? extends Tree> trees) {
252277 Object selected = branchColourAttributeCombo.getSelectedItem();
360385 private final JSpinner fontSizeSpinner;
361386 private final JSpinner digitsSpinner;
362387
363 private final TreeViewer treeViewer;
388 private final JComboBox orderCombo;
389
390 private final TreeViewer treeViewer;
364391
365392 private final String tipKey;
366393 private final String nodeKey;
5959
6060
6161 public TreesController(final TreeViewer treeViewer) {
62 this(treeViewer, true, true, true);
63 }
64 public TreesController(final TreeViewer treeViewer,
65 final boolean rooting,
66 final boolean ordering,
67 final boolean transforming) {
6268 this.treeViewer = treeViewer;
6369
6470 titleLabel = new JLabel(CONTROLLER_TITLE);
6571
6672 optionsPanel = new OptionsPanel();
6773
68 rootingCheck = new JCheckBox("Midpoint root");
69 rootingCheck.setOpaque(false);
70 optionsPanel.addComponent(rootingCheck);
71
72 rootingCheck.setSelected(treeViewer.isRootingOn());
73
74 rootingCheck.addActionListener(new ActionListener() {
75 @Override
76 public void actionPerformed(ActionEvent actionEvent) {
77 if (rootingCheck.isSelected()) {
78 treeViewer.setRootingOn(true);
79 treeViewer.setRootingType(TreePane.RootingType.MID_POINT);
80 } else {
81 treeViewer.setRootingOn(false);
82 treeViewer.setRootingType(TreePane.RootingType.USER_ROOTING);
83 }
84
85 }
86 });
87
88 orderCombo = new JComboBox(new String[] {"Off",
89 SortedRootedTree.BranchOrdering.INCREASING_NODE_DENSITY.toString(),
90 SortedRootedTree.BranchOrdering.DECREASING_NODE_DENSITY.toString()});
91 orderCombo.setOpaque(false);
92 orderCombo.setSelectedItem(treeViewer.isOrderBranchesOn() ?
93 treeViewer.getBranchOrdering().ordinal() + 1 : 0);
94 orderCombo.addItemListener(new ItemListener() {
95 public void itemStateChanged(ItemEvent itemEvent) {
96 if (orderCombo.getSelectedIndex() == 0) {
97 treeViewer.setOrderBranchesOn(false);
98 } else {
99 treeViewer.setOrderBranchesOn(true);
100 treeViewer.setBranchOrdering(SortedRootedTree.BranchOrdering.values()[orderCombo.getSelectedIndex() - 1]);
101 }
102 }
103 });
104
105 optionsPanel.addComponentWithLabel("Order:", orderCombo);
106
107 transformCombo = new JComboBox(new String[] {"Off",
108 TransformedRootedTree.Transform.CLADOGRAM.toString(),
109 TransformedRootedTree.Transform.PROPORTIONAL.toString(),
110 TransformedRootedTree.Transform.EQUAL_LENGTHS.toString()});
111 transformCombo.setOpaque(false);
112 transformCombo.setSelectedItem(treeViewer.isOrderBranchesOn() ?
113 treeViewer.getBranchTransform().ordinal() + 1 : 0);
114 transformCombo.addItemListener(new ItemListener() {
115 public void itemStateChanged(ItemEvent itemEvent) {
116 if (transformCombo.getSelectedIndex() == 0) {
117 treeViewer.setTransformBranchesOn(false);
118 } else {
119 treeViewer.setTransformBranchesOn(true);
120 treeViewer.setBranchTransform(TransformedRootedTree.Transform.values()[transformCombo.getSelectedIndex() - 1]);
121 }
122 }
123 });
124 optionsPanel.addComponentWithLabel("Transform:", transformCombo);
74 if (rooting) {
75 rootingCheck = new JCheckBox("Midpoint root");
76 rootingCheck.setOpaque(false);
77 optionsPanel.addComponent(rootingCheck);
78
79 rootingCheck.setSelected(treeViewer.isRootingOn());
80
81 rootingCheck.addActionListener(new ActionListener() {
82 @Override
83 public void actionPerformed(ActionEvent actionEvent) {
84 if (rootingCheck.isSelected()) {
85 treeViewer.setRootingOn(true);
86 treeViewer.setRootingType(TreePane.RootingType.MID_POINT);
87 } else {
88 treeViewer.setRootingOn(false);
89 treeViewer.setRootingType(TreePane.RootingType.USER_ROOTING);
90 }
91
92 }
93 });
94 } else {
95 rootingCheck = null;
96 }
97
98 if (ordering) {
99 orderCombo = new JComboBox(new String[]{"Off",
100 SortedRootedTree.BranchOrdering.INCREASING_NODE_DENSITY.toString(),
101 SortedRootedTree.BranchOrdering.DECREASING_NODE_DENSITY.toString()});
102 orderCombo.setOpaque(false);
103 orderCombo.setSelectedItem(treeViewer.isOrderBranchesOn() ?
104 treeViewer.getBranchOrdering().ordinal() + 1 : 0);
105 orderCombo.addItemListener(new ItemListener() {
106 public void itemStateChanged(ItemEvent itemEvent) {
107 if (orderCombo.getSelectedIndex() == 0) {
108 treeViewer.setOrderBranchesOn(false);
109 } else {
110 treeViewer.setOrderBranchesOn(true);
111 treeViewer.setBranchOrdering(SortedRootedTree.BranchOrdering.values()[orderCombo.getSelectedIndex() - 1]);
112 }
113 }
114 });
115
116 optionsPanel.addComponentWithLabel("Order:", orderCombo);
117 } else {
118 orderCombo = null;
119 }
120
121 if (transforming) {
122 transformCombo = new JComboBox(new String[]{"Off",
123 TransformedRootedTree.Transform.CLADOGRAM.toString(),
124 TransformedRootedTree.Transform.PROPORTIONAL.toString(),
125 TransformedRootedTree.Transform.EQUAL_LENGTHS.toString()});
126 transformCombo.setOpaque(false);
127 transformCombo.setSelectedItem(treeViewer.isOrderBranchesOn() ?
128 treeViewer.getBranchTransform().ordinal() + 1 : 0);
129 transformCombo.addItemListener(new ItemListener() {
130 public void itemStateChanged(ItemEvent itemEvent) {
131 if (transformCombo.getSelectedIndex() == 0) {
132 treeViewer.setTransformBranchesOn(false);
133 } else {
134 treeViewer.setTransformBranchesOn(true);
135 treeViewer.setBranchTransform(TransformedRootedTree.Transform.values()[transformCombo.getSelectedIndex() - 1]);
136 }
137 }
138 });
139 optionsPanel.addComponentWithLabel("Transform:", transformCombo);
140 } else {
141 transformCombo = null;
142 }
125143 }
126144
127145 public JComponent getTitleComponent() {
273273 return treePane.getSelectedTips();
274274 }
275275
276 public Set<Taxon> getSelectedTaxa() {
277 return treePane.getSelectedTaxa();
278 }
279
276280 /**
277281 * Select taxa with a search string matching the currently displayed attribute
278282 * @param searchType
287291 }
288292
289293 selectTaxa(attributeName, searchType, searchString, caseSensitive);
294
295 scrollToSelectedTips();
290296 }
291297
292298 public void selectTaxa(String attributeName, TextSearchType searchType, String searchString, boolean caseSensitive) {
503509 return false;
504510 }
505511
512 public void scrollToSelectedTips() {
513 Set<Node> selectedTips = treePane.getSelectedTips();
514 if (selectedTips.size() > 0) {
515 Point point = treePane.getLocationOfTip(selectedTips.iterator().next());
516 treePane.scrollPointToVisible(point);
517 }
518 }
519
506520 public void cartoonSelectedNodes() {
507521 treePane.cartoonSelectedNodes();
508522 fireTreeSettingsChanged();
589603 public void removeTreeSelectionListener(TreeSelectionListener treeSelectionListener) {
590604 treePane.removeTreeSelectionListener(treeSelectionListener);
591605 }
606
607 public TreePaneSelector.SelectionMode getSelectionMode() {
608 return treePaneSelector.getSelectionMode();
609 }
610
592611
593612 public void setSelectionMode(TreePaneSelector.SelectionMode selectionMode) {
594613 TreePaneSelector.SelectionMode oldSelectionMode = treePaneSelector.getSelectionMode();
628647 // nodeBarPainter.setupAttributes(trees);
629648 }
630649
650 public void setTipShapePainter(NodeShapePainter tipShapePainter) {
651 treePane.setTipShapePainter(tipShapePainter);
652 }
653
631654 public void setNodeShapePainter(NodeShapePainter nodeShapePainter) {
632655 treePane.setNodeShapePainter(nodeShapePainter);
633656 }
665665 if (!isCalibrated)
666666 calibrate();
667667
668 if (minorTick <= 0) {
669 return 0;
670 }
671
668672 if (majorTickIndex == majorTickCount-1)
669673 return (int)((maxAxis-maxTick)/minorTick);
670674 else if (majorTickIndex==-1)
3333 */
3434 public class TimeScale {
3535
36 public TimeScale(double rootAge) {
36 private boolean isReversed = false;
37
38 public TimeScale(double rootAge) {
3739 this.rootAge = rootAge;
3840 this.scaleFactor = Double.NaN;
3941 this.offsetAge = 0.0;
4951 if (Double.isNaN(scaleFactor)) {
5052 return rootAge / tree.getHeight(tree.getRootNode());
5153 }
52 return scaleFactor;
54 return scaleFactor * (isReversed ? -1.0 : 1.0);
5355 }
5456
5557 public double getAge(double height, RootedTree tree) {
7072 return (time / getScaleFactor(tree));
7173 }
7274
73 private final double rootAge;
75 public void setReversed(boolean isReversed) {
76 this.isReversed = isReversed;
77 }
78
79 private final double rootAge;
7480 private final double offsetAge;
7581 private final double scaleFactor;
82
7683 }
3232 import java.awt.*;
3333 import java.awt.geom.*;
3434 import java.awt.print.*;
35 import java.io.IOException;
3635 import java.util.*;
3736 import java.util.List;
3837
4746 * $LastChangedRevision$
4847 */
4948 public class TreePane extends JComponent implements PainterListener, Printable {
49 public final static boolean DEBUG_OUTLINE = false;
50
5051 public enum RootingType {
5152 USER_ROOTING("User Selection"),
5253 MID_POINT("Midpoint");
6162 private String name;
6263 }
6364
64
65 public final static boolean DEBUG_OUTLINE = false;
6665
6766 public final String CARTOON_ATTRIBUTE_NAME = "!cartoon";
6867 public final String COLLAPSE_ATTRIBUTE_NAME = "!collapse";
9089 }
9190 }
9291
92 private void recalibrate() {
93 calibrated = false;
94 }
95
9396 private void setupTree() {
9497 tree = constructTransformedTree(originalTree);
9598
9699 recalculateCollapsedNodes();
97100
98 calibrated = false;
101 recalibrate();
99102 invalidate();
100103 repaint();
101104 }
151154
152155 treeLayout.addTreeLayoutListener(new TreeLayoutListener() {
153156 public void treeLayoutChanged() {
154 calibrated = false;
157 recalibrate();
155158 repaint();
156159 }
157160 });
158 calibrated = false;
161 recalibrate();
159162 invalidate();
160163 repaint();
161164 }
166169
167170 public void setTimeScale(TimeScale timeScale) {
168171 this.timeScale = timeScale;
169 calibrated = false;
172 this.timeScale.setReversed(isAxisReversed());
173 recalibrate();
170174 repaint();
171175 }
172176
232236 }
233237 node.setAttribute("!rotate", rotate);
234238
235 calibrated = false;
239 recalibrate();
236240 invalidate();
237241 repaint();
238242 }
246250 node.removeAttribute("!rotate");
247251 }
248252
249 calibrated = false;
253 recalibrate();
250254 invalidate();
251255 repaint();
252256 }
299303 */
300304 public double scaleOnAxis(double value) {
301305 double height = timeScale.getHeight(value, tree);
302 if (treeLayout.isAxisReversed()) {
303 return treeBounds.getX() + treeBounds.getWidth() - (height * treeScale);
306 if (isAxisReversed()) {
307 return (treeBounds.getX() + treeBounds.getWidth()) - (height * treeScale);
304308 } else {
305309 return treeBounds.getX() + (height * treeScale);
306310 }
307311 }
308312
309313 public Shape getAxisLine(double value) {
314 if (isAxisReversed()) {
315 value = maxTreeHeight - value;
316 } else {
317 value -= rootHeightOffset;
318 }
310319 double height = timeScale.getHeight(value, tree);
311320 Shape line = treeLayout.getAxisLine(height);
312321 if (line != null) {
326335
327336 public void setAxisOrigin(double axisOrigin) {
328337 this.axisOrigin = axisOrigin;
329 calibrated = false;
338 recalibrate();
330339 repaint();
331340 }
332341
333342 public void setAxisReversed(final boolean isAxisReversed) {
334 treeLayout.setAxisReversed(isAxisReversed);
335 calibrated = false;
336 repaint();
343 this.isAxisReversed = isAxisReversed;
344 this.timeScale.setReversed(isAxisReversed());
345 recalibrate();
346 repaint();
347 }
348
349 public boolean isAxisReversed() {
350 return isAxisReversed;
337351 }
338352
339353 private void setupScaleAxis() {
356370 public void setRootAge(double rootAge) {
357371 double rootLength = timeScale.getHeight(rootAge, tree) - tree.getHeight(tree.getRootNode());
358372 treeLayout.setRootLength(rootLength);
359 calibrated = false;
373 recalibrate();
360374 repaint();
361375 }
362376
375389
376390 public void setTickSpacing(double userMajorTickSpacing, double userMinorTickSpacing) {
377391 scaleAxis.setManualAxis(userMajorTickSpacing, userMinorTickSpacing);
378 calibrated = false;
392 recalibrate();
379393 repaint();
380394 }
381395
382396 public void setAutomaticScale() {
383397 scaleAxis.setAutomatic();
384 calibrated = false;
398 recalibrate();
385399 repaint();
386400 }
387401
388402 public void painterChanged() {
389 calibrated = false;
403 recalibrate();
390404 repaint();
391405 }
392406
393407 public void painterSettingsChanged() {
394 calibrated = false;
408 recalibrate();
395409 repaint();
396410 }
397411
398412 public void attributesChanged() {
399 calibrated = false;
413 recalibrate();
400414 repaint();
401415 }
402416
495509
496510 public void setShowingTipCallouts(boolean showingTipCallouts) {
497511 this.showingTipCallouts = showingTipCallouts;
498 calibrated = false;
512 recalibrate();
499513 repaint();
500514 }
501515
719733 Object[] values = new Object[] { tipCount, height };
720734 node.setAttribute(CARTOON_ATTRIBUTE_NAME, values);
721735 }
722 calibrated = false;
736 recalibrate();
723737 repaint();
724738 } else {
725739 for (Node child : tree.getChildren(node)) {
745759 Object[] values = new Object[] { tipName, height };
746760 node.setAttribute(COLLAPSE_ATTRIBUTE_NAME, values);
747761 }
748 calibrated = false;
762 recalibrate();
749763 repaint();
750764 } else {
751765 for (Node child : tree.getChildren(node)) {
768782 Object[] values = new Object[] { tipCount, height, color };
769783 node.setAttribute(HILIGHT_ATTRIBUTE_NAME, values);
770784
771 calibrated = false;
785 recalibrate();
772786 repaint();
773787 } else {
774788 for (Node child : tree.getChildren(node)) {
805819 Object[] values = new Object[] { tipCount, height, oldValues[2] };
806820 node.setAttribute(HILIGHT_ATTRIBUTE_NAME, values);
807821 }
808 calibrated = false;
822 recalibrate();
809823 repaint();
810824 } else {
811825 for (Node child : tree.getChildren(node)) {
827841 node.removeAttribute(CARTOON_ATTRIBUTE_NAME);
828842 }
829843 }
830 calibrated = false;
844 recalibrate();
831845 repaint();
832846 }
833847 }
845859 if (node.getAttribute(CARTOON_ATTRIBUTE_NAME) != null) {
846860 node.removeAttribute(CARTOON_ATTRIBUTE_NAME);
847861 }
848 calibrated = false;
862 recalibrate();
849863 repaint();
850864 } else {
851865 for (Node child : tree.getChildren(node)) {
864878 node.removeAttribute(HILIGHT_ATTRIBUTE_NAME);
865879 }
866880 }
867 calibrated = false;
881 recalibrate();
868882 repaint();
869883 }
870884 }
875889 if (selectedNodes.size() == 0 || selectedNodes.contains(node)) {
876890 if (node.getAttribute(HILIGHT_ATTRIBUTE_NAME) != null) {
877891 node.removeAttribute(HILIGHT_ATTRIBUTE_NAME);
878 calibrated = false;
892 recalibrate();
879893 repaint();
880894 }
881895 }
976990 if (this.tipLabelPainter != null) {
977991 this.tipLabelPainter.addPainterListener(this);
978992 }
979 calibrated = false;
993 recalibrate();
980994 repaint();
981995 }
982996
9931007 if (this.nodeLabelPainter != null) {
9941008 this.nodeLabelPainter.addPainterListener(this);
9951009 }
996 calibrated = false;
1010 recalibrate();
9971011 repaint();
9981012 }
9991013
10101024 if (this.branchLabelPainter != null) {
10111025 this.branchLabelPainter.addPainterListener(this);
10121026 }
1013 calibrated = false;
1027 recalibrate();
10141028 repaint();
10151029 }
10161030
10271041 if (this.nodeBarPainter != null) {
10281042 this.nodeBarPainter.addPainterListener(this);
10291043 }
1030 calibrated = false;
1044 recalibrate();
10311045 repaint();
10321046 }
10331047
10351049 return nodeBarPainter;
10361050 }
10371051
1052 public void setTipShapePainter(NodeShapePainter tipShapePainter) {
1053 tipShapePainter.setTreePane(this);
1054 if (this.tipShapePainter != null) {
1055 this.tipShapePainter.removePainterListener(this);
1056 }
1057 this.tipShapePainter = tipShapePainter;
1058 if (this.tipShapePainter != null) {
1059 this.tipShapePainter.addPainterListener(this);
1060 }
1061 recalibrate();
1062 repaint();
1063 }
1064
1065 public NodeShapePainter getTipShapePainter() {
1066 return tipShapePainter;
1067 }
10381068
10391069 public void setNodeShapePainter(NodeShapePainter nodeShapePainter) {
10401070 nodeShapePainter.setTreePane(this);
10451075 if (this.nodeShapePainter != null) {
10461076 this.nodeShapePainter.addPainterListener(this);
10471077 }
1048 calibrated = false;
1078 recalibrate();
10491079 repaint();
10501080 }
10511081
10611091
10621092 scalePainters.add(scalePainter);
10631093
1064 calibrated = false;
1094 recalibrate();
10651095 repaint();
10661096 }
10671097
10721102
10731103 scalePainters.remove(scalePainter);
10741104
1075 calibrated = false;
1105 recalibrate();
10761106 repaint();
10771107 }
10781108
10851115 if (this.scaleGridPainter != null) {
10861116 this.scaleGridPainter.addPainterListener(this);
10871117 }
1088 calibrated = false;
1118 recalibrate();
10891119 repaint();
10901120 }
10911121
10951125
10961126 this.legendPainter = legendPainter;
10971127
1098 calibrated = false;
1128 recalibrate();
10991129 repaint();
11001130 }
11011131
11091139
11101140 public void setLabelSpacing(float labelSpacing) {
11111141 this.labelXOffset = labelSpacing;
1112 calibrated = false;
1142 recalibrate();
11131143 repaint();
11141144 }
11151145
11201150 super.setPreferredSize(dimension);
11211151 }
11221152
1123 calibrated = false;
1153 recalibrate();
11241154 }
11251155
11261156 public double getHeightAt(Graphics2D graphics2D, Point2D point) {
11341164
11351165 public Node getNodeAt(Graphics2D g2, Point point) {
11361166 Rectangle rect = new Rectangle(point.x - 1, point.y - 1, 3, 3);
1167 rect.translate(-insets.left, -insets.top);
11371168
11381169 for (Node node : tree.getExternalNodes()) {
11391170 Shape taxonLabelBound = tipLabelBounds.get(node);
11891220
11901221 public Set<Node> getSelectedTips() {
11911222 return selectedTips;
1223 }
1224
1225 public Set<Taxon> getSelectedTaxa() {
1226 Set<Taxon> selectedTaxa = new LinkedHashSet<Taxon>();
1227 for (Node node : getSelectedTips()) {
1228 selectedTaxa.add(tree.getTaxon(node));
1229 }
1230 return selectedTaxa;
11921231 }
11931232
11941233 public RootedTree getSelectedSubtree() {
12761315 this.rulerHeight = rulerHeight;
12771316 }
12781317
1318 public Point getLocationOfTip(Node tip) {
1319 if (tip == null) {
1320 return new Point(0,0);
1321 }
1322 Shape path = transform.createTransformedShape(treeLayoutCache.getTipLabelPath(tip));
1323 return path.getBounds().getLocation();
1324 }
1325
12791326 public void scrollPointToVisible(Point point) {
12801327 scrollRectToVisible(new Rectangle(point.x, point.y, 0, 0));
12811328 }
13161363 public void paint(Graphics graphics) {
13171364 if (tree == null) return;
13181365
1319 graphics.setColor(Color.white);
1320 Rectangle r = graphics.getClipBounds();
1321 if (r != null) {
1322 graphics.fillRect(r.x, r.y, r.width, r.height);
1323 }
1324
1366 // graphics.setColor(Color.white);
1367 // Rectangle r = graphics.getClipBounds();
1368 // if (r != null) {
1369 // graphics.fillRect(r.x, r.y, r.width, r.height);
1370 // }
1371 //
13251372 final Graphics2D g2 = (Graphics2D) graphics;
1373 g2.translate(insets.left, insets.top);
1374
13261375 if (!calibrated) {
13271376 calibrate(g2, getWidth(), getHeight());
13281377 }
14011450 Graphics2D g2 = (Graphics2D) graphics;
14021451 g2.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
14031452
1404 calibrated = false;
1453 recalibrate();
14051454 setDoubleBuffered(false);
14061455
14071456 drawTree(g2, pageFormat.getImageableWidth(), pageFormat.getImageableHeight());
14081457
14091458 setDoubleBuffered(true);
1410 calibrated = false;
1459 recalibrate();
14111460
14121461 return PAGE_EXISTS;
14131462 }
14431492 scalePainter.paint(g2, this, Painter.Justification.CENTER, scaleBounds);
14441493 }
14451494 }
1495
1496 if (scaleGridPainter != null && scaleGridPainter.isVisible()) {
1497 Rectangle2D gridBounds = new Rectangle2D.Double(
1498 treeBounds.getX(), 0.0,
1499 treeBounds.getWidth(), treeBounds.getHeight());
1500 scaleGridPainter.paint(g2, this, null, gridBounds);
1501 }
1502
14461503
14471504 // Paint backgrounds
14481505 if (nodeBackgroundDecorator != null) {
14561513 g2.setPaint(background);
14571514 g2.fill(transNodePath);
14581515
1459 // Experimental outlining - requires order of drawing to be pre-order
1516 // Experimental outlining - requires order of drawing to be pre-order
14601517 // g2.setStroke(new BasicStroke(8, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
14611518 // g2.draw(transNodePath);
14621519 }
14951552 }
14961553 }
14971554
1498 if (scaleGridPainter != null && scaleGridPainter.isVisible()) {
1499 Rectangle2D gridBounds = new Rectangle2D.Double(
1500 treeBounds.getX(), 0.0,
1501 treeBounds.getWidth(), totalScaleBounds.getY());
1502 scaleGridPainter.paint(g2, this, null, gridBounds);
1503 }
1504
15051555 if (DEBUG_OUTLINE) {
1506 g2.setPaint(Color.red);
1556 g2.setPaint(Color.blue);
15071557 g2.draw(treeBounds);
15081558 }
15091559
16261676 }
16271677 }
16281678
1679 if (tipShapePainter != null && tipShapePainter.isVisible()) {
1680 for (Node node : tipPoints.keySet()) {
1681 Point2D point = tipPoints.get(node);
1682 point = transform.transform(point, null);
1683 tipShapePainter.paint(g2, node, point, nodeShapeTransforms.get(node));
1684 }
1685 }
1686
16291687 // Paint tip labels
16301688 if (tipLabelPainter != null && tipLabelPainter.isVisible()) {
16311689
16361694 Painter.Justification tipLabelJustification = tipLabelJustifications.get(node);
16371695 g2.transform(tipLabelTransform);
16381696
1697 double labelWidth = tipLabelWidths.get(node);
16391698 tipLabelPainter.paint(g2, node, tipLabelJustification,
1640 new Rectangle2D.Double(0.0, 0.0, tipLabelWidth, tipLabelPainter.getPreferredHeight()));
1699 new Rectangle2D.Double(0.0, 0.0, labelWidth, tipLabelPainter.getPreferredHeight()));
16411700
16421701 g2.setTransform(oldTransform);
16431702
16801739 final double preferredWidth = branchLabelPainter.getPreferredWidth();
16811740 final double preferredHeight = branchLabelPainter.getPreferredHeight();
16821741
1683 //Line2D labelPath = treeLayout.getBranchLabelPath(node);
1684
16851742 branchLabelPainter.paint(g2, node, Painter.Justification.CENTER,
1686 //new Rectangle2D.Double(-preferredWidth/2, -preferredHeight, preferredWidth, preferredHeight));
16871743 new Rectangle2D.Double(0, 0, preferredWidth, preferredHeight));
16881744
16891745 g2.setTransform(oldTransform);
17011757 treeLayout.layout(tree, treeLayoutCache);
17021758
17031759 maxTreeHeight = tree.getHeight(tree.getRootNode()) + treeLayout.getRootLength();
1760 rootHeightOffset = 0.0;
1761
17041762
17051763 // First of all get the bounds for the unscaled tree
17061764 treeBounds = null;
17541812 // bounds on node bars
17551813 if (!isTransformBranchesOn() && nodeBarPainter != null && nodeBarPainter.isVisible()) {
17561814 nodeBars.clear();
1815
17571816 // Iterate though the nodes
17581817 for (Node node : tree.getInternalNodes()) {
17591818
17631822 nodeBars.put(node, nodeBarPainter.getNodeBar());
17641823 }
17651824 }
1825
17661826 if (nodeBarPainter.getMaxHeight() > maxTreeHeight) {
1827 rootHeightOffset = Math.max(nodeBarPainter.getMaxHeight() - maxTreeHeight, 0.0);
17671828 maxTreeHeight = nodeBarPainter.getMaxHeight();
17681829 }
17691830 }
1831
17701832
17711833 // totalTreeBounds includes all the stuff which is not in a tree scale (like labels and shapes) but in
17721834 // screen pixel scale. This is added to the treeBounds to make space round the edge.
17731835
17741836 // add the tree bounds
1775 final Rectangle2D totalTreeBounds = treeBounds.getBounds2D(); // (YH) same as (Rectangle2D) treeBounds.clone();
1837 final Rectangle2D totalTreeBounds = treeBounds.getBounds2D();
1838 // final Rectangle2D totalTreeBounds = new Rectangle2D.Double(0.0, 0.0,treeBounds.getWidth(),treeBounds.getHeight());
1839
1840 tipLabelWidths.clear();
17761841
17771842 if (tipLabelPainter != null && tipLabelPainter.isVisible()) {
17781843
1779 tipLabelWidth = 0.0;
1780 final double tipLabelHeight = tipLabelPainter.getPreferredHeight();
1844 // calculateMaxTipLabelWidth(g2, tree.getRootNode());
17811845
17821846 // put this in a recursive function to allow for collapsed node labels
1783 calibrateTipLabels(g2, tree.getRootNode(), tipLabelHeight, totalTreeBounds);
1847 calibrateTipLabels(g2, tree.getRootNode(), totalTreeBounds);
17841848 }
17851849
17861850 if (nodeLabelPainter != null && nodeLabelPainter.isVisible()) {
18231887 }
18241888
18251889 // bounds on nodeShapes
1890 if (tipShapePainter != null && tipShapePainter.isVisible()) {
1891 tipPoints.clear();
1892 // Iterate though the external nodes
1893 for (Node node : tree.getExternalNodes()) {
1894
1895 Rectangle2D shapeBounds = tipShapePainter.calibrate(g2, node);
1896 if (shapeBounds != null) {
1897 totalTreeBounds.add(shapeBounds);
1898
1899 // just at the centroid in here as the actual shape will be reconstructed when drawing
1900 tipPoints.put(node, new Point2D.Double(shapeBounds.getCenterX(), shapeBounds.getCenterY()));
1901 }
1902 }
1903 }
18261904 if (nodeShapePainter != null && nodeShapePainter.isVisible()) {
18271905 nodePoints.clear();
1828 // Iterate though the nodes
1829 for (Node node : tree.getNodes()) {
1906 // Iterate though the internal nodes
1907 for (Node node : tree.getInternalNodes()) {
18301908
18311909 Rectangle2D shapeBounds = nodeShapePainter.calibrate(g2, node);
18321910 if (shapeBounds != null) {
18411919 // Now rescale the scale axis
18421920 setupScaleAxis();
18431921
1844 totalScaleBounds = new Rectangle2D.Double(0.0, 0.0, 0.0, 0.0);
1922 bottomPanelBounds = new Rectangle2D.Double();
18451923 double y = totalTreeBounds.getHeight();
18461924 for (ScalePainter scalePainter : scalePainters) {
18471925 if (scalePainter.isVisible()) {
18481926 scalePainter.calibrate(g2, this);
18491927 Rectangle2D sb = new Rectangle2D.Double(
1850 totalTreeBounds.getX(), y,
1851 totalTreeBounds.getWidth(), scalePainter.getPreferredHeight());
1928 treeBounds.getX(), y,
1929 treeBounds.getWidth(), scalePainter.getPreferredHeight());
18521930 y += sb.getHeight();
1853 totalScaleBounds.add(sb);
1931 bottomPanelBounds.add(sb);
18541932 scaleBounds.put(scalePainter, sb);
18551933 }
18561934 }
1857 totalTreeBounds.add(totalScaleBounds);
1858
1935
1936 leftPanelBounds = new Rectangle2D.Double();
18591937 if (legendPainter != null && legendPainter.isVisible()) {
18601938 legendPainter.calibrate(g2, this);
18611939 final double w2 = legendPainter.getPreferredWidth();
1862 legendBounds = new Rectangle2D.Double(-w2, 0, w2, treeBounds.getHeight());
1863 totalTreeBounds.add(legendBounds);
1864 }
1865
1866 // // translate treeBounds to the inset within totalTreeBounds
1867 // treeBounds.setRect(-totalTreeBounds.getX(), -totalTreeBounds.getY(), treeBounds.getWidth(), treeBounds.getHeight());
1868 //
1869 // // translate totalTreeBounds so it is at 0, 0
1870 // totalTreeBounds.setRect(0, 0,
1871 // totalTreeBounds.getWidth(),
1872 // totalTreeBounds.getHeight());
1873
1940 legendBounds = new Rectangle2D.Double(0.0, 0.0, w2, height);
1941 leftPanelBounds.add(legendBounds);
1942 }
18741943
18751944 final double availableW = width - insets.left - insets.right;
18761945 final double availableH = height - insets.top - insets.bottom;
19121981 // Get the amount of canvas that is going to be taken up by the tree -
19131982 // The rest is taken up by taxon labels which don't scale
19141983
1915 final double w = availableW - xDiff;
1916 final double h = availableH - yDiff;
1984 final double w = availableW - xDiff - leftPanelBounds.getWidth() - rightPanelBounds.getWidth();
1985 final double h = availableH - yDiff - topPanelBounds.getHeight() - bottomPanelBounds.getHeight();
19171986
19181987 double xScale;
19191988 double yScale;
19472016 yScale = h / treeBounds.getHeight();
19482017
19492018 // and set the origin in the top left corner
1950 xOffset = - treeBounds.getX() * xScale;
1951 yOffset = - treeBounds.getY() * yScale;
1952
2019 xOffset = -treeBounds.getX() * xScale + (treeBounds.getX() - totalTreeBounds.getX());
2020 yOffset = -treeBounds.getY() * yScale + (treeBounds.getY() - totalTreeBounds.getY());
19532021 treeScale = xScale;
19542022 }
19552023
19572025
19582026 // Create the overall transform
19592027 transform = new AffineTransform();
1960 transform.translate(xOffset + insets.left, yOffset + insets.top);
2028 transform.translate(xOffset + leftPanelBounds.getWidth(), yOffset + topPanelBounds.getHeight());
19612029 transform.scale(xScale, yScale);
19622030
19632031 // Get the bounds for the newly scaled tree
19922060
19932061 Rectangle2D shapeBounds = nodeBarPainter.calibrate(g2, node);
19942062 if (shapeBounds != null) {
2063 shapeBounds = transform.createTransformedShape(shapeBounds).getBounds2D();
19952064 treeBounds.add(shapeBounds);
19962065 nodeBars.put(node, nodeBarPainter.getNodeBar());
19972066 }
20052074
20062075 if (tipLabelPainter != null && tipLabelPainter.isVisible()) {
20072076 final double labelHeight = tipLabelPainter.getPreferredHeight();
2008 Rectangle2D labelBounds = new Rectangle2D.Double(0.0, 0.0, tipLabelWidth, labelHeight);
20092077
20102078 // Iterate though the external nodes with tip labels
20112079 for (Node node : treeLayoutCache.getTipLabelPathMap().keySet()) {
20122080 // Get the line that represents the path for the tip label
20132081 Line2D tipPath = treeLayoutCache.getTipLabelPath(node);
20142082
2083 final double labelWidth = tipLabelWidths.get(node);
2084 Rectangle2D labelBounds = new Rectangle2D.Double(0.0, 0.0, labelWidth, labelHeight);
2085
20152086 // Work out how it is rotated and create a transform that matches that
2016 AffineTransform taxonTransform = calculateTransform(transform, tipPath, tipLabelWidth, labelHeight, true);
2087 AffineTransform taxonTransform = calculateTransform(transform, tipPath, labelWidth, labelHeight, true);
20172088
20182089 // Store the transformed bounds in the map for use when selecting
20192090 tipLabelBounds.put(node, taxonTransform.createTransformedShape(labelBounds));
21012172 }
21022173 }
21032174
2175 nodeShapeTransforms.clear();
21042176 if (nodeShapePainter != null && nodeShapePainter.isVisible()) {
2105 nodeShapeTransforms.clear();
21062177 // Iterate though the nodes
21072178 for (Node node : nodePoints.keySet()) {
21082179 Line2D shapePath = getTreeLayoutCache().getNodeShapePath(node);
21112182 }
21122183 }
21132184 }
2114
2115 y = height;
2185 if (tipShapePainter != null && tipShapePainter.isVisible()) {
2186 // Iterate though the nodes
2187 for (Node node : tipPoints.keySet()) {
2188 Line2D shapePath = getTreeLayoutCache().getNodeShapePath(node);
2189 if (shapePath != null) {
2190 nodeShapeTransforms.put(node, calculateTransform(transform, shapePath));
2191 }
2192 }
2193 }
2194
2195
2196 y = availableH;
21162197 for (ScalePainter scalePainter : scalePainters) {
21172198 if (scalePainter.isVisible()) {
21182199 scalePainter.calibrate(g2, this);
21202201 }
21212202 }
21222203
2123 totalScaleBounds = new Rectangle2D.Double(0, y, treeBounds.getWidth(), 0.0);
2204 bottomPanelBounds = new Rectangle2D.Double(0, y, treeBounds.getWidth(), 0.0);
21242205 for (ScalePainter scalePainter : scalePainters) {
21252206 if (scalePainter.isVisible()) {
21262207 scalePainter.calibrate(g2, this);
21272208 final double h1 = scalePainter.getPreferredHeight();
21282209 Rectangle2D sb = new Rectangle2D.Double(treeBounds.getX(), y, treeBounds.getWidth(), h1);
21292210 y += h1;
2130 totalScaleBounds.add(sb);
2211 bottomPanelBounds.add(sb);
21312212 scaleBounds.put(scalePainter, sb);
21322213 }
21332214 }
21342215
2216 leftPanelBounds = new Rectangle2D.Double(0, 0, 0.0, 0.0);
21352217 if (legendPainter != null && legendPainter.isVisible()) {
21362218 legendPainter.calibrate(g2, this);
21372219 final double w2 = legendPainter.getPreferredWidth();
2138 legendBounds = new Rectangle2D.Double(0, 0, w2, treeBounds.getHeight());
2220 legendBounds = new Rectangle2D.Double(0.0, 0.0, w2, availableH);
2221 leftPanelBounds.add(legendBounds);
2222
21392223 }
21402224
21412225 calloutPaths.clear();
21442228 calibrated = true;
21452229 }
21462230
2147 private void calibrateTipLabels(final Graphics2D g2, final Node node, final double tipLabelHeight, final Rectangle2D totalTreeBounds) {
2231 // private void calculateMaxTipLabelWidth(final Graphics2D g2, final Node node) {
2232 //
2233 // if (tree.isExternal(node) || node.getAttribute(COLLAPSE_ATTRIBUTE_NAME) != null) {
2234 // tipLabelPainter.calibrate(g2, node);
2235 // double labelWidth = tipLabelPainter.getPreferredWidth();
2236 // tipLabelWidths.put(node, labelWidth);
2237 // maxTipLabelWidth = Math.max(maxTipLabelWidth, labelWidth);
2238 // } else {
2239 // for (Node child : tree.getChildren(node)) {
2240 // calculateMaxTipLabelWidth(g2, child);
2241 // }
2242 // }
2243 // }
2244
2245 private void calibrateTipLabels(final Graphics2D g2, final Node node, final Rectangle2D totalTreeBounds) {
21482246
21492247 if (tree.isExternal(node) || node.getAttribute(COLLAPSE_ATTRIBUTE_NAME) != null) {
21502248 tipLabelPainter.calibrate(g2, node);
2151 double width = tipLabelPainter.getPreferredWidth();
2152 tipLabelWidth = Math.max(tipLabelWidth, width);
2153
2154 Rectangle2D labelBounds = new Rectangle2D.Double(0.0, 0.0, width, tipLabelHeight);
2249 double labelWidth = tipLabelPainter.getPreferredWidth();
2250 double labelHeight = tipLabelPainter.getPreferredHeight();
2251
2252 tipLabelWidths.put(node, labelWidth);
2253 Rectangle2D labelBounds = new Rectangle2D.Double(0.0, 0.0, labelWidth, labelHeight);
21552254
21562255 // Get the line that represents the path for the taxon label
21572256 Line2D taxonPath = treeLayoutCache.getTipLabelPath(node);
21582257
21592258 if (taxonPath != null) {
21602259 // Work out how it is rotated and create a transform that matches that
2161 AffineTransform taxonTransform = calculateTransform(null, taxonPath, tipLabelWidth, tipLabelHeight, true);
2260 AffineTransform taxonTransform = calculateTransform(null, taxonPath, labelWidth, labelHeight, true);
21622261
21632262 // and add the translated bounds to the overall bounds
21642263 totalTreeBounds.add(taxonTransform.createTransformedShape(labelBounds).getBounds2D());
21652264 }
21662265 } else {
21672266 for (Node child : tree.getChildren(node)) {
2168 calibrateTipLabels(g2, child, tipLabelHeight, totalTreeBounds);
2267 calibrateTipLabels(g2, child, totalTreeBounds);
21692268 }
21702269 }
21712270 }
21722271
21732272 private AffineTransform calculateTransform(AffineTransform globalTransform, Line2D line,
2174 double width, double height, boolean just) {
2273 double width, double height, boolean justify) {
21752274 final Point2D origin = line.getP1();
21762275 if (globalTransform != null) {
21772276 globalTransform.transform(origin, origin);
21922291 // to shift it by the entire width of the string.
21932292 final double ty = origin.getY() - (height / 2.0);
21942293 double tx = origin.getX();
2195 if( just) {
2294 if (justify) {
21962295 if (line.getX2() > line.getX1()) {
21972296 tx += labelXOffset;
21982297 } else {
22262325
22272326 // Overridden methods to recalibrate tree when bounds change
22282327 public void setBounds(int x, int y, int width, int height) {
2229 calibrated = false;
2328 recalibrate();
22302329 super.setBounds(x, y, width, height);
22312330 }
22322331
22332332 public void setBounds(Rectangle rectangle) {
2234 calibrated = false;
2333 recalibrate();
22352334 super.setBounds(rectangle);
22362335 }
22372336
22382337 public void setSize(Dimension dimension) {
2239 calibrated = false;
2338 recalibrate();
22402339 super.setSize(dimension);
22412340 }
22422341
22432342 public void setSize(int width, int height) {
2244 calibrated = false;
2343 recalibrate();
22452344 super.setSize(width, height);
22462345 }
22472346
22642363 private Rectangle2D treeBounds = new Rectangle2D.Double();
22652364 private double treeScale;
22662365 private double maxTreeHeight;
2366 private double rootHeightOffset;
22672367
22682368 private ScaleAxis scaleAxis = new ScaleAxis(ScaleAxis.AT_DATA, ScaleAxis.AT_DATA);
22692369 private double axisOrigin = 0.0;
22702370 private TimeScale timeScale = new TimeScale(1.0, 0.0);
2371 private boolean isAxisReversed = false;
22712372
22722373 //private Insets insets = new Insets(0, 0, 0, 0);
22732374 private Insets insets = new Insets(6, 6, 6, 6);
22742375
22752376 private Set<Node> selectedNodes = new HashSet<Node>();
2276 private Set<Node> selectedTips = new HashSet<Node>();
2377 private Set<Node> selectedTips = new LinkedHashSet<Node>();
22772378
22782379 private double rulerHeight = -1.0;
22792380 private Rectangle2D dragRectangle = null;
22902391
22912392 private Decorator nodeBackgroundDecorator = null;
22922393
2293 private float labelXOffset = 5.0F;
2394 private float labelXOffset = 10.0F;
22942395 private LabelPainter<Node> tipLabelPainter = null;
2295 private double tipLabelWidth;
2396 //private double maxTipLabelWidth;
22962397 private LabelPainter<Node> nodeLabelPainter = null;
22972398 private LabelPainter<Node> branchLabelPainter = null;
22982399
22992400 private NodeBarPainter nodeBarPainter = null;
23002401
23012402 private NodeShapePainter nodeShapePainter = null;
2403 private NodeShapePainter tipShapePainter = null;
23022404
23032405 private List<ScalePainter> scalePainters = new ArrayList<ScalePainter>();
2304 private Rectangle2D totalScaleBounds = null;
23052406 private Map<ScalePainter, Rectangle2D> scaleBounds = new HashMap<ScalePainter, Rectangle2D>();
23062407
23072408 private ScaleGridPainter scaleGridPainter = null;
23082409
23092410 private LegendPainter legendPainter = null;
23102411 private Rectangle2D legendBounds = new Rectangle2D.Double();
2412
2413 private Rectangle2D topPanelBounds = new Rectangle2D.Double();
2414 private Rectangle2D leftPanelBounds = new Rectangle2D.Double();
2415 private Rectangle2D bottomPanelBounds = new Rectangle2D.Double();
2416 private Rectangle2D rightPanelBounds = new Rectangle2D.Double();
23112417
23122418
23132419 private BasicStroke branchLineStroke = new BasicStroke(1.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
23242430
23252431 private Map<Node, AffineTransform> tipLabelTransforms = new HashMap<Node, AffineTransform>();
23262432 private Map<Node, Shape> tipLabelBounds = new HashMap<Node, Shape>();
2433 private Map<Node, Double> tipLabelWidths = new HashMap<Node, Double>();
23272434 private Map<Node, Painter.Justification> tipLabelJustifications = new HashMap<Node, Painter.Justification>();
23282435
23292436 private Map<Node, AffineTransform> nodeLabelTransforms = new HashMap<Node, AffineTransform>();
23352442 private Map<Node, Painter.Justification> branchLabelJustifications = new HashMap<Node, Painter.Justification>();
23362443
23372444 private Map<Node, Shape> nodeBars = new HashMap<Node, Shape>();
2445 private Map<Node, Point2D> tipPoints = new HashMap<Node, Point2D>();
23382446 private Map<Node, Point2D> nodePoints = new HashMap<Node, Point2D>();
23392447 private Map<Node, AffineTransform> nodeShapeTransforms = new HashMap<Node, AffineTransform>();
23402448
4545 public TreePaneRollOver(TreePane treePane) {
4646 this.treePane = treePane;
4747 treePane.addMouseMotionListener(this);
48
49 }
48 }
5049
5150 public void mouseEntered(MouseEvent mouseEvent) {
5251 }
5958 if (tree != null) {
6059 Node node = treePane.getNodeAt((Graphics2D) treePane.getGraphics(), mouseEvent.getPoint());
6160 if (node != null) {
62 StringBuilder sb = new StringBuilder();
63 if (!tree.isExternal(node)) {
64 int n = RootedTreeUtils.getTipCount(tree, node);
65 sb.append("Subtree: ").append(n).append(" tips");
66 } else {
67 sb.append("Tip: \"").append(tree.getTaxon(node).toString()).append("\"");
68 }
69 sb.append(" [height = ").append(formatter.getFormattedValue(tree.getHeight(node)));
70 sb.append(", length = ").append(formatter.getFormattedValue(tree.getLength(node)));
71 sb.append("]");
72 fireStatusChanged(StatusPanel.NORMAL, sb.toString());
61 fireStatusChanged(StatusPanel.NORMAL, getNodeText(tree, node));
7362 } else {
74 fireStatusChanged(StatusPanel.NORMAL, " ");
63 fireStatusChanged(StatusPanel.NORMAL, getNodeText(tree, tree.getRootNode()));
7564 }
7665 } else {
7766 fireStatusChanged(StatusPanel.NORMAL, " ");
7867 }
7968 }
69
70 private String getNodeText(RootedTree tree, Node node) {
71 StringBuilder sb = new StringBuilder();
72 if (!tree.isExternal(node)) {
73 int n = RootedTreeUtils.getTipCount(tree, node);
74 sb.append(tree.isRoot(node) ? "Tree: " : "Subtree: ").append(n).append(" tips");
75 } else {
76 sb.append("Tip: \"").append(tree.getTaxon(node).toString()).append("\"");
77 }
78 sb.append(" [height = ").append(formatter.getFormattedValue(tree.getHeight(node)));
79 sb.append(", length = ").append(formatter.getFormattedValue(tree.getLength(node)));
80 sb.append("]");
81 return sb.toString();
82 }
8083
8184 public void mouseDragged(MouseEvent mouseEvent) {
8285 }
4242 */
4343 public ContinuousScale(String settings) {
4444 if (settings.startsWith("{")) {
45 settings = settings.substring(1, settings.length());
45 settings = settings.substring(1, settings.length());
4646 }
4747 if (settings.endsWith("}")) {
4848 settings = settings.substring(0, settings.length() - 1);
8787
8888 // First collect the set of all attribute values
8989 Set<Object> values = new TreeSet<Object>();
90
91 boolean isNumber = true;
92
93 // Find the range of numbers
9094 for (Attributable item : items) {
9195 Object value = item.getAttribute(attributeName);
9296 if (value != null) {
93 values.add(value);
94 }
95 }
96
97 boolean isNumber = true;
98
99 // Find the range of numbers
100 for (Object value : values) {
101 double realValue = -1.0;
102
103 if (value instanceof Boolean) {
104 realValue = ((Boolean)value ? 1 : 0);
105 } else if (value instanceof Number) {
106 realValue = ((Number)value).doubleValue();
107 } else if (value instanceof String) {
108 // it is a string but it could still code for
109 // a boolean, integer or real
110 if (value.toString().equalsIgnoreCase("true")) {
111 realValue = 1;
112 } else if (value.toString().equalsIgnoreCase("false")) {
113 realValue = 0;
114 } else {
115 try {
116 realValue = Double.parseDouble(value.toString());
117 } catch(NumberFormatException nfe) {
118 isNumber = false;
97
98 double realValue = -1.0;
99
100 if (value instanceof Boolean) {
101 realValue = ((Boolean)value ? 1 : 0);
102 } else if (value instanceof Number) {
103 realValue = ((Number)value).doubleValue();
104 } else if (value instanceof String) {
105 // it is a string but it could still code for
106 // a boolean, integer or real
107 if (value.toString().equalsIgnoreCase("true")) {
108 realValue = 1;
109 } else if (value.toString().equalsIgnoreCase("false")) {
110 realValue = 0;
111 } else {
112 try {
113 realValue = Double.parseDouble(value.toString());
114 } catch(NumberFormatException nfe) {
115 isNumber = false;
116 }
119117 }
120118 }
121 }
122
123 if (isNumber) {
124 if (realValue < minValue) {
125 minValue = realValue;
119
120 if (isNumber) {
121 if (realValue < minValue) {
122 minValue = realValue;
123 }
124 if (realValue > maxValue) {
125 maxValue = realValue;
126 }
127
126128 }
127 if (realValue > maxValue) {
128 maxValue = realValue;
129 }
130
129 values.add(realValue);
131130 }
132131 }
133132
121121 }
122122 String attribute = colourDecorator.getAttributeName();
123123
124 if (TreePane.DEBUG_OUTLINE) {
125 g2.setPaint(Color.red);
126 g2.draw(bounds);
127 }
128
124129 Font oldFont = g2.getFont();
125130 Paint oldPaint = g2.getPaint();
126131 Stroke oldStroke = g2.getStroke();
4949 */
5050 public class NodeBarController extends AbstractController {
5151
52 private static Preferences PREFS = Preferences.userNodeForPackage(NodeBarController.class);
52 private static Preferences PREFS = Preferences.userNodeForPackage(TreeViewer.class);
5353
5454 private static final String NODE_BARS_KEY = "nodeBars";
5555 public static final String DISPLAY_ATTRIBUTE_KEY = "displayAttribute";
2323 import figtree.treeviewer.TreeViewer;
2424 import figtree.treeviewer.decorators.Decorator;
2525 import jam.controlpalettes.AbstractController;
26 import jam.controlpalettes.ControllerListener;
2726 import jam.panels.OptionsPanel;
2827
2928 import javax.swing.*;
3029 import javax.swing.event.ChangeEvent;
3130 import javax.swing.event.ChangeListener;
31 import java.awt.*;
3232 import java.awt.event.ActionEvent;
3333 import java.awt.event.ActionListener;
3434 import java.util.Map;
4848 */
4949 public class NodeShapeController extends AbstractController {
5050
51 private static Preferences PREFS = Preferences.userNodeForPackage(NodeBarController.class);
52
51 public enum NodeType {
52 INTERNAL, EXTERNAL, BOTH
53 }
54
55 private static Preferences PREFS = Preferences.userNodeForPackage(TreeViewer.class);
56
57 private static final String IS_EXTERNAL = "isExternal";
58 private static final String IS_INTERNAL = "isInternal";
5359 private static final String NODE_SHAPE_KEY = "nodeShape";
5460 public static final String SHAPE_TYPE_KEY = "shapeType";
5561 public static final String SCALE_TYPE_KEY = "scaleType";
5864 private static final String SHAPE_SIZE_KEY = "size";
5965 private static final String SHAPE_MIN_SIZE_KEY = "minSize";
6066
61 public NodeShapeController(String title, final NodeShapePainter nodeShapePainter,
67 public NodeShapeController(final String title, final NodeType type, final NodeShapePainter nodeShapePainter,
6268 final AttributeColourController colourController,
6369 final TreeViewer treeViewer) {
6470 this.title = title;
65 this.nodeShapePainter = nodeShapePainter;
71
72 this.type = type;
6673
6774 final float defaultShapeSize = PREFS.getFloat(SHAPE_SIZE_KEY, (float)NodeShapePainter.MAX_SIZE);
6875
6976 optionsPanel = new ControllerOptionsPanel(2, 2);
7077
7178 titleCheckBox = new JCheckBox(getTitle());
72 titleCheckBox.setSelected(this.nodeShapePainter.isVisible());
79 titleCheckBox.setSelected(nodeShapePainter.isVisible());
7380
7481 titleCheckBox.addChangeListener(new ChangeListener() {
7582 public void stateChanged(ChangeEvent changeEvent) {
7784 nodeShapePainter.setVisible(selected);
7885 }
7986 });
87
88 final ControllerOptionsPanel nodeCheckPanel;
89 if (type == NodeType.BOTH) {
90 externalNodeCheck = new JCheckBox("external");
91 internalNodeCheck = new JCheckBox("internal");
92 ActionListener listener = new ActionListener() {
93 public void actionPerformed(ActionEvent event) {
94 nodeShapePainter.setNodeType(externalNodeCheck.isSelected(), internalNodeCheck.isSelected());
95 }
96 };
97 externalNodeCheck.addActionListener(listener);
98 internalNodeCheck.addActionListener(listener);
99 nodeCheckPanel = new ControllerOptionsPanel(2, 2);
100 nodeCheckPanel.setBorder(BorderFactory.createEmptyBorder());
101 nodeCheckPanel.addSpanningComponent(externalNodeCheck);
102 nodeCheckPanel.addSpanningComponent(internalNodeCheck);
103
104 externalNodeCheck.setSelected(nodeShapePainter.isExternal());
105 internalNodeCheck.setSelected(nodeShapePainter.isInternal());
106 } else {
107 nodeCheckPanel = null;
108 externalNodeCheck = null;
109 internalNodeCheck = null;
110 if (type == NodeType.EXTERNAL) {
111 nodeShapePainter.setNodeType(true, false);
112 } else if (type == NodeType.INTERNAL) {
113 nodeShapePainter.setNodeType(false, true);
114 }
115 }
80116
81117 shapeTypeCombo = new JComboBox(NodeShapePainter.ShapeType.values());
82118 shapeTypeCombo.addActionListener(new ActionListener() {
115151 }
116152 });
117153
154 outlineStrokeCombo = new JComboBox(new String[] {"None", "0.25", "0.5", "1.0", "2.0", "3.0", "4.0", "5.0"});
155 outlineStrokeCombo.addActionListener(new ActionListener() {
156 public void actionPerformed(ActionEvent event) {
157 nodeShapePainter.setOutline((outlineStrokeCombo.getSelectedIndex() == 0 ? 0.0f :
158 Float.parseFloat(outlineStrokeCombo.getSelectedItem().toString())),
159 (Paint)outlinePaintCombo.getSelectedItem()
160 );
161 }
162 });
163
164 final Paint[] outlinePaints = {Color.black, Color.white};
165 outlinePaintCombo = new JComboBox(new String[] {"black", "white"});
166 outlinePaintCombo.addActionListener(new ActionListener() {
167 public void actionPerformed(ActionEvent event) {
168 nodeShapePainter.setOutline((outlineStrokeCombo.getSelectedIndex() == 0 ? 0.0f :
169 Float.parseFloat(outlineStrokeCombo.getSelectedItem().toString())),
170 outlinePaints[outlinePaintCombo.getSelectedIndex()]
171 );
172 }
173 });
174
175 final JLabel label8;
176 if (type == NodeType.BOTH) {
177 label8 = optionsPanel.addComponentWithLabel("Show:", nodeCheckPanel);
178 } else {
179 label8 = null;
180 }
118181
119182 final JLabel label1 = optionsPanel.addComponentWithLabel("Shape:", shapeTypeCombo);
120183 final JLabel label2 = optionsPanel.addComponentWithLabel("Max size:", shapeSizeSpinner);
124187 optionsPanel.addSeparator();
125188 final JLabel label6 = optionsPanel.addComponentWithLabel("Colour by:", colourAttributeCombo);
126189 final JLabel label7 = optionsPanel.addComponentWithLabel("Setup:", setupColourButton);
190
191 final JLabel label9 = optionsPanel.addComponentWithLabel("Outline width:", outlineStrokeCombo);
192 final JLabel label10 = optionsPanel.addComponentWithLabel("Outline colour:", outlinePaintCombo);
127193
128194 new AttributeComboHelper(colourAttributeCombo, treeViewer, "User selection").addListener(new AttributeComboHelperListener() {
129195 @Override
161227
162228 addComponent(label1);
163229 addComponent(shapeTypeCombo);
230 if (type == NodeType.BOTH) {
231 addComponent(label8);
232 addComponent(externalNodeCheck);
233 addComponent(internalNodeCheck);
234 }
164235 addComponent(label2);
165236 addComponent(shapeSizeSpinner);
166237 addComponent(label3);
173244 addComponent(colourAttributeCombo);
174245 addComponent(label7);
175246 addComponent(setupColourButton);
247 addComponent(label9);
248 addComponent(outlineStrokeCombo);
249 addComponent(label10);
250 addComponent(outlinePaintCombo);
176251 enableComponents(titleCheckBox.isSelected());
177252
178253 titleCheckBox.addChangeListener(new ChangeListener() {
205280 }
206281
207282 public void setSettings(Map<String,Object> settings) {
208 titleCheckBox.setSelected((Boolean)settings.get(NODE_SHAPE_KEY + "." + IS_SHOWN));
209
210 shapeTypeCombo.setSelectedItem((NodeShapePainter.ShapeType.valueOf(settings.get(NODE_SHAPE_KEY + "." + SHAPE_TYPE_KEY).toString().toUpperCase())));
211 scaleTypeCombo.setSelectedItem((NodeShapePainter.ScaleType.valueOf(settings.get(NODE_SHAPE_KEY + "." + SCALE_TYPE_KEY).toString().toUpperCase())));
212 colourAttributeCombo.setSelectedItem((String) settings.get(NODE_SHAPE_KEY + "." + COLOUR_ATTRIBUTE_KEY));
213 sizeAttributeCombo.setSelectedItem((String) settings.get(NODE_SHAPE_KEY + "." + SIZE_ATTRIBUTE_KEY));
214 shapeSizeSpinner.setValue((Double)settings.get(NODE_SHAPE_KEY + "." + SHAPE_SIZE_KEY));
215 shapeMinSizeSpinner.setValue((Double) settings.get(NODE_SHAPE_KEY + "." + SHAPE_MIN_SIZE_KEY));
283 String key = NODE_SHAPE_KEY + (type == NodeType.INTERNAL ? "Internal" : (type == NodeType.EXTERNAL ? "External" : ""));
284
285 titleCheckBox.setSelected((Boolean)settings.get(key + "." + IS_SHOWN));
286
287 if (type == NodeType.BOTH) {
288 externalNodeCheck.setSelected((Boolean) settings.get(key + "." + IS_EXTERNAL));
289 internalNodeCheck.setSelected((Boolean) settings.get(key + "." + IS_INTERNAL));
290 }
291 shapeTypeCombo.setSelectedItem((NodeShapePainter.ShapeType.valueOf(settings.get(key + "." + SHAPE_TYPE_KEY).toString().toUpperCase())));
292 scaleTypeCombo.setSelectedItem((NodeShapePainter.ScaleType.valueOf(settings.get(key + "." + SCALE_TYPE_KEY).toString().toUpperCase())));
293 colourAttributeCombo.setSelectedItem((String) settings.get(key + "." + COLOUR_ATTRIBUTE_KEY));
294 sizeAttributeCombo.setSelectedItem((String) settings.get(key + "." + SIZE_ATTRIBUTE_KEY));
295 shapeSizeSpinner.setValue((Double)settings.get(key + "." + SHAPE_SIZE_KEY));
296 shapeMinSizeSpinner.setValue((Double) settings.get(key + "." + SHAPE_MIN_SIZE_KEY));
216297 }
217298
218299 public void getSettings(Map<String, Object> settings) {
219 settings.put(NODE_SHAPE_KEY + "." + IS_SHOWN, titleCheckBox.isSelected());
220 settings.put(NODE_SHAPE_KEY + "." + SHAPE_TYPE_KEY, shapeTypeCombo.getSelectedItem());
221 settings.put(NODE_SHAPE_KEY + "." + SCALE_TYPE_KEY, scaleTypeCombo.getSelectedItem());
222 settings.put(NODE_SHAPE_KEY + "." + COLOUR_ATTRIBUTE_KEY, colourAttributeCombo.getSelectedItem());
223 settings.put(NODE_SHAPE_KEY + "." + SIZE_ATTRIBUTE_KEY, sizeAttributeCombo.getSelectedItem());
224 settings.put(NODE_SHAPE_KEY + "." + SHAPE_SIZE_KEY, shapeSizeSpinner.getValue());
225 settings.put(NODE_SHAPE_KEY + "." + SHAPE_MIN_SIZE_KEY, shapeMinSizeSpinner.getValue());
300 String key = NODE_SHAPE_KEY + (type == NodeType.INTERNAL ? "Internal" : (type == NodeType.EXTERNAL ? "External" : ""));
301 settings.put(key + "." + IS_SHOWN, titleCheckBox.isSelected());
302 if (type == NodeType.BOTH) {
303 settings.put(key + "." + IS_EXTERNAL, externalNodeCheck.isSelected());
304 settings.put(key + "." + IS_INTERNAL, internalNodeCheck.isSelected());
305 }
306 settings.put(key + "." + SHAPE_TYPE_KEY, shapeTypeCombo.getSelectedItem());
307 settings.put(key + "." + SCALE_TYPE_KEY, scaleTypeCombo.getSelectedItem());
308 settings.put(key + "." + COLOUR_ATTRIBUTE_KEY, colourAttributeCombo.getSelectedItem());
309 settings.put(key + "." + SIZE_ATTRIBUTE_KEY, sizeAttributeCombo.getSelectedItem());
310 settings.put(key + "." + SHAPE_SIZE_KEY, shapeSizeSpinner.getValue());
311 settings.put(key + "." + SHAPE_MIN_SIZE_KEY, shapeMinSizeSpinner.getValue());
226312 }
227313
228314 private final JCheckBox titleCheckBox;
229315 private final OptionsPanel optionsPanel;
230316
317 private final JCheckBox externalNodeCheck;
318 private final JCheckBox internalNodeCheck;
231319 private final JComboBox shapeTypeCombo;
232320 private final JComboBox scaleTypeCombo;
233321 private final JComboBox sizeAttributeCombo;
234322 private final JComboBox colourAttributeCombo;
235323 private final JSpinner shapeSizeSpinner;
236324 private final JSpinner shapeMinSizeSpinner;
325 private final JComboBox outlineStrokeCombo;
326 private final JComboBox outlinePaintCombo;
237327
238328 public String getTitle() {
239329 return title;
240330 }
241331
242332 private final String title;
243 private final NodeShapePainter nodeShapePainter;
333 private final NodeType type;
244334 }
114114 public void setScaleType(ScaleType scaleType) {
115115 this.scaleType = scaleType;
116116 firePainterChanged();
117 }
118
119 public void setNodeType(boolean external, boolean internal) {
120 this.external = external;
121 this.internal = internal;
122 firePainterChanged();
123 }
124
125 public boolean isExternal() {
126 return external;
127 }
128
129 public boolean isInternal() {
130 return internal;
117131 }
118132
119133 public double getPreferredWidth() {
146160 g2.setPaint(paint);
147161 g2.fill(nodeShape);
148162
149 g2.setPaint(Color.black);
150 g2.setStroke(new BasicStroke(0.5F));
151 g2.draw(nodeShape);
163 if (outlineStroke > 0.0F) {
164 g2.setPaint(outlinePaint);
165 g2.setStroke(new BasicStroke(outlineStroke));
166 g2.draw(nodeShape);
167 }
152168 }
153169
154170 /**
180196
181197 public void setColourDecorator(Decorator colourDecorator) {
182198 this.colourDecorator = colourDecorator;
199 firePainterChanged();
200 }
201
202 public void setOutline(final float outlineStroke, final Paint outlinePaint) {
203 this.outlineStroke = outlineStroke;
204 this.outlinePaint = outlinePaint;
183205 firePainterChanged();
184206 }
185207
246268 private ScaleType scaleType = ScaleType.WIDTH;
247269 private String sizeAttribute = null;
248270
271 private boolean external = true;
272 private boolean internal = true;
273
249274 private Decorator colourDecorator = null;
250275 private ContinuousScale sizeScale = null;
251276
277 private float outlineStroke = 0.5f;
278 private Paint outlinePaint = Color.black;
279
252280 private TreePane treePane;
253281 }
191191
192192 protected void paintAxis(Graphics2D g2, Rectangle2D axisBounds)
193193 {
194 ScaleAxis axis = treePane.getScaleAxis();
195
196 g2.setPaint(getForeground());
197 g2.setStroke(getScaleBarStroke());
198
199 double minX = treePane.scaleOnAxis(axis.getMinAxis());
200 double maxX = treePane.scaleOnAxis(axis.getMaxAxis());
201
202 Line2D line = new Line2D.Double(minX, axisBounds.getMinY(), maxX, axisBounds.getMinY());
203 g2.draw(line);
204
205 int n1 = axis.getMajorTickCount();
206 int n2, i, j;
207
208 n2 = axis.getMinorTickCount(-1);
209 if (axis.getLabelFirst()) { // Draw first minor tick as a major one (with a label)
210
211 paintMajorTick(g2, axisBounds, axis, axis.getMinorTickValue(0, -1));
212
213 for (j = 1; j < n2; j++) {
214 paintMinorTick(g2, axisBounds, axis.getMinorTickValue(j, -1));
215 }
216 } else {
217
218 for (j = 0; j < n2; j++) {
219 paintMinorTick(g2, axisBounds, axis.getMinorTickValue(j, -1));
220 }
221 }
222
223 for (i = 0; i < n1; i++) {
224
225 paintMajorTick(g2, axisBounds, axis, axis.getMajorTickValue(i));
226 n2 = axis.getMinorTickCount(i);
227
228 if (i == (n1-1) && axis.getLabelLast()) { // Draw last minor tick as a major one
229
230 paintMajorTick(g2, axisBounds, axis, axis.getMinorTickValue(0, i));
231
232 for (j = 1; j < n2; j++) {
233 paintMinorTick(g2, axisBounds, axis.getMinorTickValue(j, i));
234 }
235 } else {
236
237 for (j = 0; j < n2; j++) {
238 paintMinorTick(g2, axisBounds, axis.getMinorTickValue(j, i));
239 }
240 }
241 }
242 }
194 ScaleAxis axis = treePane.getScaleAxis();
195
196 g2.setPaint(getForeground());
197 g2.setStroke(getScaleBarStroke());
198
199 double minX = treePane.scaleOnAxis(axis.getMinAxis());
200 double maxX = treePane.scaleOnAxis(axis.getMaxAxis());
201
202 Line2D line = new Line2D.Double(minX, axisBounds.getY() + topMargin, maxX, axisBounds.getY() + topMargin);
203 g2.draw(line);
204
205 int n1 = axis.getMajorTickCount();
206 int n2, i, j;
207
208 n2 = axis.getMinorTickCount(-1);
209 if (axis.getLabelFirst()) { // Draw first minor tick as a major one (with a label)
210
211 paintMajorTick(g2, axisBounds, axis, axis.getMinorTickValue(0, -1));
212
213 for (j = 1; j < n2; j++) {
214 paintMinorTick(g2, axisBounds, axis.getMinorTickValue(j, -1));
215 }
216 } else {
217
218 for (j = 0; j < n2; j++) {
219 paintMinorTick(g2, axisBounds, axis.getMinorTickValue(j, -1));
220 }
221 }
222
223 for (i = 0; i < n1; i++) {
224
225 paintMajorTick(g2, axisBounds, axis, axis.getMajorTickValue(i));
226 n2 = axis.getMinorTickCount(i);
227
228 if (i == (n1-1) && axis.getLabelLast()) { // Draw last minor tick as a major one
229
230 paintMajorTick(g2, axisBounds, axis, axis.getMinorTickValue(0, i));
231
232 for (j = 1; j < n2; j++) {
233 paintMinorTick(g2, axisBounds, axis.getMinorTickValue(j, i));
234 }
235 } else {
236
237 for (j = 0; j < n2; j++) {
238 paintMinorTick(g2, axisBounds, axis.getMinorTickValue(j, i));
239 }
240 }
241 }
242 }
243243
244244 protected void paintMajorTick(Graphics2D g2, Rectangle2D axisBounds, ScaleAxis axis, double value)
245245 {
250250 String label = getNumberFormat().format(value);
251251 double pos = treePane.scaleOnAxis(value);
252252
253 Line2D line = new Line2D.Double(pos, axisBounds.getMinY(), pos, axisBounds.getMinY() + majorTickSize);
253 Line2D line = new Line2D.Double(pos, axisBounds.getMinY() + topMargin, pos, axisBounds.getMinY() + majorTickSize + topMargin);
254254 g2.draw(line);
255255
256256 g2.setPaint(getForeground());
257257 double width = g2.getFontMetrics().stringWidth(label);
258 g2.drawString(label, (float)(pos - (width / 2)), (float)(axisBounds.getMinY() + tickLabelOffset));
258 g2.drawString(label, (float)(pos - (width / 2)), (float)(axisBounds.getMinY() + tickLabelOffset + topMargin));
259259 }
260260
261261 protected void paintMinorTick(Graphics2D g2, Rectangle2D axisBounds, double value)
266266
267267 double pos = treePane.scaleOnAxis(value);
268268
269 Line2D line = new Line2D.Double(pos, axisBounds.getMinY(), pos, axisBounds.getMinY() + minorTickSize);
269 Line2D line = new Line2D.Double(pos, axisBounds.getMinY() + topMargin, pos, axisBounds.getMinY() + minorTickSize + topMargin);
270270 g2.draw(line);
271271 }
272272
313313 titleCheckBox.setSelected((Boolean)settings.get(SCALE_AXIS_KEY + "." + IS_SHOWN));
314314 reverseAxisCheck.setSelected((Boolean)settings.get(SCALE_AXIS_KEY + "." + REVERSE_AXIS_KEY));
315315 showGridCheck.setSelected((Boolean)settings.get(SCALE_AXIS_KEY + "." + SHOW_GRID_KEY));
316 // minorTicksText.setValue((Double)settings.get(SCALE_AXIS_KEY + "." + MINOR_TICKS_KEY));
316 minorTicksText.setValue((Double)settings.get(SCALE_AXIS_KEY + "." + MINOR_TICKS_KEY));
317317 majorTicksText.setValue((Double)settings.get(SCALE_AXIS_KEY + "." + MAJOR_TICKS_KEY));
318318 originText.setValue((Double)settings.get(SCALE_AXIS_KEY + "." + ORIGIN_KEY));
319319 autoScaleCheck.setSelected((Boolean)settings.get(SCALE_AXIS_KEY + "." + AUTOMATIC_SCALE_KEY));
328328 settings.put(SCALE_AXIS_KEY + "." + REVERSE_AXIS_KEY, reverseAxisCheck.isSelected());
329329 settings.put(SCALE_AXIS_KEY + "." + SHOW_GRID_KEY, showGridCheck.isSelected());
330330 settings.put(SCALE_AXIS_KEY + "." + AUTOMATIC_SCALE_KEY, autoScaleCheck.isSelected());
331 // settings.put(SCALE_AXIS_KEY + "." + MINOR_TICKS_KEY, minorTicksText.getValue());
331 settings.put(SCALE_AXIS_KEY + "." + MINOR_TICKS_KEY, minorTicksText.getValue());
332332 settings.put(SCALE_AXIS_KEY + "." + MAJOR_TICKS_KEY, majorTicksText.getValue());
333333 settings.put(SCALE_AXIS_KEY + "." + ORIGIN_KEY, originText.getValue());
334334 settings.put(SCALE_AXIS_KEY + "." + FONT_SIZE_KEY, fontSizeSpinner.getValue());
5656 preferredWidth = treePane.getTreeBounds().getWidth();
5757 preferredHeight = treePane.getTreeBounds().getHeight();
5858
59 return new Rectangle2D.Double(0.0, 0.0, preferredWidth, preferredHeight);
59 return new Rectangle2D.Double(0.0, 0.0, preferredWidth, preferredHeight);
6060 }
6161
6262 public void paint(Graphics2D g2, TreePane treePane, Justification justification, Rectangle2D bounds) {
6565 Stroke oldStroke = g2.getStroke();
6666
6767 if (TreePane.DEBUG_OUTLINE) {
68 g2.setPaint(Color.red);
68 g2.setPaint(Color.blue);
6969 g2.draw(bounds);
7070 }
7171
3434 */
3535 public abstract class AbstractTreeLayout implements TreeLayout {
3636 private double rootLength = 0.0;
37 private boolean isAxisReversed;
38
39 public boolean isAxisReversed() {
40 return isAxisReversed;
41 }
42
43 public void setAxisReversed(final boolean axisReversed) {
44 isAxisReversed = axisReversed;
45 }
4637
4738 public double getRootLength() {
4839 return rootLength;
9292
9393 public Shape getAxisLine(double height) {
9494 double x = height;
95 if (isAxisReversed()) {
96 x = maxXPosition - x;
97 }
9895 return new Ellipse2D.Double(-x, -x, x * 2.0, x * 2.0);
9996 }
10097
8484
8585 public Shape getAxisLine(double height) {
8686 double x = height;
87 if (isAxisReversed()) {
88 x = maxXPosition - x;
89 }
9087 double y1 = 0.0;
9188 double y2 = 1.0;
9289 return new Line2D.Double(x, y1, x, y2);
9491
9592 public Shape getHeightArea(double height1, double height2) {
9693 double x = height1;
97 if (isAxisReversed()) {
98 x = maxXPosition - x;
99 }
10094 double y = 0.0;
10195 double w = Math.abs(height2 - height1);
10296 double h = 1.0;
8181
8282 void setRootLength(double rootLength);
8383
84 boolean isAxisReversed();
85
86 void setAxisReversed(final boolean axisReversed);
87
88
8984 /**
9085 * Return whether this layout is showing a branch colouring
9186 * @return showing colouring?