Merge tag 'upstream/1.4.3+dfsg'
Upstream version 1.4.3+dfsg
Fabian Klötzl
6 years ago
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 |
11 | 11 | |
12 | 12 | <property name="lib" location="lib"/> |
13 | 13 | <property name="dist" location="dist"/> |
14 | <property name="packaging_tools" value="packaging_tools" /> | |
14 | 15 | |
15 | 16 | <property environment="env"/> |
16 | 17 | |
59 | 60 | </manifest> |
60 | 61 | </jar> |
61 | 62 | |
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 | ||
67 | 68 | <manifest> |
68 | 69 | <attribute name="Built-By" value="${user.name}"/> |
69 | <attribute name="Main-Class" value="figtree.application.FigTreePDF"/> | |
70 | 70 | </manifest> |
71 | 71 | </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 | ||
72 | 83 | |
73 | 84 | <war destfile="${dist}/figtree.war" |
74 | 85 | webxml="WebRoot/WEB-INF/web.xml"> |
90 | 101 | |
91 | 102 | </target> |
92 | 103 | |
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" /> | |
94 | 106 | <property name="release_dir" value="release" /> |
95 | 107 | <property name="name" value="FigTree" /> |
96 | 108 | |
100 | 112 | <property name="Linux_dir" value="${release_dir}/Linux" /> |
101 | 113 | <property name="Windows_dir" value="${release_dir}/Windows" /> |
102 | 114 | |
103 | <property name="Mac_package_dir_SL" value="${Mac_dir}/${name} v${version}" /> | |
104 | 115 | <property name="Mac_package_dir" value="${Mac_dir}/${name} v${version}" /> |
105 | 116 | <property name="Linux_package_dir" value="${Linux_dir}/${name}_v${version}" /> |
106 | 117 | <property name="Windows_package_dir" value="${Windows_dir}/${name} v${version}" /> |
120 | 131 | classpath="${launch4j.dir}/launch4j.jar :${launch4j.dir}/lib/xstream.jar" /> |
121 | 132 | |
122 | 133 | <copy file="${dist}/figtree.jar" todir="${Windows_package_dir}/lib"/> |
134 | <!-- | |
123 | 135 | <copy todir="${Windows_package_dir}/lib"> |
124 | 136 | <fileset dir="${Windows_dir}/lib"/> |
125 | 137 | </copy> |
138 | --> | |
126 | 139 | <copy todir="${Windows_package_dir}"> |
127 | 140 | <fileset dir="${common_dir}/"/> |
128 | 141 | </copy> |
130 | 143 | <launch4j configFile="${Windows_dir}/FigTree_launch4j.xml" |
131 | 144 | jar="${dist}/figtree.jar" |
132 | 145 | outfile="${Windows_package_dir}/${name} v${version}.exe" |
133 | fileVersion="${version}.0" | |
146 | fileVersion="${version_number}.0" | |
134 | 147 | txtFileVersion="${version}" |
135 | productVersion="${version}.0" | |
148 | productVersion="${version_number}.0" | |
136 | 149 | txtProductVersion="${version}" |
137 | 150 | /> |
138 | 151 | |
155 | 168 | |
156 | 169 | <copy file="${Linux_dir}/icons/figtree.png" todir="${Linux_package_dir}/images"/> |
157 | 170 | <copy file="${dist}/figtree.jar" todir="${Linux_package_dir}/lib"/> |
171 | <!-- | |
158 | 172 | <copy todir="${Linux_package_dir}/lib"> |
159 | 173 | <fileset dir="${Linux_dir}/lib"/> |
160 | 174 | </copy> |
175 | --> | |
161 | 176 | <copy todir="${Linux_package_dir}"> |
162 | 177 | <fileset dir="${common_dir}/"/> |
163 | 178 | </copy> |
169 | 184 | <echo message="Linux/Unix version release is finished." /> |
170 | 185 | </target> |
171 | 186 | |
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}" /> | |
175 | 190 | <!-- 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}"> | |
181 | 196 | <fileset dir="${common_dir}/"/> |
182 | 197 | </copy> |
183 | <copy todir="${Mac_package_dir_SL}/QuickLook Plugin"> | |
198 | <copy todir="${Mac_package_dir}/QuickLook Plugin"> | |
184 | 199 | <fileset dir="${Mac_dir}/QuickLook Plugin"/> |
185 | 200 | </copy> |
186 | 201 | |
187 | 202 | <taskdef name="jarbundler" classname="net.sourceforge.jarbundler.JarBundler"/> |
188 | 203 | |
189 | 204 | <!-- create a jar bundle for the mac --> |
190 | <jarbundler dir="${Mac_package_dir_SL}" | |
205 | <jarbundler dir="${Mac_package_dir}" | |
191 | 206 | name="${name} v${version}" |
192 | 207 | mainclass="figtree.application.FigTreeApplication" |
193 | 208 | icon="${Mac_dir}/icons/FigTree.icns" |
209 | stubfile="${packaging_tools}/mac/universalJavaApplicationStub" | |
210 | useJavaXKey="true" | |
194 | 211 | jvmversion="1.6+" |
195 | 212 | vmoptions="-Xmx1024M" |
196 | 213 | 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" | |
199 | 217 | bundleid="figtree" > |
200 | 218 | <javaproperty name="apple.laf.useScreenMenuBar" value="true"/> |
201 | 219 | <jarfileset dir="${dist}"> |
214 | 232 | </jarbundler> |
215 | 233 | |
216 | 234 | <!-- remove code signing --> |
235 | <!-- | |
217 | 236 | <exec executable="/usr/bin/codesign"> |
218 | 237 | <arg value="-s"/> |
219 | 238 | <arg value="-"/> |
220 | <arg value="--force"/> | |
239 | <arg value="- -force"/> remove space from between minus signs | |
221 | 240 | <arg value="${Mac_dir}/${name} v${version}/${name} v${version}.app"/> |
222 | 241 | </exec> |
223 | ||
242 | --> | |
243 | ||
224 | 244 | <echo message="Building disk image." /> |
225 | 245 | |
226 | 246 | <!-- create disk image --> |
Binary diff not shown
0 | FigTree v1.4 2006-2012 | |
0 | FigTree v1.4.3 2006-2016 | |
1 | 1 | Andrew Rambaut |
2 | 2 | |
3 | 3 | Institute of Evolutionary Biology |
6 | 6 | |
7 | 7 | |
8 | 8 | 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 | |
10 | 10 | |
11 | 11 | Contents: |
12 | 12 | 1) INTRODUCTION |
26 | 26 | ___________________________________________________________________________ |
27 | 27 | 2) VERSION HISTORY |
28 | 28 | |
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 | ||
29 | 75 | v1.4 Released 8th October 2012. |
30 | 76 | |
31 | 77 | New Features: |
29 | 29 | |
30 | 30 | package figtree.application; |
31 | 31 | |
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; | |
32 | 37 | import figtree.application.preferences.*; |
33 | 38 | import figtree.treeviewer.ExtendedTreeViewer; |
34 | 39 | import jam.framework.*; |
51 | 56 | import javax.swing.*; |
52 | 57 | |
53 | 58 | 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; | |
54 | 62 | |
55 | 63 | /** |
56 | 64 | * Application class for FigTree including main() method for invoking it. |
67 | 75 | */ |
68 | 76 | public class FigTreeApplication extends MultiDocApplication { |
69 | 77 | |
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"; | |
72 | 80 | |
73 | 81 | public static FigTreeApplication application; |
74 | 82 | |
163 | 171 | |
164 | 172 | GraphicFormat format = null; |
165 | 173 | if (graphicFormat.equals("PDF")) { |
166 | if (graphicFileName != null) { | |
167 | System.out.println("Creating PDF graphic: " + graphicFileName); | |
168 | } | |
169 | 174 | 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; | |
190 | 177 | } else if (graphicFormat.equals("GIF")) { |
191 | if (graphicFileName != null) { | |
192 | System.out.println("Creating GIF graphic: " + graphicFileName); | |
193 | } | |
194 | 178 | format = GraphicFormat.GIF; |
195 | 179 | } else if (graphicFormat.equals("PNG")) { |
196 | 180 | format = GraphicFormat.PNG; |
200 | 184 | throw new RuntimeException("Unknown graphic format"); |
201 | 185 | } |
202 | 186 | |
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); | |
209 | 192 | |
210 | 193 | } catch(ImportException ie) { |
211 | throw new RuntimeException("Error writing graphic file: " + ie); | |
194 | throw new RuntimeException("Error writing graphic file: " + ie.getMessage()); | |
212 | 195 | } 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()); | |
214 | 199 | } |
215 | 200 | |
216 | 201 | } |
263 | 248 | new Arguments.Option[] { |
264 | 249 | new Arguments.StringOption("graphic", new String[] { |
265 | 250 | "PDF", |
266 | // "SVG", | |
251 | "SVG", | |
267 | 252 | // "SWF", "PS", "EMF", |
268 | 253 | "PNG", |
269 | "GIF", | |
254 | // "GIF", | |
270 | 255 | "JPEG" |
271 | 256 | }, false, "produce a graphic with the given format"), |
272 | 257 | new Arguments.IntegerOption("width", "the width of the graphic in pixels"), |
404 | 389 | icon = new ImageIcon(url); |
405 | 390 | } |
406 | 391 | |
407 | final String nameString = "FigTree " + VERSION; | |
392 | final String nameString = "FigTree"; | |
408 | 393 | String titleString = "<html>" + |
409 | 394 | "<div style=\"font-family:'Helvetica Neue', Helvetica, Arial, 'Lucida Grande',sans-serif\">" + |
410 | 395 | "<p style=\"font-weight: 100; font-size: 36px\">FigTree</p>" + |
28 | 28 | import figtree.treeviewer.decorators.DiscreteColourDecorator; |
29 | 29 | import figtree.treeviewer.decorators.HSBDiscreteColourDecorator; |
30 | 30 | import figtree.treeviewer.painters.StatesPainter; |
31 | import jebl.evolution.align.Output; | |
31 | 32 | import jebl.evolution.alignments.Alignment; |
32 | 33 | import jebl.evolution.alignments.BasicAlignment; |
33 | 34 | import jebl.evolution.graphs.Node; |
51 | 52 | import org.apache.batik.svggen.SVGGraphics2D; |
52 | 53 | import org.apache.batik.svggen.SVGGraphics2DIOException; |
53 | 54 | import org.w3c.dom.DOMImplementation; |
55 | import org.w3c.dom.Element; | |
54 | 56 | |
55 | 57 | import javax.imageio.ImageIO; |
56 | 58 | import javax.swing.*; |
287 | 289 | public void actionPerformed(ActionEvent e){ |
288 | 290 | if (treeViewer.isRootingOn() && treeViewer.getRootingType() == TreePane.RootingType.USER_ROOTING) { |
289 | 291 | 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", | |
291 | 293 | "Unable to switch trees", |
292 | 294 | JOptionPane.ERROR_MESSAGE); |
293 | 295 | |
307 | 309 | public void actionPerformed(ActionEvent e){ |
308 | 310 | if (treeViewer.isRootingOn() && treeViewer.getRootingType() == TreePane.RootingType.USER_ROOTING) { |
309 | 311 | 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", | |
311 | 313 | "Unable to switch trees", |
312 | 314 | JOptionPane.ERROR_MESSAGE); |
313 | 315 | |
1215 | 1217 | File file = new File(dialog.getDirectory(), dialog.getFile()); |
1216 | 1218 | |
1217 | 1219 | 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); | |
1239 | 1230 | } catch (IOException ioe) { |
1240 | 1231 | JOptionPane.showMessageDialog(this, "Error writing tree file: " + ioe.getMessage(), |
1241 | 1232 | "Export Error", |
1246 | 1237 | |
1247 | 1238 | } |
1248 | 1239 | |
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 { | |
1250 | 1264 | int imageType = BufferedImage.TYPE_INT_RGB; |
1251 | 1265 | |
1252 | 1266 | if (format == GraphicFormat.PNG) { |
1262 | 1276 | } |
1263 | 1277 | component.paint(g); |
1264 | 1278 | 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 { | |
1269 | 1283 | // Get a DOMImplementation and create an XML document |
1270 | 1284 | DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation(); |
1271 | 1285 | org.w3c.dom.Document document = domImpl.createDocument(null, "svg", null); |
1274 | 1288 | SVGGraphics2D svgGenerator = new SVGGraphics2D(document); |
1275 | 1289 | |
1276 | 1290 | 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())); | |
1277 | 1297 | |
1278 | 1298 | // 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(); | |
1288 | 1305 | Document document = new Document(new com.itextpdf |
1289 | 1306 | .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 | ||
1314 | 1320 | document.close(); |
1315 | 1321 | } |
1316 | 1322 | |
1317 | 1323 | public void doCopy() { |
1318 | 1324 | StringWriter writer = new StringWriter(); |
1319 | 1325 | 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 | } | |
1321 | 1331 | } catch (IOException e) { |
1322 | 1332 | e.printStackTrace(); |
1323 | 1333 | } |
1379 | 1389 | |
1380 | 1390 | public void doSelectAll() { |
1381 | 1391 | 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(); | |
1382 | 1400 | } |
1383 | 1401 | |
1384 | 1402 | protected void writeTreeFile(Writer writer, ExportTreeDialog.Format format, |
1851 | 1869 | private AnnotationDialog annotationDialog = null; |
1852 | 1870 | private AnnotationDialog copyAnnotationDialog = null; |
1853 | 1871 | private SelectAnnotationDialog selectAnnotationDialog = null; |
1854 | }⏎ | |
1872 | } |
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 |
47 | 47 | |
48 | 48 | public final static int CONTROL_PALETTE_WIDTH = 200; |
49 | 49 | |
50 | private final static boolean SEPARATE_NODE_SHAPE_PANELS = true; | |
51 | ||
50 | 52 | public FigTreePanel(JFrame frame, final ExtendedTreeViewer treeViewer, ControlPalette controlPalette) { |
51 | 53 | |
52 | 54 | this.treeViewer = treeViewer; |
79 | 81 | controlPalette.addController(new LabelPainterController("Tip Labels", "tipLabels", tipLabelPainter, frame, attributeColourController, treeViewer)); |
80 | 82 | treeViewer.setTipLabelPainter(tipLabelPainter); |
81 | 83 | |
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 | } | |
82 | 91 | // Create a node label painter and its controller |
83 | 92 | final BasicLabelPainter nodeLabelPainter = new BasicLabelPainter(BasicLabelPainter.PainterIntent.NODE); |
84 | 93 | nodeLabelPainter.setVisible(false); |
85 | 94 | controlPalette.addController(new LabelPainterController("Node Labels", "nodeLabels", nodeLabelPainter, frame, attributeColourController, treeViewer)); |
86 | 95 | 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); | |
87 | 107 | |
88 | 108 | // Create a node bar painter and its controller |
89 | 109 | final NodeBarPainter nodeBarPainter = new NodeBarPainter(); |
91 | 111 | nodeBarPainter.setVisible(false); |
92 | 112 | controlPalette.addController(new NodeBarController("Node Bars", nodeBarPainter, treeViewer)); |
93 | 113 | 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); | |
100 | 114 | |
101 | 115 | // Create a branch label painter and its controller |
102 | 116 | final BasicLabelPainter branchLabelPainter = new BasicLabelPainter(BasicLabelPainter.PainterIntent.BRANCH); |
192 | 206 | if (utilityPanel == null) { |
193 | 207 | return; |
194 | 208 | } |
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 | } | |
206 | 220 | |
207 | 221 | public void toggleMidpointRoot() { |
208 | 222 | treesController.toggleMidpointRoot(); |
55 | 55 | public class JSONTreeExporter implements TreeExporter { |
56 | 56 | public static final String treeNameAttributeKey = "name"; |
57 | 57 | |
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" })); | |
59 | 60 | public final static String ORIGIN = "2013.34520547945"; |
60 | 61 | |
61 | 62 | public JSONTreeExporter(Writer writer) { |
120 | 120 | treeViewer, |
121 | 121 | "tipLabels", tipLabelPainter, |
122 | 122 | "nodeLabels", nodeLabelPainter, |
123 | "branchLabels", branchLabelPainter)); | |
123 | "branchLabels", branchLabelPainter, | |
124 | true, true)); | |
124 | 125 | |
125 | 126 | controlPalette1.addController(new LabelPainterController( |
126 | 127 | "tipLabels", tipLabelPainter, |
138 | 139 | "tipLabels", tipLabelPainter, |
139 | 140 | "nodeLabels", nodeLabelPainter, |
140 | 141 | "branchLabels", branchLabelPainter, |
141 | true)); | |
142 | true, false)); | |
142 | 143 | |
143 | 144 | controlPalette1.addController(new LabelPainterController( |
144 | 145 | "tipLabels", tipLabelPainter, |
159 | 160 | "tipLabels", tipLabelPainter, |
160 | 161 | "nodeLabels", nodeLabelPainter, |
161 | 162 | "branchLabels", branchLabelPainter, |
162 | true)); | |
163 | true, false)); | |
163 | 164 | |
164 | 165 | controlPalette2.addController(new TreeColouringController(treeViewer, "Clustering:")); |
165 | 166 | add(controlPalette2.getPanel(), BorderLayout.NORTH); |
21 | 21 | |
22 | 22 | import figtree.treeviewer.TreeViewer; |
23 | 23 | import figtree.treeviewer.painters.AttributeComboHelper; |
24 | import figtree.treeviewer.painters.AttributeComboHelperListener; | |
24 | 25 | import jam.controlpalettes.AbstractController; |
25 | 26 | import jam.panels.OptionsPanel; |
26 | 27 | |
30 | 31 | import java.util.Map; |
31 | 32 | |
32 | 33 | import figtree.treeviewer.painters.LabelPainter; |
33 | import sun.jvm.hotspot.tools.FinalizerInfo; | |
34 | 34 | |
35 | 35 | /** |
36 | 36 | * @author Andrew Rambaut |
47 | 47 | private static final String DISPLAY_ATTRIBUTE_KEY = "displayAttribute"; |
48 | 48 | |
49 | 49 | public LabelPainterController(String tipKey, |
50 | final LabelPainter tipLabelPainter, | |
50 | final SimpleLabelPainter tipLabelPainter, | |
51 | 51 | String nodeKey, |
52 | final LabelPainter nodeLabelPainter, | |
52 | final SimpleLabelPainter nodeLabelPainter, | |
53 | 53 | String branchKey, |
54 | final LabelPainter branchLabelPainter, | |
54 | final SimpleLabelPainter branchLabelPainter, | |
55 | 55 | final TreeViewer treeViewer) { |
56 | 56 | |
57 | 57 | this.tipKey = tipKey; |
67 | 67 | |
68 | 68 | } |
69 | 69 | |
70 | private JComboBox setupComboBox(String title, final LabelPainter labelPainter, final TreeViewer treeViewer) { | |
70 | private JComboBox setupComboBox(String title, final SimpleLabelPainter labelPainter, final TreeViewer treeViewer) { | |
71 | 71 | // String[] attributes = labelPainter.getAttributes(); |
72 | 72 | final JComboBox displayAttributeCombo = new JComboBox(); |
73 | 73 | 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()); | |
75 | 78 | optionsPanel.addComponentWithLabel(title, displayAttributeCombo); |
76 | 79 | |
77 | 80 | displayAttributeCombo.addItemListener(new ItemListener() { |
78 | 81 | public void itemStateChanged(ItemEvent itemEvent) { |
79 | 82 | String attribute = (String)displayAttributeCombo.getSelectedItem(); |
80 | if (attribute.equals("none")) { | |
83 | if (attribute == null || attribute.equalsIgnoreCase("none")) { | |
81 | 84 | labelPainter.setVisible(false); |
82 | 85 | } else { |
83 | 86 | labelPainter.setDisplayAttribute(attribute); |
49 | 49 | */ |
50 | 50 | public class SimpleLabelPainter extends LabelPainter<Node> { |
51 | 51 | |
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) { | |
57 | 57 | super(intent); |
58 | 58 | |
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 | } | |
140 | 140 | |
141 | 141 | public Set<Attributable> getAttributableItems() { |
142 | 142 | return null; |
143 | 143 | } |
144 | 144 | |
145 | 145 | 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(); | |
300 | 300 | //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 | } | |
322 | 322 | |
323 | 323 | public String getDisplayAttribute() { |
324 | 324 | return displayAttribute; |
325 | 325 | } |
326 | 326 | |
327 | 327 | 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; | |
345 | 343 | |
346 | 344 | }⏎ |
19 | 19 | |
20 | 20 | package figtree.panel; |
21 | 21 | |
22 | import jebl.evolution.trees.SortedRootedTree; | |
22 | 23 | import jebl.evolution.trees.Tree; |
23 | 24 | import jebl.evolution.graphs.Node; |
24 | 25 | import figtree.treeviewer.TreeViewer; |
93 | 94 | final LabelPainter nodeLabelPainter, |
94 | 95 | String branchKey, |
95 | 96 | final LabelPainter branchLabelPainter) { |
96 | this(treeViewer, tipKey, tipLabelPainter, nodeKey, nodeLabelPainter, branchKey, branchLabelPainter, true); | |
97 | this(treeViewer, tipKey, tipLabelPainter, nodeKey, nodeLabelPainter, branchKey, branchLabelPainter, true, false); | |
97 | 98 | } |
98 | 99 | |
99 | 100 | public TreeAppearanceController(final TreeViewer treeViewer, |
103 | 104 | final LabelPainter nodeLabelPainter, |
104 | 105 | String branchKey, |
105 | 106 | final LabelPainter branchLabelPainter, |
106 | boolean hideColouring) { | |
107 | boolean hideColouring, | |
108 | boolean ordering) { | |
107 | 109 | this.treeViewer = treeViewer; |
108 | 110 | |
109 | 111 | this.hideColouring = hideColouring; |
246 | 248 | } |
247 | 249 | }); |
248 | 250 | } |
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 | } | |
250 | 275 | |
251 | 276 | private void setupAttributes(Collection<? extends Tree> trees) { |
252 | 277 | Object selected = branchColourAttributeCombo.getSelectedItem(); |
360 | 385 | private final JSpinner fontSizeSpinner; |
361 | 386 | private final JSpinner digitsSpinner; |
362 | 387 | |
363 | private final TreeViewer treeViewer; | |
388 | private final JComboBox orderCombo; | |
389 | ||
390 | private final TreeViewer treeViewer; | |
364 | 391 | |
365 | 392 | private final String tipKey; |
366 | 393 | private final String nodeKey; |
59 | 59 | |
60 | 60 | |
61 | 61 | 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) { | |
62 | 68 | this.treeViewer = treeViewer; |
63 | 69 | |
64 | 70 | titleLabel = new JLabel(CONTROLLER_TITLE); |
65 | 71 | |
66 | 72 | optionsPanel = new OptionsPanel(); |
67 | 73 | |
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 | } | |
125 | 143 | } |
126 | 144 | |
127 | 145 | public JComponent getTitleComponent() { |
273 | 273 | return treePane.getSelectedTips(); |
274 | 274 | } |
275 | 275 | |
276 | public Set<Taxon> getSelectedTaxa() { | |
277 | return treePane.getSelectedTaxa(); | |
278 | } | |
279 | ||
276 | 280 | /** |
277 | 281 | * Select taxa with a search string matching the currently displayed attribute |
278 | 282 | * @param searchType |
287 | 291 | } |
288 | 292 | |
289 | 293 | selectTaxa(attributeName, searchType, searchString, caseSensitive); |
294 | ||
295 | scrollToSelectedTips(); | |
290 | 296 | } |
291 | 297 | |
292 | 298 | public void selectTaxa(String attributeName, TextSearchType searchType, String searchString, boolean caseSensitive) { |
503 | 509 | return false; |
504 | 510 | } |
505 | 511 | |
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 | ||
506 | 520 | public void cartoonSelectedNodes() { |
507 | 521 | treePane.cartoonSelectedNodes(); |
508 | 522 | fireTreeSettingsChanged(); |
589 | 603 | public void removeTreeSelectionListener(TreeSelectionListener treeSelectionListener) { |
590 | 604 | treePane.removeTreeSelectionListener(treeSelectionListener); |
591 | 605 | } |
606 | ||
607 | public TreePaneSelector.SelectionMode getSelectionMode() { | |
608 | return treePaneSelector.getSelectionMode(); | |
609 | } | |
610 | ||
592 | 611 | |
593 | 612 | public void setSelectionMode(TreePaneSelector.SelectionMode selectionMode) { |
594 | 613 | TreePaneSelector.SelectionMode oldSelectionMode = treePaneSelector.getSelectionMode(); |
628 | 647 | // nodeBarPainter.setupAttributes(trees); |
629 | 648 | } |
630 | 649 | |
650 | public void setTipShapePainter(NodeShapePainter tipShapePainter) { | |
651 | treePane.setTipShapePainter(tipShapePainter); | |
652 | } | |
653 | ||
631 | 654 | public void setNodeShapePainter(NodeShapePainter nodeShapePainter) { |
632 | 655 | treePane.setNodeShapePainter(nodeShapePainter); |
633 | 656 | } |
665 | 665 | if (!isCalibrated) |
666 | 666 | calibrate(); |
667 | 667 | |
668 | if (minorTick <= 0) { | |
669 | return 0; | |
670 | } | |
671 | ||
668 | 672 | if (majorTickIndex == majorTickCount-1) |
669 | 673 | return (int)((maxAxis-maxTick)/minorTick); |
670 | 674 | else if (majorTickIndex==-1) |
33 | 33 | */ |
34 | 34 | public class TimeScale { |
35 | 35 | |
36 | public TimeScale(double rootAge) { | |
36 | private boolean isReversed = false; | |
37 | ||
38 | public TimeScale(double rootAge) { | |
37 | 39 | this.rootAge = rootAge; |
38 | 40 | this.scaleFactor = Double.NaN; |
39 | 41 | this.offsetAge = 0.0; |
49 | 51 | if (Double.isNaN(scaleFactor)) { |
50 | 52 | return rootAge / tree.getHeight(tree.getRootNode()); |
51 | 53 | } |
52 | return scaleFactor; | |
54 | return scaleFactor * (isReversed ? -1.0 : 1.0); | |
53 | 55 | } |
54 | 56 | |
55 | 57 | public double getAge(double height, RootedTree tree) { |
70 | 72 | return (time / getScaleFactor(tree)); |
71 | 73 | } |
72 | 74 | |
73 | private final double rootAge; | |
75 | public void setReversed(boolean isReversed) { | |
76 | this.isReversed = isReversed; | |
77 | } | |
78 | ||
79 | private final double rootAge; | |
74 | 80 | private final double offsetAge; |
75 | 81 | private final double scaleFactor; |
82 | ||
76 | 83 | } |
32 | 32 | import java.awt.*; |
33 | 33 | import java.awt.geom.*; |
34 | 34 | import java.awt.print.*; |
35 | import java.io.IOException; | |
36 | 35 | import java.util.*; |
37 | 36 | import java.util.List; |
38 | 37 | |
47 | 46 | * $LastChangedRevision$ |
48 | 47 | */ |
49 | 48 | public class TreePane extends JComponent implements PainterListener, Printable { |
49 | public final static boolean DEBUG_OUTLINE = false; | |
50 | ||
50 | 51 | public enum RootingType { |
51 | 52 | USER_ROOTING("User Selection"), |
52 | 53 | MID_POINT("Midpoint"); |
61 | 62 | private String name; |
62 | 63 | } |
63 | 64 | |
64 | ||
65 | public final static boolean DEBUG_OUTLINE = false; | |
66 | 65 | |
67 | 66 | public final String CARTOON_ATTRIBUTE_NAME = "!cartoon"; |
68 | 67 | public final String COLLAPSE_ATTRIBUTE_NAME = "!collapse"; |
90 | 89 | } |
91 | 90 | } |
92 | 91 | |
92 | private void recalibrate() { | |
93 | calibrated = false; | |
94 | } | |
95 | ||
93 | 96 | private void setupTree() { |
94 | 97 | tree = constructTransformedTree(originalTree); |
95 | 98 | |
96 | 99 | recalculateCollapsedNodes(); |
97 | 100 | |
98 | calibrated = false; | |
101 | recalibrate(); | |
99 | 102 | invalidate(); |
100 | 103 | repaint(); |
101 | 104 | } |
151 | 154 | |
152 | 155 | treeLayout.addTreeLayoutListener(new TreeLayoutListener() { |
153 | 156 | public void treeLayoutChanged() { |
154 | calibrated = false; | |
157 | recalibrate(); | |
155 | 158 | repaint(); |
156 | 159 | } |
157 | 160 | }); |
158 | calibrated = false; | |
161 | recalibrate(); | |
159 | 162 | invalidate(); |
160 | 163 | repaint(); |
161 | 164 | } |
166 | 169 | |
167 | 170 | public void setTimeScale(TimeScale timeScale) { |
168 | 171 | this.timeScale = timeScale; |
169 | calibrated = false; | |
172 | this.timeScale.setReversed(isAxisReversed()); | |
173 | recalibrate(); | |
170 | 174 | repaint(); |
171 | 175 | } |
172 | 176 | |
232 | 236 | } |
233 | 237 | node.setAttribute("!rotate", rotate); |
234 | 238 | |
235 | calibrated = false; | |
239 | recalibrate(); | |
236 | 240 | invalidate(); |
237 | 241 | repaint(); |
238 | 242 | } |
246 | 250 | node.removeAttribute("!rotate"); |
247 | 251 | } |
248 | 252 | |
249 | calibrated = false; | |
253 | recalibrate(); | |
250 | 254 | invalidate(); |
251 | 255 | repaint(); |
252 | 256 | } |
299 | 303 | */ |
300 | 304 | public double scaleOnAxis(double value) { |
301 | 305 | 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); | |
304 | 308 | } else { |
305 | 309 | return treeBounds.getX() + (height * treeScale); |
306 | 310 | } |
307 | 311 | } |
308 | 312 | |
309 | 313 | public Shape getAxisLine(double value) { |
314 | if (isAxisReversed()) { | |
315 | value = maxTreeHeight - value; | |
316 | } else { | |
317 | value -= rootHeightOffset; | |
318 | } | |
310 | 319 | double height = timeScale.getHeight(value, tree); |
311 | 320 | Shape line = treeLayout.getAxisLine(height); |
312 | 321 | if (line != null) { |
326 | 335 | |
327 | 336 | public void setAxisOrigin(double axisOrigin) { |
328 | 337 | this.axisOrigin = axisOrigin; |
329 | calibrated = false; | |
338 | recalibrate(); | |
330 | 339 | repaint(); |
331 | 340 | } |
332 | 341 | |
333 | 342 | 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; | |
337 | 351 | } |
338 | 352 | |
339 | 353 | private void setupScaleAxis() { |
356 | 370 | public void setRootAge(double rootAge) { |
357 | 371 | double rootLength = timeScale.getHeight(rootAge, tree) - tree.getHeight(tree.getRootNode()); |
358 | 372 | treeLayout.setRootLength(rootLength); |
359 | calibrated = false; | |
373 | recalibrate(); | |
360 | 374 | repaint(); |
361 | 375 | } |
362 | 376 | |
375 | 389 | |
376 | 390 | public void setTickSpacing(double userMajorTickSpacing, double userMinorTickSpacing) { |
377 | 391 | scaleAxis.setManualAxis(userMajorTickSpacing, userMinorTickSpacing); |
378 | calibrated = false; | |
392 | recalibrate(); | |
379 | 393 | repaint(); |
380 | 394 | } |
381 | 395 | |
382 | 396 | public void setAutomaticScale() { |
383 | 397 | scaleAxis.setAutomatic(); |
384 | calibrated = false; | |
398 | recalibrate(); | |
385 | 399 | repaint(); |
386 | 400 | } |
387 | 401 | |
388 | 402 | public void painterChanged() { |
389 | calibrated = false; | |
403 | recalibrate(); | |
390 | 404 | repaint(); |
391 | 405 | } |
392 | 406 | |
393 | 407 | public void painterSettingsChanged() { |
394 | calibrated = false; | |
408 | recalibrate(); | |
395 | 409 | repaint(); |
396 | 410 | } |
397 | 411 | |
398 | 412 | public void attributesChanged() { |
399 | calibrated = false; | |
413 | recalibrate(); | |
400 | 414 | repaint(); |
401 | 415 | } |
402 | 416 | |
495 | 509 | |
496 | 510 | public void setShowingTipCallouts(boolean showingTipCallouts) { |
497 | 511 | this.showingTipCallouts = showingTipCallouts; |
498 | calibrated = false; | |
512 | recalibrate(); | |
499 | 513 | repaint(); |
500 | 514 | } |
501 | 515 | |
719 | 733 | Object[] values = new Object[] { tipCount, height }; |
720 | 734 | node.setAttribute(CARTOON_ATTRIBUTE_NAME, values); |
721 | 735 | } |
722 | calibrated = false; | |
736 | recalibrate(); | |
723 | 737 | repaint(); |
724 | 738 | } else { |
725 | 739 | for (Node child : tree.getChildren(node)) { |
745 | 759 | Object[] values = new Object[] { tipName, height }; |
746 | 760 | node.setAttribute(COLLAPSE_ATTRIBUTE_NAME, values); |
747 | 761 | } |
748 | calibrated = false; | |
762 | recalibrate(); | |
749 | 763 | repaint(); |
750 | 764 | } else { |
751 | 765 | for (Node child : tree.getChildren(node)) { |
768 | 782 | Object[] values = new Object[] { tipCount, height, color }; |
769 | 783 | node.setAttribute(HILIGHT_ATTRIBUTE_NAME, values); |
770 | 784 | |
771 | calibrated = false; | |
785 | recalibrate(); | |
772 | 786 | repaint(); |
773 | 787 | } else { |
774 | 788 | for (Node child : tree.getChildren(node)) { |
805 | 819 | Object[] values = new Object[] { tipCount, height, oldValues[2] }; |
806 | 820 | node.setAttribute(HILIGHT_ATTRIBUTE_NAME, values); |
807 | 821 | } |
808 | calibrated = false; | |
822 | recalibrate(); | |
809 | 823 | repaint(); |
810 | 824 | } else { |
811 | 825 | for (Node child : tree.getChildren(node)) { |
827 | 841 | node.removeAttribute(CARTOON_ATTRIBUTE_NAME); |
828 | 842 | } |
829 | 843 | } |
830 | calibrated = false; | |
844 | recalibrate(); | |
831 | 845 | repaint(); |
832 | 846 | } |
833 | 847 | } |
845 | 859 | if (node.getAttribute(CARTOON_ATTRIBUTE_NAME) != null) { |
846 | 860 | node.removeAttribute(CARTOON_ATTRIBUTE_NAME); |
847 | 861 | } |
848 | calibrated = false; | |
862 | recalibrate(); | |
849 | 863 | repaint(); |
850 | 864 | } else { |
851 | 865 | for (Node child : tree.getChildren(node)) { |
864 | 878 | node.removeAttribute(HILIGHT_ATTRIBUTE_NAME); |
865 | 879 | } |
866 | 880 | } |
867 | calibrated = false; | |
881 | recalibrate(); | |
868 | 882 | repaint(); |
869 | 883 | } |
870 | 884 | } |
875 | 889 | if (selectedNodes.size() == 0 || selectedNodes.contains(node)) { |
876 | 890 | if (node.getAttribute(HILIGHT_ATTRIBUTE_NAME) != null) { |
877 | 891 | node.removeAttribute(HILIGHT_ATTRIBUTE_NAME); |
878 | calibrated = false; | |
892 | recalibrate(); | |
879 | 893 | repaint(); |
880 | 894 | } |
881 | 895 | } |
976 | 990 | if (this.tipLabelPainter != null) { |
977 | 991 | this.tipLabelPainter.addPainterListener(this); |
978 | 992 | } |
979 | calibrated = false; | |
993 | recalibrate(); | |
980 | 994 | repaint(); |
981 | 995 | } |
982 | 996 | |
993 | 1007 | if (this.nodeLabelPainter != null) { |
994 | 1008 | this.nodeLabelPainter.addPainterListener(this); |
995 | 1009 | } |
996 | calibrated = false; | |
1010 | recalibrate(); | |
997 | 1011 | repaint(); |
998 | 1012 | } |
999 | 1013 | |
1010 | 1024 | if (this.branchLabelPainter != null) { |
1011 | 1025 | this.branchLabelPainter.addPainterListener(this); |
1012 | 1026 | } |
1013 | calibrated = false; | |
1027 | recalibrate(); | |
1014 | 1028 | repaint(); |
1015 | 1029 | } |
1016 | 1030 | |
1027 | 1041 | if (this.nodeBarPainter != null) { |
1028 | 1042 | this.nodeBarPainter.addPainterListener(this); |
1029 | 1043 | } |
1030 | calibrated = false; | |
1044 | recalibrate(); | |
1031 | 1045 | repaint(); |
1032 | 1046 | } |
1033 | 1047 | |
1035 | 1049 | return nodeBarPainter; |
1036 | 1050 | } |
1037 | 1051 | |
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 | } | |
1038 | 1068 | |
1039 | 1069 | public void setNodeShapePainter(NodeShapePainter nodeShapePainter) { |
1040 | 1070 | nodeShapePainter.setTreePane(this); |
1045 | 1075 | if (this.nodeShapePainter != null) { |
1046 | 1076 | this.nodeShapePainter.addPainterListener(this); |
1047 | 1077 | } |
1048 | calibrated = false; | |
1078 | recalibrate(); | |
1049 | 1079 | repaint(); |
1050 | 1080 | } |
1051 | 1081 | |
1061 | 1091 | |
1062 | 1092 | scalePainters.add(scalePainter); |
1063 | 1093 | |
1064 | calibrated = false; | |
1094 | recalibrate(); | |
1065 | 1095 | repaint(); |
1066 | 1096 | } |
1067 | 1097 | |
1072 | 1102 | |
1073 | 1103 | scalePainters.remove(scalePainter); |
1074 | 1104 | |
1075 | calibrated = false; | |
1105 | recalibrate(); | |
1076 | 1106 | repaint(); |
1077 | 1107 | } |
1078 | 1108 | |
1085 | 1115 | if (this.scaleGridPainter != null) { |
1086 | 1116 | this.scaleGridPainter.addPainterListener(this); |
1087 | 1117 | } |
1088 | calibrated = false; | |
1118 | recalibrate(); | |
1089 | 1119 | repaint(); |
1090 | 1120 | } |
1091 | 1121 | |
1095 | 1125 | |
1096 | 1126 | this.legendPainter = legendPainter; |
1097 | 1127 | |
1098 | calibrated = false; | |
1128 | recalibrate(); | |
1099 | 1129 | repaint(); |
1100 | 1130 | } |
1101 | 1131 | |
1109 | 1139 | |
1110 | 1140 | public void setLabelSpacing(float labelSpacing) { |
1111 | 1141 | this.labelXOffset = labelSpacing; |
1112 | calibrated = false; | |
1142 | recalibrate(); | |
1113 | 1143 | repaint(); |
1114 | 1144 | } |
1115 | 1145 | |
1120 | 1150 | super.setPreferredSize(dimension); |
1121 | 1151 | } |
1122 | 1152 | |
1123 | calibrated = false; | |
1153 | recalibrate(); | |
1124 | 1154 | } |
1125 | 1155 | |
1126 | 1156 | public double getHeightAt(Graphics2D graphics2D, Point2D point) { |
1134 | 1164 | |
1135 | 1165 | public Node getNodeAt(Graphics2D g2, Point point) { |
1136 | 1166 | Rectangle rect = new Rectangle(point.x - 1, point.y - 1, 3, 3); |
1167 | rect.translate(-insets.left, -insets.top); | |
1137 | 1168 | |
1138 | 1169 | for (Node node : tree.getExternalNodes()) { |
1139 | 1170 | Shape taxonLabelBound = tipLabelBounds.get(node); |
1189 | 1220 | |
1190 | 1221 | public Set<Node> getSelectedTips() { |
1191 | 1222 | 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; | |
1192 | 1231 | } |
1193 | 1232 | |
1194 | 1233 | public RootedTree getSelectedSubtree() { |
1276 | 1315 | this.rulerHeight = rulerHeight; |
1277 | 1316 | } |
1278 | 1317 | |
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 | ||
1279 | 1326 | public void scrollPointToVisible(Point point) { |
1280 | 1327 | scrollRectToVisible(new Rectangle(point.x, point.y, 0, 0)); |
1281 | 1328 | } |
1316 | 1363 | public void paint(Graphics graphics) { |
1317 | 1364 | if (tree == null) return; |
1318 | 1365 | |
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 | // | |
1325 | 1372 | final Graphics2D g2 = (Graphics2D) graphics; |
1373 | g2.translate(insets.left, insets.top); | |
1374 | ||
1326 | 1375 | if (!calibrated) { |
1327 | 1376 | calibrate(g2, getWidth(), getHeight()); |
1328 | 1377 | } |
1401 | 1450 | Graphics2D g2 = (Graphics2D) graphics; |
1402 | 1451 | g2.translate(pageFormat.getImageableX(), pageFormat.getImageableY()); |
1403 | 1452 | |
1404 | calibrated = false; | |
1453 | recalibrate(); | |
1405 | 1454 | setDoubleBuffered(false); |
1406 | 1455 | |
1407 | 1456 | drawTree(g2, pageFormat.getImageableWidth(), pageFormat.getImageableHeight()); |
1408 | 1457 | |
1409 | 1458 | setDoubleBuffered(true); |
1410 | calibrated = false; | |
1459 | recalibrate(); | |
1411 | 1460 | |
1412 | 1461 | return PAGE_EXISTS; |
1413 | 1462 | } |
1443 | 1492 | scalePainter.paint(g2, this, Painter.Justification.CENTER, scaleBounds); |
1444 | 1493 | } |
1445 | 1494 | } |
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 | ||
1446 | 1503 | |
1447 | 1504 | // Paint backgrounds |
1448 | 1505 | if (nodeBackgroundDecorator != null) { |
1456 | 1513 | g2.setPaint(background); |
1457 | 1514 | g2.fill(transNodePath); |
1458 | 1515 | |
1459 | // Experimental outlining - requires order of drawing to be pre-order | |
1516 | // Experimental outlining - requires order of drawing to be pre-order | |
1460 | 1517 | // g2.setStroke(new BasicStroke(8, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND)); |
1461 | 1518 | // g2.draw(transNodePath); |
1462 | 1519 | } |
1495 | 1552 | } |
1496 | 1553 | } |
1497 | 1554 | |
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 | ||
1505 | 1555 | if (DEBUG_OUTLINE) { |
1506 | g2.setPaint(Color.red); | |
1556 | g2.setPaint(Color.blue); | |
1507 | 1557 | g2.draw(treeBounds); |
1508 | 1558 | } |
1509 | 1559 | |
1626 | 1676 | } |
1627 | 1677 | } |
1628 | 1678 | |
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 | ||
1629 | 1687 | // Paint tip labels |
1630 | 1688 | if (tipLabelPainter != null && tipLabelPainter.isVisible()) { |
1631 | 1689 | |
1636 | 1694 | Painter.Justification tipLabelJustification = tipLabelJustifications.get(node); |
1637 | 1695 | g2.transform(tipLabelTransform); |
1638 | 1696 | |
1697 | double labelWidth = tipLabelWidths.get(node); | |
1639 | 1698 | 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())); | |
1641 | 1700 | |
1642 | 1701 | g2.setTransform(oldTransform); |
1643 | 1702 | |
1680 | 1739 | final double preferredWidth = branchLabelPainter.getPreferredWidth(); |
1681 | 1740 | final double preferredHeight = branchLabelPainter.getPreferredHeight(); |
1682 | 1741 | |
1683 | //Line2D labelPath = treeLayout.getBranchLabelPath(node); | |
1684 | ||
1685 | 1742 | branchLabelPainter.paint(g2, node, Painter.Justification.CENTER, |
1686 | //new Rectangle2D.Double(-preferredWidth/2, -preferredHeight, preferredWidth, preferredHeight)); | |
1687 | 1743 | new Rectangle2D.Double(0, 0, preferredWidth, preferredHeight)); |
1688 | 1744 | |
1689 | 1745 | g2.setTransform(oldTransform); |
1701 | 1757 | treeLayout.layout(tree, treeLayoutCache); |
1702 | 1758 | |
1703 | 1759 | maxTreeHeight = tree.getHeight(tree.getRootNode()) + treeLayout.getRootLength(); |
1760 | rootHeightOffset = 0.0; | |
1761 | ||
1704 | 1762 | |
1705 | 1763 | // First of all get the bounds for the unscaled tree |
1706 | 1764 | treeBounds = null; |
1754 | 1812 | // bounds on node bars |
1755 | 1813 | if (!isTransformBranchesOn() && nodeBarPainter != null && nodeBarPainter.isVisible()) { |
1756 | 1814 | nodeBars.clear(); |
1815 | ||
1757 | 1816 | // Iterate though the nodes |
1758 | 1817 | for (Node node : tree.getInternalNodes()) { |
1759 | 1818 | |
1763 | 1822 | nodeBars.put(node, nodeBarPainter.getNodeBar()); |
1764 | 1823 | } |
1765 | 1824 | } |
1825 | ||
1766 | 1826 | if (nodeBarPainter.getMaxHeight() > maxTreeHeight) { |
1827 | rootHeightOffset = Math.max(nodeBarPainter.getMaxHeight() - maxTreeHeight, 0.0); | |
1767 | 1828 | maxTreeHeight = nodeBarPainter.getMaxHeight(); |
1768 | 1829 | } |
1769 | 1830 | } |
1831 | ||
1770 | 1832 | |
1771 | 1833 | // totalTreeBounds includes all the stuff which is not in a tree scale (like labels and shapes) but in |
1772 | 1834 | // screen pixel scale. This is added to the treeBounds to make space round the edge. |
1773 | 1835 | |
1774 | 1836 | // 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(); | |
1776 | 1841 | |
1777 | 1842 | if (tipLabelPainter != null && tipLabelPainter.isVisible()) { |
1778 | 1843 | |
1779 | tipLabelWidth = 0.0; | |
1780 | final double tipLabelHeight = tipLabelPainter.getPreferredHeight(); | |
1844 | // calculateMaxTipLabelWidth(g2, tree.getRootNode()); | |
1781 | 1845 | |
1782 | 1846 | // 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); | |
1784 | 1848 | } |
1785 | 1849 | |
1786 | 1850 | if (nodeLabelPainter != null && nodeLabelPainter.isVisible()) { |
1823 | 1887 | } |
1824 | 1888 | |
1825 | 1889 | // 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 | } | |
1826 | 1904 | if (nodeShapePainter != null && nodeShapePainter.isVisible()) { |
1827 | 1905 | 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()) { | |
1830 | 1908 | |
1831 | 1909 | Rectangle2D shapeBounds = nodeShapePainter.calibrate(g2, node); |
1832 | 1910 | if (shapeBounds != null) { |
1841 | 1919 | // Now rescale the scale axis |
1842 | 1920 | setupScaleAxis(); |
1843 | 1921 | |
1844 | totalScaleBounds = new Rectangle2D.Double(0.0, 0.0, 0.0, 0.0); | |
1922 | bottomPanelBounds = new Rectangle2D.Double(); | |
1845 | 1923 | double y = totalTreeBounds.getHeight(); |
1846 | 1924 | for (ScalePainter scalePainter : scalePainters) { |
1847 | 1925 | if (scalePainter.isVisible()) { |
1848 | 1926 | scalePainter.calibrate(g2, this); |
1849 | 1927 | Rectangle2D sb = new Rectangle2D.Double( |
1850 | totalTreeBounds.getX(), y, | |
1851 | totalTreeBounds.getWidth(), scalePainter.getPreferredHeight()); | |
1928 | treeBounds.getX(), y, | |
1929 | treeBounds.getWidth(), scalePainter.getPreferredHeight()); | |
1852 | 1930 | y += sb.getHeight(); |
1853 | totalScaleBounds.add(sb); | |
1931 | bottomPanelBounds.add(sb); | |
1854 | 1932 | scaleBounds.put(scalePainter, sb); |
1855 | 1933 | } |
1856 | 1934 | } |
1857 | totalTreeBounds.add(totalScaleBounds); | |
1858 | ||
1935 | ||
1936 | leftPanelBounds = new Rectangle2D.Double(); | |
1859 | 1937 | if (legendPainter != null && legendPainter.isVisible()) { |
1860 | 1938 | legendPainter.calibrate(g2, this); |
1861 | 1939 | 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 | } | |
1874 | 1943 | |
1875 | 1944 | final double availableW = width - insets.left - insets.right; |
1876 | 1945 | final double availableH = height - insets.top - insets.bottom; |
1912 | 1981 | // Get the amount of canvas that is going to be taken up by the tree - |
1913 | 1982 | // The rest is taken up by taxon labels which don't scale |
1914 | 1983 | |
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(); | |
1917 | 1986 | |
1918 | 1987 | double xScale; |
1919 | 1988 | double yScale; |
1947 | 2016 | yScale = h / treeBounds.getHeight(); |
1948 | 2017 | |
1949 | 2018 | // 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()); | |
1953 | 2021 | treeScale = xScale; |
1954 | 2022 | } |
1955 | 2023 | |
1957 | 2025 | |
1958 | 2026 | // Create the overall transform |
1959 | 2027 | transform = new AffineTransform(); |
1960 | transform.translate(xOffset + insets.left, yOffset + insets.top); | |
2028 | transform.translate(xOffset + leftPanelBounds.getWidth(), yOffset + topPanelBounds.getHeight()); | |
1961 | 2029 | transform.scale(xScale, yScale); |
1962 | 2030 | |
1963 | 2031 | // Get the bounds for the newly scaled tree |
1992 | 2060 | |
1993 | 2061 | Rectangle2D shapeBounds = nodeBarPainter.calibrate(g2, node); |
1994 | 2062 | if (shapeBounds != null) { |
2063 | shapeBounds = transform.createTransformedShape(shapeBounds).getBounds2D(); | |
1995 | 2064 | treeBounds.add(shapeBounds); |
1996 | 2065 | nodeBars.put(node, nodeBarPainter.getNodeBar()); |
1997 | 2066 | } |
2005 | 2074 | |
2006 | 2075 | if (tipLabelPainter != null && tipLabelPainter.isVisible()) { |
2007 | 2076 | final double labelHeight = tipLabelPainter.getPreferredHeight(); |
2008 | Rectangle2D labelBounds = new Rectangle2D.Double(0.0, 0.0, tipLabelWidth, labelHeight); | |
2009 | 2077 | |
2010 | 2078 | // Iterate though the external nodes with tip labels |
2011 | 2079 | for (Node node : treeLayoutCache.getTipLabelPathMap().keySet()) { |
2012 | 2080 | // Get the line that represents the path for the tip label |
2013 | 2081 | Line2D tipPath = treeLayoutCache.getTipLabelPath(node); |
2014 | 2082 | |
2083 | final double labelWidth = tipLabelWidths.get(node); | |
2084 | Rectangle2D labelBounds = new Rectangle2D.Double(0.0, 0.0, labelWidth, labelHeight); | |
2085 | ||
2015 | 2086 | // 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); | |
2017 | 2088 | |
2018 | 2089 | // Store the transformed bounds in the map for use when selecting |
2019 | 2090 | tipLabelBounds.put(node, taxonTransform.createTransformedShape(labelBounds)); |
2101 | 2172 | } |
2102 | 2173 | } |
2103 | 2174 | |
2175 | nodeShapeTransforms.clear(); | |
2104 | 2176 | if (nodeShapePainter != null && nodeShapePainter.isVisible()) { |
2105 | nodeShapeTransforms.clear(); | |
2106 | 2177 | // Iterate though the nodes |
2107 | 2178 | for (Node node : nodePoints.keySet()) { |
2108 | 2179 | Line2D shapePath = getTreeLayoutCache().getNodeShapePath(node); |
2111 | 2182 | } |
2112 | 2183 | } |
2113 | 2184 | } |
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; | |
2116 | 2197 | for (ScalePainter scalePainter : scalePainters) { |
2117 | 2198 | if (scalePainter.isVisible()) { |
2118 | 2199 | scalePainter.calibrate(g2, this); |
2120 | 2201 | } |
2121 | 2202 | } |
2122 | 2203 | |
2123 | totalScaleBounds = new Rectangle2D.Double(0, y, treeBounds.getWidth(), 0.0); | |
2204 | bottomPanelBounds = new Rectangle2D.Double(0, y, treeBounds.getWidth(), 0.0); | |
2124 | 2205 | for (ScalePainter scalePainter : scalePainters) { |
2125 | 2206 | if (scalePainter.isVisible()) { |
2126 | 2207 | scalePainter.calibrate(g2, this); |
2127 | 2208 | final double h1 = scalePainter.getPreferredHeight(); |
2128 | 2209 | Rectangle2D sb = new Rectangle2D.Double(treeBounds.getX(), y, treeBounds.getWidth(), h1); |
2129 | 2210 | y += h1; |
2130 | totalScaleBounds.add(sb); | |
2211 | bottomPanelBounds.add(sb); | |
2131 | 2212 | scaleBounds.put(scalePainter, sb); |
2132 | 2213 | } |
2133 | 2214 | } |
2134 | 2215 | |
2216 | leftPanelBounds = new Rectangle2D.Double(0, 0, 0.0, 0.0); | |
2135 | 2217 | if (legendPainter != null && legendPainter.isVisible()) { |
2136 | 2218 | legendPainter.calibrate(g2, this); |
2137 | 2219 | 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 | ||
2139 | 2223 | } |
2140 | 2224 | |
2141 | 2225 | calloutPaths.clear(); |
2144 | 2228 | calibrated = true; |
2145 | 2229 | } |
2146 | 2230 | |
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) { | |
2148 | 2246 | |
2149 | 2247 | if (tree.isExternal(node) || node.getAttribute(COLLAPSE_ATTRIBUTE_NAME) != null) { |
2150 | 2248 | 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); | |
2155 | 2254 | |
2156 | 2255 | // Get the line that represents the path for the taxon label |
2157 | 2256 | Line2D taxonPath = treeLayoutCache.getTipLabelPath(node); |
2158 | 2257 | |
2159 | 2258 | if (taxonPath != null) { |
2160 | 2259 | // 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); | |
2162 | 2261 | |
2163 | 2262 | // and add the translated bounds to the overall bounds |
2164 | 2263 | totalTreeBounds.add(taxonTransform.createTransformedShape(labelBounds).getBounds2D()); |
2165 | 2264 | } |
2166 | 2265 | } else { |
2167 | 2266 | for (Node child : tree.getChildren(node)) { |
2168 | calibrateTipLabels(g2, child, tipLabelHeight, totalTreeBounds); | |
2267 | calibrateTipLabels(g2, child, totalTreeBounds); | |
2169 | 2268 | } |
2170 | 2269 | } |
2171 | 2270 | } |
2172 | 2271 | |
2173 | 2272 | private AffineTransform calculateTransform(AffineTransform globalTransform, Line2D line, |
2174 | double width, double height, boolean just) { | |
2273 | double width, double height, boolean justify) { | |
2175 | 2274 | final Point2D origin = line.getP1(); |
2176 | 2275 | if (globalTransform != null) { |
2177 | 2276 | globalTransform.transform(origin, origin); |
2192 | 2291 | // to shift it by the entire width of the string. |
2193 | 2292 | final double ty = origin.getY() - (height / 2.0); |
2194 | 2293 | double tx = origin.getX(); |
2195 | if( just) { | |
2294 | if (justify) { | |
2196 | 2295 | if (line.getX2() > line.getX1()) { |
2197 | 2296 | tx += labelXOffset; |
2198 | 2297 | } else { |
2226 | 2325 | |
2227 | 2326 | // Overridden methods to recalibrate tree when bounds change |
2228 | 2327 | public void setBounds(int x, int y, int width, int height) { |
2229 | calibrated = false; | |
2328 | recalibrate(); | |
2230 | 2329 | super.setBounds(x, y, width, height); |
2231 | 2330 | } |
2232 | 2331 | |
2233 | 2332 | public void setBounds(Rectangle rectangle) { |
2234 | calibrated = false; | |
2333 | recalibrate(); | |
2235 | 2334 | super.setBounds(rectangle); |
2236 | 2335 | } |
2237 | 2336 | |
2238 | 2337 | public void setSize(Dimension dimension) { |
2239 | calibrated = false; | |
2338 | recalibrate(); | |
2240 | 2339 | super.setSize(dimension); |
2241 | 2340 | } |
2242 | 2341 | |
2243 | 2342 | public void setSize(int width, int height) { |
2244 | calibrated = false; | |
2343 | recalibrate(); | |
2245 | 2344 | super.setSize(width, height); |
2246 | 2345 | } |
2247 | 2346 | |
2264 | 2363 | private Rectangle2D treeBounds = new Rectangle2D.Double(); |
2265 | 2364 | private double treeScale; |
2266 | 2365 | private double maxTreeHeight; |
2366 | private double rootHeightOffset; | |
2267 | 2367 | |
2268 | 2368 | private ScaleAxis scaleAxis = new ScaleAxis(ScaleAxis.AT_DATA, ScaleAxis.AT_DATA); |
2269 | 2369 | private double axisOrigin = 0.0; |
2270 | 2370 | private TimeScale timeScale = new TimeScale(1.0, 0.0); |
2371 | private boolean isAxisReversed = false; | |
2271 | 2372 | |
2272 | 2373 | //private Insets insets = new Insets(0, 0, 0, 0); |
2273 | 2374 | private Insets insets = new Insets(6, 6, 6, 6); |
2274 | 2375 | |
2275 | 2376 | private Set<Node> selectedNodes = new HashSet<Node>(); |
2276 | private Set<Node> selectedTips = new HashSet<Node>(); | |
2377 | private Set<Node> selectedTips = new LinkedHashSet<Node>(); | |
2277 | 2378 | |
2278 | 2379 | private double rulerHeight = -1.0; |
2279 | 2380 | private Rectangle2D dragRectangle = null; |
2290 | 2391 | |
2291 | 2392 | private Decorator nodeBackgroundDecorator = null; |
2292 | 2393 | |
2293 | private float labelXOffset = 5.0F; | |
2394 | private float labelXOffset = 10.0F; | |
2294 | 2395 | private LabelPainter<Node> tipLabelPainter = null; |
2295 | private double tipLabelWidth; | |
2396 | //private double maxTipLabelWidth; | |
2296 | 2397 | private LabelPainter<Node> nodeLabelPainter = null; |
2297 | 2398 | private LabelPainter<Node> branchLabelPainter = null; |
2298 | 2399 | |
2299 | 2400 | private NodeBarPainter nodeBarPainter = null; |
2300 | 2401 | |
2301 | 2402 | private NodeShapePainter nodeShapePainter = null; |
2403 | private NodeShapePainter tipShapePainter = null; | |
2302 | 2404 | |
2303 | 2405 | private List<ScalePainter> scalePainters = new ArrayList<ScalePainter>(); |
2304 | private Rectangle2D totalScaleBounds = null; | |
2305 | 2406 | private Map<ScalePainter, Rectangle2D> scaleBounds = new HashMap<ScalePainter, Rectangle2D>(); |
2306 | 2407 | |
2307 | 2408 | private ScaleGridPainter scaleGridPainter = null; |
2308 | 2409 | |
2309 | 2410 | private LegendPainter legendPainter = null; |
2310 | 2411 | 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(); | |
2311 | 2417 | |
2312 | 2418 | |
2313 | 2419 | private BasicStroke branchLineStroke = new BasicStroke(1.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER); |
2324 | 2430 | |
2325 | 2431 | private Map<Node, AffineTransform> tipLabelTransforms = new HashMap<Node, AffineTransform>(); |
2326 | 2432 | private Map<Node, Shape> tipLabelBounds = new HashMap<Node, Shape>(); |
2433 | private Map<Node, Double> tipLabelWidths = new HashMap<Node, Double>(); | |
2327 | 2434 | private Map<Node, Painter.Justification> tipLabelJustifications = new HashMap<Node, Painter.Justification>(); |
2328 | 2435 | |
2329 | 2436 | private Map<Node, AffineTransform> nodeLabelTransforms = new HashMap<Node, AffineTransform>(); |
2335 | 2442 | private Map<Node, Painter.Justification> branchLabelJustifications = new HashMap<Node, Painter.Justification>(); |
2336 | 2443 | |
2337 | 2444 | private Map<Node, Shape> nodeBars = new HashMap<Node, Shape>(); |
2445 | private Map<Node, Point2D> tipPoints = new HashMap<Node, Point2D>(); | |
2338 | 2446 | private Map<Node, Point2D> nodePoints = new HashMap<Node, Point2D>(); |
2339 | 2447 | private Map<Node, AffineTransform> nodeShapeTransforms = new HashMap<Node, AffineTransform>(); |
2340 | 2448 |
45 | 45 | public TreePaneRollOver(TreePane treePane) { |
46 | 46 | this.treePane = treePane; |
47 | 47 | treePane.addMouseMotionListener(this); |
48 | ||
49 | } | |
48 | } | |
50 | 49 | |
51 | 50 | public void mouseEntered(MouseEvent mouseEvent) { |
52 | 51 | } |
59 | 58 | if (tree != null) { |
60 | 59 | Node node = treePane.getNodeAt((Graphics2D) treePane.getGraphics(), mouseEvent.getPoint()); |
61 | 60 | 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)); | |
73 | 62 | } else { |
74 | fireStatusChanged(StatusPanel.NORMAL, " "); | |
63 | fireStatusChanged(StatusPanel.NORMAL, getNodeText(tree, tree.getRootNode())); | |
75 | 64 | } |
76 | 65 | } else { |
77 | 66 | fireStatusChanged(StatusPanel.NORMAL, " "); |
78 | 67 | } |
79 | 68 | } |
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 | } | |
80 | 83 | |
81 | 84 | public void mouseDragged(MouseEvent mouseEvent) { |
82 | 85 | } |
42 | 42 | */ |
43 | 43 | public ContinuousScale(String settings) { |
44 | 44 | if (settings.startsWith("{")) { |
45 | settings = settings.substring(1, settings.length()); | |
45 | settings = settings.substring(1, settings.length()); | |
46 | 46 | } |
47 | 47 | if (settings.endsWith("}")) { |
48 | 48 | settings = settings.substring(0, settings.length() - 1); |
87 | 87 | |
88 | 88 | // First collect the set of all attribute values |
89 | 89 | Set<Object> values = new TreeSet<Object>(); |
90 | ||
91 | boolean isNumber = true; | |
92 | ||
93 | // Find the range of numbers | |
90 | 94 | for (Attributable item : items) { |
91 | 95 | Object value = item.getAttribute(attributeName); |
92 | 96 | 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 | } | |
119 | 117 | } |
120 | 118 | } |
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 | ||
126 | 128 | } |
127 | if (realValue > maxValue) { | |
128 | maxValue = realValue; | |
129 | } | |
130 | ||
129 | values.add(realValue); | |
131 | 130 | } |
132 | 131 | } |
133 | 132 |
121 | 121 | } |
122 | 122 | String attribute = colourDecorator.getAttributeName(); |
123 | 123 | |
124 | if (TreePane.DEBUG_OUTLINE) { | |
125 | g2.setPaint(Color.red); | |
126 | g2.draw(bounds); | |
127 | } | |
128 | ||
124 | 129 | Font oldFont = g2.getFont(); |
125 | 130 | Paint oldPaint = g2.getPaint(); |
126 | 131 | Stroke oldStroke = g2.getStroke(); |
49 | 49 | */ |
50 | 50 | public class NodeBarController extends AbstractController { |
51 | 51 | |
52 | private static Preferences PREFS = Preferences.userNodeForPackage(NodeBarController.class); | |
52 | private static Preferences PREFS = Preferences.userNodeForPackage(TreeViewer.class); | |
53 | 53 | |
54 | 54 | private static final String NODE_BARS_KEY = "nodeBars"; |
55 | 55 | public static final String DISPLAY_ATTRIBUTE_KEY = "displayAttribute"; |
23 | 23 | import figtree.treeviewer.TreeViewer; |
24 | 24 | import figtree.treeviewer.decorators.Decorator; |
25 | 25 | import jam.controlpalettes.AbstractController; |
26 | import jam.controlpalettes.ControllerListener; | |
27 | 26 | import jam.panels.OptionsPanel; |
28 | 27 | |
29 | 28 | import javax.swing.*; |
30 | 29 | import javax.swing.event.ChangeEvent; |
31 | 30 | import javax.swing.event.ChangeListener; |
31 | import java.awt.*; | |
32 | 32 | import java.awt.event.ActionEvent; |
33 | 33 | import java.awt.event.ActionListener; |
34 | 34 | import java.util.Map; |
48 | 48 | */ |
49 | 49 | public class NodeShapeController extends AbstractController { |
50 | 50 | |
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"; | |
53 | 59 | private static final String NODE_SHAPE_KEY = "nodeShape"; |
54 | 60 | public static final String SHAPE_TYPE_KEY = "shapeType"; |
55 | 61 | public static final String SCALE_TYPE_KEY = "scaleType"; |
58 | 64 | private static final String SHAPE_SIZE_KEY = "size"; |
59 | 65 | private static final String SHAPE_MIN_SIZE_KEY = "minSize"; |
60 | 66 | |
61 | public NodeShapeController(String title, final NodeShapePainter nodeShapePainter, | |
67 | public NodeShapeController(final String title, final NodeType type, final NodeShapePainter nodeShapePainter, | |
62 | 68 | final AttributeColourController colourController, |
63 | 69 | final TreeViewer treeViewer) { |
64 | 70 | this.title = title; |
65 | this.nodeShapePainter = nodeShapePainter; | |
71 | ||
72 | this.type = type; | |
66 | 73 | |
67 | 74 | final float defaultShapeSize = PREFS.getFloat(SHAPE_SIZE_KEY, (float)NodeShapePainter.MAX_SIZE); |
68 | 75 | |
69 | 76 | optionsPanel = new ControllerOptionsPanel(2, 2); |
70 | 77 | |
71 | 78 | titleCheckBox = new JCheckBox(getTitle()); |
72 | titleCheckBox.setSelected(this.nodeShapePainter.isVisible()); | |
79 | titleCheckBox.setSelected(nodeShapePainter.isVisible()); | |
73 | 80 | |
74 | 81 | titleCheckBox.addChangeListener(new ChangeListener() { |
75 | 82 | public void stateChanged(ChangeEvent changeEvent) { |
77 | 84 | nodeShapePainter.setVisible(selected); |
78 | 85 | } |
79 | 86 | }); |
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 | } | |
80 | 116 | |
81 | 117 | shapeTypeCombo = new JComboBox(NodeShapePainter.ShapeType.values()); |
82 | 118 | shapeTypeCombo.addActionListener(new ActionListener() { |
115 | 151 | } |
116 | 152 | }); |
117 | 153 | |
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 | } | |
118 | 181 | |
119 | 182 | final JLabel label1 = optionsPanel.addComponentWithLabel("Shape:", shapeTypeCombo); |
120 | 183 | final JLabel label2 = optionsPanel.addComponentWithLabel("Max size:", shapeSizeSpinner); |
124 | 187 | optionsPanel.addSeparator(); |
125 | 188 | final JLabel label6 = optionsPanel.addComponentWithLabel("Colour by:", colourAttributeCombo); |
126 | 189 | 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); | |
127 | 193 | |
128 | 194 | new AttributeComboHelper(colourAttributeCombo, treeViewer, "User selection").addListener(new AttributeComboHelperListener() { |
129 | 195 | @Override |
161 | 227 | |
162 | 228 | addComponent(label1); |
163 | 229 | addComponent(shapeTypeCombo); |
230 | if (type == NodeType.BOTH) { | |
231 | addComponent(label8); | |
232 | addComponent(externalNodeCheck); | |
233 | addComponent(internalNodeCheck); | |
234 | } | |
164 | 235 | addComponent(label2); |
165 | 236 | addComponent(shapeSizeSpinner); |
166 | 237 | addComponent(label3); |
173 | 244 | addComponent(colourAttributeCombo); |
174 | 245 | addComponent(label7); |
175 | 246 | addComponent(setupColourButton); |
247 | addComponent(label9); | |
248 | addComponent(outlineStrokeCombo); | |
249 | addComponent(label10); | |
250 | addComponent(outlinePaintCombo); | |
176 | 251 | enableComponents(titleCheckBox.isSelected()); |
177 | 252 | |
178 | 253 | titleCheckBox.addChangeListener(new ChangeListener() { |
205 | 280 | } |
206 | 281 | |
207 | 282 | 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)); | |
216 | 297 | } |
217 | 298 | |
218 | 299 | 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()); | |
226 | 312 | } |
227 | 313 | |
228 | 314 | private final JCheckBox titleCheckBox; |
229 | 315 | private final OptionsPanel optionsPanel; |
230 | 316 | |
317 | private final JCheckBox externalNodeCheck; | |
318 | private final JCheckBox internalNodeCheck; | |
231 | 319 | private final JComboBox shapeTypeCombo; |
232 | 320 | private final JComboBox scaleTypeCombo; |
233 | 321 | private final JComboBox sizeAttributeCombo; |
234 | 322 | private final JComboBox colourAttributeCombo; |
235 | 323 | private final JSpinner shapeSizeSpinner; |
236 | 324 | private final JSpinner shapeMinSizeSpinner; |
325 | private final JComboBox outlineStrokeCombo; | |
326 | private final JComboBox outlinePaintCombo; | |
237 | 327 | |
238 | 328 | public String getTitle() { |
239 | 329 | return title; |
240 | 330 | } |
241 | 331 | |
242 | 332 | private final String title; |
243 | private final NodeShapePainter nodeShapePainter; | |
333 | private final NodeType type; | |
244 | 334 | } |
114 | 114 | public void setScaleType(ScaleType scaleType) { |
115 | 115 | this.scaleType = scaleType; |
116 | 116 | 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; | |
117 | 131 | } |
118 | 132 | |
119 | 133 | public double getPreferredWidth() { |
146 | 160 | g2.setPaint(paint); |
147 | 161 | g2.fill(nodeShape); |
148 | 162 | |
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 | } | |
152 | 168 | } |
153 | 169 | |
154 | 170 | /** |
180 | 196 | |
181 | 197 | public void setColourDecorator(Decorator colourDecorator) { |
182 | 198 | 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; | |
183 | 205 | firePainterChanged(); |
184 | 206 | } |
185 | 207 | |
246 | 268 | private ScaleType scaleType = ScaleType.WIDTH; |
247 | 269 | private String sizeAttribute = null; |
248 | 270 | |
271 | private boolean external = true; | |
272 | private boolean internal = true; | |
273 | ||
249 | 274 | private Decorator colourDecorator = null; |
250 | 275 | private ContinuousScale sizeScale = null; |
251 | 276 | |
277 | private float outlineStroke = 0.5f; | |
278 | private Paint outlinePaint = Color.black; | |
279 | ||
252 | 280 | private TreePane treePane; |
253 | 281 | } |
191 | 191 | |
192 | 192 | protected void paintAxis(Graphics2D g2, Rectangle2D axisBounds) |
193 | 193 | { |
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 | } | |
243 | 243 | |
244 | 244 | protected void paintMajorTick(Graphics2D g2, Rectangle2D axisBounds, ScaleAxis axis, double value) |
245 | 245 | { |
250 | 250 | String label = getNumberFormat().format(value); |
251 | 251 | double pos = treePane.scaleOnAxis(value); |
252 | 252 | |
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); | |
254 | 254 | g2.draw(line); |
255 | 255 | |
256 | 256 | g2.setPaint(getForeground()); |
257 | 257 | 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)); | |
259 | 259 | } |
260 | 260 | |
261 | 261 | protected void paintMinorTick(Graphics2D g2, Rectangle2D axisBounds, double value) |
266 | 266 | |
267 | 267 | double pos = treePane.scaleOnAxis(value); |
268 | 268 | |
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); | |
270 | 270 | g2.draw(line); |
271 | 271 | } |
272 | 272 |
313 | 313 | titleCheckBox.setSelected((Boolean)settings.get(SCALE_AXIS_KEY + "." + IS_SHOWN)); |
314 | 314 | reverseAxisCheck.setSelected((Boolean)settings.get(SCALE_AXIS_KEY + "." + REVERSE_AXIS_KEY)); |
315 | 315 | 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)); | |
317 | 317 | majorTicksText.setValue((Double)settings.get(SCALE_AXIS_KEY + "." + MAJOR_TICKS_KEY)); |
318 | 318 | originText.setValue((Double)settings.get(SCALE_AXIS_KEY + "." + ORIGIN_KEY)); |
319 | 319 | autoScaleCheck.setSelected((Boolean)settings.get(SCALE_AXIS_KEY + "." + AUTOMATIC_SCALE_KEY)); |
328 | 328 | settings.put(SCALE_AXIS_KEY + "." + REVERSE_AXIS_KEY, reverseAxisCheck.isSelected()); |
329 | 329 | settings.put(SCALE_AXIS_KEY + "." + SHOW_GRID_KEY, showGridCheck.isSelected()); |
330 | 330 | 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()); | |
332 | 332 | settings.put(SCALE_AXIS_KEY + "." + MAJOR_TICKS_KEY, majorTicksText.getValue()); |
333 | 333 | settings.put(SCALE_AXIS_KEY + "." + ORIGIN_KEY, originText.getValue()); |
334 | 334 | settings.put(SCALE_AXIS_KEY + "." + FONT_SIZE_KEY, fontSizeSpinner.getValue()); |
56 | 56 | preferredWidth = treePane.getTreeBounds().getWidth(); |
57 | 57 | preferredHeight = treePane.getTreeBounds().getHeight(); |
58 | 58 | |
59 | return new Rectangle2D.Double(0.0, 0.0, preferredWidth, preferredHeight); | |
59 | return new Rectangle2D.Double(0.0, 0.0, preferredWidth, preferredHeight); | |
60 | 60 | } |
61 | 61 | |
62 | 62 | public void paint(Graphics2D g2, TreePane treePane, Justification justification, Rectangle2D bounds) { |
65 | 65 | Stroke oldStroke = g2.getStroke(); |
66 | 66 | |
67 | 67 | if (TreePane.DEBUG_OUTLINE) { |
68 | g2.setPaint(Color.red); | |
68 | g2.setPaint(Color.blue); | |
69 | 69 | g2.draw(bounds); |
70 | 70 | } |
71 | 71 |
34 | 34 | */ |
35 | 35 | public abstract class AbstractTreeLayout implements TreeLayout { |
36 | 36 | 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 | } | |
46 | 37 | |
47 | 38 | public double getRootLength() { |
48 | 39 | return rootLength; |
92 | 92 | |
93 | 93 | public Shape getAxisLine(double height) { |
94 | 94 | double x = height; |
95 | if (isAxisReversed()) { | |
96 | x = maxXPosition - x; | |
97 | } | |
98 | 95 | return new Ellipse2D.Double(-x, -x, x * 2.0, x * 2.0); |
99 | 96 | } |
100 | 97 |
84 | 84 | |
85 | 85 | public Shape getAxisLine(double height) { |
86 | 86 | double x = height; |
87 | if (isAxisReversed()) { | |
88 | x = maxXPosition - x; | |
89 | } | |
90 | 87 | double y1 = 0.0; |
91 | 88 | double y2 = 1.0; |
92 | 89 | return new Line2D.Double(x, y1, x, y2); |
94 | 91 | |
95 | 92 | public Shape getHeightArea(double height1, double height2) { |
96 | 93 | double x = height1; |
97 | if (isAxisReversed()) { | |
98 | x = maxXPosition - x; | |
99 | } | |
100 | 94 | double y = 0.0; |
101 | 95 | double w = Math.abs(height2 - height1); |
102 | 96 | double h = 1.0; |