[med-svn] [figtree] 02/08: Imported Upstream version 1.4.3+dfsg

Fabian Klötzl kloetzl-guest at moszumanska.debian.org
Fri Jul 14 12:20:54 UTC 2017


This is an automated email from the git hooks/post-receive script.

kloetzl-guest pushed a commit to branch master
in repository figtree.

commit 5918ea4d167053cde08211f72f9136d370edfd5a
Author: Fabian Klötzl <fabian at kloetzl.info>
Date:   Fri Jul 14 12:54:23 2017 +0200

    Imported Upstream version 1.4.3+dfsg
---
 .gitignore                                         |   9 +
 README.md                                          |   4 +
 build.xml                                          |  64 ++-
 lib/libquaqua64.dylib                              | Bin 0 -> 90576 bytes
 release/common/README.txt                          |  50 +-
 src/figtree/application/FigTreeApplication.java    |  63 +--
 src/figtree/application/FigTreeFrame.java          | 134 ++---
 src/figtree/application/FigTreePDF.java            | 205 --------
 src/figtree/application/FigTreePanel.java          |  40 +-
 src/figtree/application/JSONTreeExporter.java      |   3 +-
 src/figtree/panel/FigTreePanel.java                |   7 +-
 src/figtree/panel/LabelPainterController.java      |  17 +-
 src/figtree/panel/SimpleLabelPainter.java          | 544 ++++++++++-----------
 src/figtree/panel/TreeAppearanceController.java    |  35 +-
 src/figtree/panel/TreesController.java             | 124 +++--
 src/figtree/treeviewer/DefaultTreeViewer.java      |  23 +
 src/figtree/treeviewer/ScaleAxis.java              |   4 +
 src/figtree/treeviewer/TimeScale.java              |  13 +-
 src/figtree/treeviewer/TreePane.java               | 332 ++++++++-----
 src/figtree/treeviewer/TreePaneRollOver.java       |  31 +-
 .../treeviewer/decorators/ContinuousScale.java     |  67 ++-
 src/figtree/treeviewer/painters/LegendPainter.java |   5 +
 .../treeviewer/painters/NodeBarController.java     |   2 +-
 .../treeviewer/painters/NodeShapeController.java   | 132 ++++-
 .../treeviewer/painters/NodeShapePainter.java      |  34 +-
 .../treeviewer/painters/ScaleAxisPainter.java      |  76 +--
 .../painters/ScaleAxisPainterController.java       |   4 +-
 .../treeviewer/painters/ScaleGridPainter.java      |   4 +-
 .../treeviewer/treelayouts/AbstractTreeLayout.java |   9 -
 .../treeviewer/treelayouts/PolarTreeLayout.java    |   3 -
 .../treelayouts/RectilinearTreeLayout.java         |   6 -
 src/figtree/treeviewer/treelayouts/TreeLayout.java |   5 -
 32 files changed, 1116 insertions(+), 933 deletions(-)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e9825d0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+
+*.class
+build/*
+classes/*
+dist/*
+out/*
+FigTree*
+tests/*
+examples/*
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0b6c937
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+_FigTree_
+
+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.
+
diff --git a/build.xml b/build.xml
index a3ae8db..636060e 100755
--- a/build.xml
+++ b/build.xml
@@ -12,6 +12,7 @@
 
     <property name="lib" location="lib"/>
     <property name="dist" location="dist"/>
+    <property name="packaging_tools" value="packaging_tools" />
 
     <property environment="env"/>
 
@@ -60,17 +61,27 @@
             </manifest>
         </jar>
 
+        <jar jarfile="${dist}/figtreepanel.jar">
+            <fileset dir="${build}"
+                     includes="figtree/panel/**/*.class,figtree/treeviewer/**/*.class,**/*.properties,**/*.png,**/*.gif"
+                    />
+
+            <manifest>
+                <attribute name="Built-By" value="${user.name}"/>
+            </manifest>
+        </jar>
+
         <jar jarfile="${dist}/figtree-pdf.jar">
-            <zipgroupfileset dir="${lib}" includes="jebl.jar"/>
-            <zipgroupfileset dir="${lib}" includes="jam.jar"/>
-            <zipgroupfileset dir="${lib}" includes="iText.jar"/>
-            <fileset dir="${build}" includes="**/*.class,**/*.properties,**/*.png,**/*.gif"/>
+            <fileset dir="${build}"
+                     includes="figtree/panel/**/*.class,figtree/treeviewer/**/*.class,**/*.properties,**/*.png,**/*.gif"
+                    />
+
             <manifest>
                 <attribute name="Built-By" value="${user.name}"/>
-                <attribute name="Main-Class" value="figtree.application.FigTreePDF"/>
             </manifest>
         </jar>
 
+
         <war destfile="${dist}/figtree.war"
              webxml="WebRoot/WEB-INF/web.xml">
             <fileset dir="WebRoot"/>
@@ -91,7 +102,8 @@
 
     </target>
 
-    <property name="version" value="1.4.2" />
+    <property name="version" value="1.4.3" />
+    <property name="version_number" value="1.4.3" />
     <property name="release_dir" value="release" />
     <property name="name" value="FigTree" />
 
@@ -101,7 +113,6 @@
     <property name="Linux_dir" value="${release_dir}/Linux" />
     <property name="Windows_dir" value="${release_dir}/Windows" />
 
-    <property name="Mac_package_dir_SL" value="${Mac_dir}/${name} v${version}" />
     <property name="Mac_package_dir" value="${Mac_dir}/${name} v${version}" />
     <property name="Linux_package_dir" value="${Linux_dir}/${name}_v${version}" />
     <property name="Windows_package_dir" value="${Windows_dir}/${name} v${version}" />
@@ -121,9 +132,11 @@
                  classpath="${launch4j.dir}/launch4j.jar :${launch4j.dir}/lib/xstream.jar" />
 
         <copy file="${dist}/figtree.jar" todir="${Windows_package_dir}/lib"/>
+        <!--
         <copy todir="${Windows_package_dir}/lib">
             <fileset dir="${Windows_dir}/lib"/>
         </copy>
+        -->
         <copy todir="${Windows_package_dir}">
             <fileset dir="${common_dir}/"/>
         </copy>
@@ -131,9 +144,9 @@
         <launch4j configFile="${Windows_dir}/FigTree_launch4j.xml"
                   jar="${dist}/figtree.jar"
                   outfile="${Windows_package_dir}/${name} v${version}.exe"
-                  fileVersion="${version}.0"
+                  fileVersion="${version_number}.0"
                   txtFileVersion="${version}"
-                  productVersion="${version}.0"
+                  productVersion="${version_number}.0"
                   txtProductVersion="${version}"
                 />
 
@@ -156,9 +169,11 @@
 
         <copy file="${Linux_dir}/icons/figtree.png" todir="${Linux_package_dir}/images"/>
         <copy file="${dist}/figtree.jar" todir="${Linux_package_dir}/lib"/>
+        <!--
         <copy todir="${Linux_package_dir}/lib">
             <fileset dir="${Linux_dir}/lib"/>
         </copy>
+        -->
         <copy todir="${Linux_package_dir}">
             <fileset dir="${common_dir}/"/>
         </copy>
@@ -170,33 +185,36 @@
         <echo message="Linux/Unix version release is finished." />
     </target>
 
-    <target name="mac_sl_release"
-            description="release Mac Snow Leopard version of FigTree">
-        <delete dir="${Mac_package_dir_SL}" />
+    <target name="mac_release"
+            description="release Mac version of FigTree">
+        <delete dir="${Mac_package_dir}" />
         <!-- Create the release directory -->
-        <mkdir dir="${Mac_package_dir_SL}" />
+        <mkdir dir="${Mac_package_dir}" />
 
-        <copy file="${dist}/figtree.jar" todir="${Mac_package_dir_SL}/lib"/>
-        <copy file="${dist}/figtree-pdf.jar" todir="${Mac_package_dir_SL}/QuickLook Plugin/FigTreeQuickLookPlugin.qlgenerator/Contents/Resources"/>
-        <copy todir="${Mac_package_dir_SL}">
+        <copy file="${dist}/figtree.jar" todir="${Mac_package_dir}/lib"/>
+        <copy file="${dist}/figtree-pdf.jar" todir="${Mac_package_dir}/QuickLook Plugin/FigTreeQuickLookPlugin.qlgenerator/Contents/Resources"/>
+        <copy todir="${Mac_package_dir}">
             <fileset dir="${common_dir}/"/>
         </copy>
-        <copy todir="${Mac_package_dir_SL}/QuickLook Plugin">
+        <copy todir="${Mac_package_dir}/QuickLook Plugin">
             <fileset dir="${Mac_dir}/QuickLook Plugin"/>
         </copy>
 
         <taskdef name="jarbundler" classname="net.sourceforge.jarbundler.JarBundler"/>
 
         <!-- create a jar bundle for the mac -->
-        <jarbundler dir="${Mac_package_dir_SL}"
+        <jarbundler dir="${Mac_package_dir}"
                     name="${name} v${version}"
                     mainclass="figtree.application.FigTreeApplication"
                     icon="${Mac_dir}/icons/FigTree.icns"
+                    stubfile="${packaging_tools}/mac/universalJavaApplicationStub"
+					useJavaXKey="true"
                     jvmversion="1.6+"
                     vmoptions="-Xmx1024M"
                     arguments=""
-                    version="${version} SL"
-                    infostring="${name} v${version} SL, Copyright 2006-2014, Andrew Rambaut"
+                    version="${version}"
+                    build="1"
+                    copyright="${name} v${version}, Copyright 2006-2015, Andrew Rambaut"
                     bundleid="figtree" >
             <javaproperty name="apple.laf.useScreenMenuBar" value="true"/>
             <jarfileset dir="${dist}">
@@ -215,13 +233,15 @@
         </jarbundler>
 
         <!-- remove code signing -->
+        <!--
         <exec executable="/usr/bin/codesign">
             <arg value="-s"/>
             <arg value="-"/>
-            <arg value="--force"/>
+            <arg value="- -force"/> remove space from between minus signs
             <arg value="${Mac_dir}/${name} v${version}/${name} v${version}.app"/>
         </exec>
-
+		-->
+		
         <echo message="Building disk image." />
 
         <!-- create disk image -->
diff --git a/lib/libquaqua64.dylib b/lib/libquaqua64.dylib
new file mode 100644
index 0000000..4bd3c39
Binary files /dev/null and b/lib/libquaqua64.dylib differ
diff --git a/release/common/README.txt b/release/common/README.txt
index 9dce2a7..b3e9d69 100644
--- a/release/common/README.txt
+++ b/release/common/README.txt
@@ -1,4 +1,4 @@
-                  FigTree v1.4 2006-2012
+                  FigTree v1.4.3 2006-2016
                         Andrew Rambaut
 
               Institute of Evolutionary Biology
@@ -7,7 +7,7 @@
 
 
 UNIX/Linux/Mac OS X (command-line) version README
-Last updated: a.rambaut at ed.ac.uk - 8th October 2012
+Last updated: a.rambaut at ed.ac.uk - 4th October 2016
 
 Contents:
 1) INTRODUCTION
@@ -27,6 +27,52 @@ FigTree is designed as a graphical viewer of phylogenetic trees and as a program
 ___________________________________________________________________________
 2) VERSION HISTORY
 
+v1.4.3 Released 4th October 2016
+
+New features:
+	Node shape option can now show shapes for internal or external nodes or both.
+	Copying selected taxon labels when these are selected, subtree when branches are selected.
+	Selecting 'reverse axis' should automatically reverse the Time Scale scale factor. Previously the user needed to set this to -1.0.
+	When searching for text, scrolls to show highlighted tip. 
+	
+Bugs fixed:
+	Issue 102:	Large SVG files from figtree are broken
+	Issue 95: 	Changing the origin value for the Scale Axis does not work
+	Issue 94:	The trait legend overlaps the tree.
+	Issue 93:	Command-line PDF/SVG export options not working 
+	Issue 92:	Reading a file with a mix of integer and real node labels causes exception. 
+	Issue 90:	Export picture cuts the top of the higest tip label
+	Issue 79:	Export of .SVG produces corrupt files 
+
+v1.4.2 Released 9th July 2014
+
+New features:
+	New -url command line option allows reading of trees from URLs in pipelines.
+	
+Bugs fixed:
+	Issue 76:	Scale axis should only show as many decimal places as necessary.
+	Issue 75:	Export PNG & JPEG produce blank images.
+	Issue 64:	Putting node bars on translates the tree to the right (now really fixed, I think).
+
+v1.4.1 Released 14th June 2014
+
+New features:
+	Copy selected subtrees to clipboard as NEXUS format.
+	
+	New graphics export options (PDF, SVG, PNG & JPEG).
+	
+	Control panel now scrolls and can be resized.
+	
+Bugs fixed:
+	Issue 23:	Find bar opens slowly with big trees.
+	Issue 28:	Filtering should work on currently display labels.
+	Issue 53:	Option Tip Labels: "Colour By" does not render Names in colour.
+	Issue 57:	Midpoint rooting not working correctly.
+	Issue 59:	Clear Highlighting/Cartoon etc doesn't seem to work on individual branches.
+	Issue 62:	When all clades are 'collapsed', the top triangle is clipped.
+	Issue 64:	Putting node bars on translates the tree to the right.
+	Issue 69:	Import annotation causes crash.
+
 v1.4 Released 8th October 2012.
 
 New Features:
diff --git a/src/figtree/application/FigTreeApplication.java b/src/figtree/application/FigTreeApplication.java
index 495267c..6e9684e 100755
--- a/src/figtree/application/FigTreeApplication.java
+++ b/src/figtree/application/FigTreeApplication.java
@@ -30,6 +30,11 @@
 
 package figtree.application;
 
+import com.itextpdf.text.Document;
+import com.itextpdf.text.DocumentException;
+import com.itextpdf.text.pdf.PdfContentByte;
+import com.itextpdf.text.pdf.PdfTemplate;
+import com.itextpdf.text.pdf.PdfWriter;
 import figtree.application.preferences.*;
 import figtree.treeviewer.ExtendedTreeViewer;
 import jam.framework.*;
@@ -52,6 +57,9 @@ import javax.imageio.ImageIO;
 import javax.swing.*;
 
 import ch.randelshofer.quaqua.QuaquaManager;
+import org.apache.batik.dom.GenericDOMImplementation;
+import org.apache.batik.svggen.SVGGraphics2D;
+import org.w3c.dom.DOMImplementation;
 
 /**
  * Application class for FigTree including main() method for invoking it.
@@ -68,8 +76,8 @@ import ch.randelshofer.quaqua.QuaquaManager;
  */
 public class FigTreeApplication extends MultiDocApplication {
 
-    public static final String VERSION = "1.4.2";
-    public static final String DATES = "2006-2014";
+    public static final String VERSION = "1.4.3";
+    public static final String DATES = "2006-2016";
 
     public static FigTreeApplication application;
 
@@ -164,34 +172,10 @@ public class FigTreeApplication extends MultiDocApplication {
 
             GraphicFormat format = null;
             if (graphicFormat.equals("PDF")) {
-                if (graphicFileName != null) {
-                    System.out.println("Creating PDF graphic: " + graphicFileName);
-                }
                 format = GraphicFormat.PDF;
-//            } else if (graphicFormat.equals("PS")) {
-//                if (graphicFileName != null) {
-//                    System.out.println("Creating PS graphic: " + graphicFileName);
-//                }
-//                g = new PSGraphics2D(stream, new Dimension(width, height));
-//            } else if (graphicFormat.equals("EMF")) {
-//                if (graphicFileName != null) {
-//                    System.out.println("Creating EMF graphic: " + graphicFileName);
-//                }
-//                g = new EMFGraphics2D(stream, new Dimension(width, height));
-//            } else if (graphicFormat.equals("SVG")) {
-//                if (graphicFileName != null) {
-//                    System.out.println("Creating SVG graphic: " + graphicFileName);
-//                }
-//                g = new SVGGraphics2D(stream, new Dimension(width, height));
-//            } else if (graphicFormat.equals("SWF")) {
-//                if (graphicFileName != null) {
-//                    System.out.println("Creating SWF graphic: " + graphicFileName);
-//                }
-//                g = new SWFGraphics2D(stream, new Dimension(width, height));
+            } else if (graphicFormat.equals("SVG")) {
+                format = GraphicFormat.SVG;
             } else if (graphicFormat.equals("GIF")) {
-                if (graphicFileName != null) {
-                    System.out.println("Creating GIF graphic: " + graphicFileName);
-                }
                 format = GraphicFormat.GIF;
             } else if (graphicFormat.equals("PNG")) {
                 format = GraphicFormat.PNG;
@@ -201,17 +185,18 @@ public class FigTreeApplication extends MultiDocApplication {
                 throw new RuntimeException("Unknown graphic format");
             }
 
-            JComponent comp = treeViewer.getContentPane();
-            BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
-            Graphics g = bi.createGraphics();
-            comp.paint(g);
-            g.dispose();
-            ImageIO.write(bi, format.getName(), stream);
+            if (graphicFileName != null) {
+                System.out.println("Creating " + graphicFormat + " graphic: " + graphicFileName);
+            }
+
+            FigTreeFrame.exportGraphics(format, treeViewer.getContentPane(), stream);
 
         } catch(ImportException ie) {
-            throw new RuntimeException("Error writing graphic file: " + ie);
+            throw new RuntimeException("Error writing graphic file: " + ie.getMessage());
         } catch(IOException ioe) {
-            throw new RuntimeException("Error writing graphic file: " + ioe);
+            throw new RuntimeException("Error writing graphic file: " + ioe.getMessage());
+        } catch (DocumentException de) {
+            throw new RuntimeException("Error writing graphic file: " + de.getMessage());
         }
 
     }
@@ -264,10 +249,10 @@ public class FigTreeApplication extends MultiDocApplication {
                 new Arguments.Option[] {
                         new Arguments.StringOption("graphic", new String[] {
                                 "PDF",
-                                // "SVG",
+                                "SVG",
                                 // "SWF", "PS", "EMF",
                                 "PNG",
-                                "GIF",
+                                // "GIF",
                                 "JPEG"
                         }, false, "produce a graphic with the given format"),
                         new Arguments.IntegerOption("width", "the width of the graphic in pixels"),
@@ -405,7 +390,7 @@ public class FigTreeApplication extends MultiDocApplication {
             icon = new ImageIcon(url);
         }
 
-        final String nameString = "FigTree " + VERSION;
+        final String nameString = "FigTree";
         String titleString = "<html>" +
                 "<div style=\"font-family:'Helvetica Neue', Helvetica, Arial, 'Lucida Grande',sans-serif\">" +
                 "<p style=\"font-weight: 100; font-size: 36px\">FigTree</p>" +
diff --git a/src/figtree/application/FigTreeFrame.java b/src/figtree/application/FigTreeFrame.java
index e96d370..0c4b3a7 100755
--- a/src/figtree/application/FigTreeFrame.java
+++ b/src/figtree/application/FigTreeFrame.java
@@ -29,6 +29,7 @@ import com.itextpdf.text.pdf.PdfWriter;
 import figtree.treeviewer.decorators.DiscreteColourDecorator;
 import figtree.treeviewer.decorators.HSBDiscreteColourDecorator;
 import figtree.treeviewer.painters.StatesPainter;
+import jebl.evolution.align.Output;
 import jebl.evolution.alignments.Alignment;
 import jebl.evolution.alignments.BasicAlignment;
 import jebl.evolution.graphs.Node;
@@ -52,6 +53,7 @@ import org.apache.batik.dom.GenericDOMImplementation;
 import org.apache.batik.svggen.SVGGraphics2D;
 import org.apache.batik.svggen.SVGGraphics2DIOException;
 import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Element;
 
 import javax.imageio.ImageIO;
 import javax.swing.*;
@@ -288,7 +290,7 @@ public class FigTreeFrame extends DocumentFrame implements FigTreeFileMenuHandle
                     public void actionPerformed(ActionEvent e){
                         if (treeViewer.isRootingOn() && treeViewer.getRootingType() == TreePane.RootingType.USER_ROOTING) {
                             JOptionPane.showMessageDialog(FigTreeFrame.this, "Cannot switch trees when user rooting option is on.\n" +
-                                    "Turn this option off to switch trees",
+                                            "Turn this option off to switch trees",
                                     "Unable to switch trees",
                                     JOptionPane.ERROR_MESSAGE);
 
@@ -308,7 +310,7 @@ public class FigTreeFrame extends DocumentFrame implements FigTreeFileMenuHandle
                     public void actionPerformed(ActionEvent e){
                         if (treeViewer.isRootingOn() && treeViewer.getRootingType() == TreePane.RootingType.USER_ROOTING) {
                             JOptionPane.showMessageDialog(FigTreeFrame.this, "Cannot switch trees when user rooting option is on.\n" +
-                                    "Turn this option off to switch trees",
+                                            "Turn this option off to switch trees",
                                     "Unable to switch trees",
                                     JOptionPane.ERROR_MESSAGE);
 
@@ -1216,27 +1218,16 @@ public class FigTreeFrame extends DocumentFrame implements FigTreeFileMenuHandle
             File file = new File(dialog.getDirectory(), dialog.getFile());
 
             try {
-                JComponent comp = treeViewer.getContentPane();
-                switch (format) {
-
-                    case PNG:
-                    case GIF:
-                    case BMP:
-                    case JPEG:
-                        exportGraphicsFile(format, comp, file);
-                        break;
-                    case EPS:
-                        throw new UnsupportedOperationException("EPS not handled");
-                    case SVG:
-                        exportSVGFile(comp, file);
-                        break;
-                    case PDF:
-                        exportPDFFile(comp, file);
-                        break;
-                    default:
-                        throw new UnsupportedOperationException("Format not handled: " + format);
+                OutputStream stream = new FileOutputStream(file);
 
-                }
+                exportGraphics(format, treeViewer.getContentPane(), stream);
+
+                stream.flush();
+                stream.close();
+            } catch(DocumentException de) {
+                JOptionPane.showMessageDialog(this, "Error writing PDF file: " + de,
+                        "Export PDF Error",
+                        JOptionPane.ERROR_MESSAGE);
             } catch (IOException ioe) {
                 JOptionPane.showMessageDialog(this, "Error writing tree file: " + ioe.getMessage(),
                         "Export Error",
@@ -1247,7 +1238,30 @@ public class FigTreeFrame extends DocumentFrame implements FigTreeFileMenuHandle
 
     }
 
-    private final void exportGraphicsFile(GraphicFormat format, JComponent component, File file) throws IOException {
+    public final static void exportGraphics(GraphicFormat format, JComponent comp, OutputStream stream) throws IOException, DocumentException {
+        switch (format) {
+
+            case PNG:
+            case GIF:
+            case BMP:
+            case JPEG:
+                exportGraphicsFile(format, comp, stream);
+                break;
+            case EPS:
+                throw new UnsupportedOperationException("EPS not handled");
+            case SVG:
+                exportSVGFile(comp, stream);
+                break;
+            case PDF:
+                exportPDFFile(comp, stream);
+                break;
+            default:
+                throw new UnsupportedOperationException("Format not handled: " + format);
+
+        }
+
+    }
+    private final static void exportGraphicsFile(GraphicFormat format, JComponent component, OutputStream stream) throws IOException {
         int imageType = BufferedImage.TYPE_INT_RGB;
 
         if (format == GraphicFormat.PNG) {
@@ -1263,10 +1277,10 @@ public class FigTreeFrame extends DocumentFrame implements FigTreeFileMenuHandle
         }
         component.paint(g);
         g.dispose();
-        ImageIO.write(bi, format.getName(), file);
+        ImageIO.write(bi, format.getName(), stream);
     }
 
-    private final void exportSVGFile(JComponent component, File file) throws IOException {
+    private final static void exportSVGFile(JComponent component, OutputStream stream) throws IOException {
         // Get a DOMImplementation and create an XML document
         DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();
         org.w3c.dom.Document document = domImpl.createDocument(null, "svg", null);
@@ -1275,50 +1289,46 @@ public class FigTreeFrame extends DocumentFrame implements FigTreeFileMenuHandle
         SVGGraphics2D svgGenerator = new SVGGraphics2D(document);
 
         component.paint(svgGenerator);
+        Element svgRoot = svgGenerator.getRoot();
+        Rectangle2D bounds = component.getBounds();
+        String viewBox = "0 0 " + bounds.getWidth() + " " + bounds.getHeight();
+        svgRoot.setAttributeNS(null, svgGenerator.SVG_VIEW_BOX_ATTRIBUTE, viewBox);
+        svgRoot.setAttributeNS(null, svgGenerator.SVG_WIDTH_ATTRIBUTE, Double.toString(bounds.getWidth()));
+        svgRoot.setAttributeNS(null, svgGenerator.SVG_HEIGHT_ATTRIBUTE, Double.toString(bounds.getHeight()));
 
         // Write svg file
-        OutputStream outputStream = new FileOutputStream(file);
-        Writer out = new OutputStreamWriter(outputStream, "UTF-8");
-        svgGenerator.stream(out, true /* use css */);
-        outputStream.flush();
-        outputStream.close();
+        Writer out = new OutputStreamWriter(stream, "UTF-8");
+        svgGenerator.stream(svgRoot, out, true /* use css */, false /* escaped */);
     }
 
-    public final void exportPDFFile(JComponent component, File file) {
-        Rectangle2D bounds = treeViewer.getContentPane().getBounds();
+    public final static void exportPDFFile(JComponent component, OutputStream stream) throws DocumentException {
+        Rectangle2D bounds = component.getBounds();
         Document document = new Document(new com.itextpdf
                 .text.Rectangle((float)bounds.getWidth(), (float)bounds.getHeight()));
-        try {
-            // step 2
-            PdfWriter writer;
-            writer = PdfWriter.getInstance(document, new FileOutputStream(file));
-            // step 3
-            document.open();
-            // step 4
-            PdfContentByte cb = writer.getDirectContent();
-            PdfTemplate tp = cb.createTemplate((float)bounds.getWidth(), (float)bounds.getHeight());
-            Graphics2D g2d = tp.createGraphics((float)bounds.getWidth(), (float)bounds.getHeight(), new DefaultFontMapper());
-            component.print(g2d);
-            g2d.dispose();
-            cb.addTemplate(tp, 0, 0);
-        }
-        catch(DocumentException de) {
-            JOptionPane.showMessageDialog(this, "Error writing PDF file: " + de,
-                    "Export PDF Error",
-                    JOptionPane.ERROR_MESSAGE);
-        }
-        catch (FileNotFoundException e) {
-            JOptionPane.showMessageDialog(this, "Error writing PDF file: " + e,
-                    "Export PDF Error",
-                    JOptionPane.ERROR_MESSAGE);
-        }
+        // step 2
+        PdfWriter writer;
+        writer = PdfWriter.getInstance(document, stream);
+        // step 3
+        document.open();
+        // step 4
+        PdfContentByte cb = writer.getDirectContent();
+        PdfTemplate tp = cb.createTemplate((float)bounds.getWidth(), (float)bounds.getHeight());
+        Graphics2D g2d = tp.createGraphics((float)bounds.getWidth(), (float)bounds.getHeight(), new DefaultFontMapper());
+        component.print(g2d);
+        g2d.dispose();
+        cb.addTemplate(tp, 0, 0);
+
         document.close();
     }
 
     public void doCopy() {
         StringWriter writer = new StringWriter();
         try {
-            writeTreeFile(writer, ExportTreeDialog.Format.NEXUS, true, false, false, true, true);
+            if (treeViewer.getSelectionMode() == TreePaneSelector.SelectionMode.TAXA) {
+                writeTaxa(writer);
+            } else {
+                writeTreeFile(writer, ExportTreeDialog.Format.NEXUS, true, false, false, true, true);
+            }
         } catch (IOException e) {
             e.printStackTrace();
         }
@@ -1382,6 +1392,14 @@ public class FigTreeFrame extends DocumentFrame implements FigTreeFileMenuHandle
         treeViewer.selectAll();
     }
 
+    protected void writeTaxa(Writer writer) throws IOException {
+        PrintWriter printWriter = new PrintWriter(writer);
+        for (Taxon taxon : treeViewer.getSelectedTaxa()) {
+            printWriter.println(taxon.getName());
+        }
+        writer.close();
+    }
+
     protected void writeTreeFile(Writer writer, ExportTreeDialog.Format format,
                                  boolean writeAllTrees,
                                  boolean writeAsDisplayed,
@@ -1852,4 +1870,4 @@ public class FigTreeFrame extends DocumentFrame implements FigTreeFileMenuHandle
     private AnnotationDialog annotationDialog = null;
     private AnnotationDialog copyAnnotationDialog = null;
     private SelectAnnotationDialog selectAnnotationDialog = null;
-}
\ No newline at end of file
+}
diff --git a/src/figtree/application/FigTreePDF.java b/src/figtree/application/FigTreePDF.java
deleted file mode 100755
index dc9ff76..0000000
--- a/src/figtree/application/FigTreePDF.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * FigTreePDF.java
- *
- * Copyright (C) 2006-2014 Andrew Rambaut
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-/**
- * TracerApp.java
- *
- * Title:			Tracer
- * Description:		An application for analysing MCMC trace files.
- * @author			Andrew Rambaut
- * @author			Alexei Drummond
- * @version			$Id: FigTreeApplication.java,v 1.15 2007/09/10 14:52:02 rambaut Exp $
- */
-
-package figtree.application;
-
-import com.itextpdf.text.Document;
-import com.itextpdf.text.pdf.PdfContentByte;
-import com.itextpdf.text.pdf.PdfTemplate;
-import com.itextpdf.text.pdf.PdfWriter;
-import figtree.treeviewer.ExtendedTreeViewer;
-import jam.controlpalettes.BasicControlPalette;
-import jam.controlpalettes.ControlPalette;
-
-import java.io.*;
-import java.util.*;
-import java.util.List;
-import java.awt.*;
-
-import jebl.evolution.io.ImportException;
-import jebl.evolution.io.NewickImporter;
-import jebl.evolution.trees.Tree;
-
-/**
- * Commandline main() to generate PDF graphics from FigTree trees.
- *
- * @author Andrew Rambaut
- * @version $Id$
- *
- * $HeadURL$
- *
- * $LastChangedBy$
- * $LastChangedDate$
- * $LastChangedRevision$
- */
-public class FigTreePDF {
-
-    public static final String VERSION = "1.4.1";
-    public static final String DATES = "2006-2014";
-
-    static public void createGraphic(int width, int height, String treeFileName, String graphicFileName) {
-
-        try {
-            BufferedReader bufferedReader = new BufferedReader(new FileReader(treeFileName));
-            String line = bufferedReader.readLine();
-            while (line != null && line.length() == 0) {
-                line = bufferedReader.readLine();
-            }
-
-            bufferedReader.close();
-
-            boolean isNexus = (line != null && line.toUpperCase().contains("#NEXUS"));
-
-            Reader reader = new FileReader(treeFileName);
-
-            Map<String, Object> settings = new HashMap<String, Object>();
-
-            ExtendedTreeViewer treeViewer = new ExtendedTreeViewer();
-            ControlPalette controlPalette = new BasicControlPalette(200, BasicControlPalette.DisplayMode.ONLY_ONE_OPEN);
-            FigTreePanel figTreePanel = new FigTreePanel(null, treeViewer, controlPalette);
-
-            // First of all, fully populate the settings map so that
-            // all the settings have defaults
-            controlPalette.getSettings(settings);
-
-            List<Tree> trees = new ArrayList<Tree>();
-
-            if (isNexus) {
-                FigTreeNexusImporter importer = new FigTreeNexusImporter(reader);
-                trees.add(importer.importNextTree());
-
-                // Try to find a figtree block and if found, parse the settings
-                while (true) {
-                    try {
-                        importer.findNextBlock();
-                        if (importer.getNextBlockName().equalsIgnoreCase("FIGTREE")) {
-                            importer.parseFigTreeBlock(settings);
-                        }
-                    } catch (EOFException ex) {
-                        break;
-                    }
-                }
-            } else {
-                NewickImporter importer = new NewickImporter(reader, true);
-                trees.add(importer.importNextTree());
-            }
-
-            if (trees.size() == 0) {
-                throw new ImportException("This file contained no trees.");
-            }
-
-            treeViewer.setTrees(trees);
-
-            controlPalette.setSettings(settings);
-
-            treeViewer.getContentPane().setSize(width, height);
-
-            OutputStream stream;
-            if (graphicFileName != null) {
-                stream = new FileOutputStream(graphicFileName);
-            } else {
-                stream = System.out;
-            }
-
-            Document document = new Document();
-            document.setPageSize(new com.itextpdf.text.Rectangle(width, height));
-            try {
-                PdfWriter writer = PdfWriter.getInstance(document, stream);
-                document.open();
-                PdfContentByte cb = writer.getDirectContent();
-                PdfTemplate tp = cb.createTemplate(width, height);
-                Graphics2D g2 = tp.createGraphics(width, height);
-                tp.setWidth(width);
-                tp.setHeight(height);
-                treeViewer.getContentPane().print(g2);
-                g2.dispose();
-                tp.sanityCheck(); // all the g2 content is written to tp, not cb
-                cb.addTemplate(tp, 0, 0);
-                cb.sanityCheck();
-            } catch (Exception e) {
-                System.err.println(e.getMessage());
-            }
-            document.close();
-
-        } catch(ImportException ie) {
-            throw new RuntimeException("Error writing graphic file: " + ie);
-        } catch(IOException ioe) {
-            throw new RuntimeException("Error writing graphic file: " + ioe);
-        }
-
-    }
-
-    // Main entry point
-    static public void main(String[] args) {
-
-        Arguments arguments = new Arguments(
-                new Arguments.Option[] {
-                        new Arguments.IntegerOption("width", "the width of the graphic in pixels"),
-                        new Arguments.IntegerOption("height", "the height of the graphic in pixels")
-                });
-
-        try {
-            arguments.parseArguments(args);
-        } catch (Arguments.ArgumentException ae) {
-            System.out.println();
-            System.out.println(ae.getMessage());
-            System.out.println();
-            System.exit(1);
-        }
-
-
-        int width = 800;
-        int height = 600;
-
-        if (arguments.hasOption("width")) {
-            width = arguments.getIntegerOption("width");
-        }
-
-        if (arguments.hasOption("height")) {
-            height = arguments.getIntegerOption("height");
-        }
-
-        // command line version...
-        String[] args2 = arguments.getLeftoverArguments();
-
-        if (args2.length == 0) {
-            // no tree file specified
-            System.exit(0);
-        } else if (args2.length == 1) {
-            // no graphic file specified - write to stdout
-            createGraphic(width, height, args2[0], (args2.length > 1 ? args2[1] : null));
-            System.exit(0);
-        } else {
-            createGraphic(width, height, args2[0], (args2.length > 1 ? args2[1] : null));
-            System.exit(0);
-        }
-    }
-}
-
diff --git a/src/figtree/application/FigTreePanel.java b/src/figtree/application/FigTreePanel.java
index fa1a5aa..c04a05c 100755
--- a/src/figtree/application/FigTreePanel.java
+++ b/src/figtree/application/FigTreePanel.java
@@ -48,6 +48,8 @@ public class FigTreePanel extends JPanel {
 
     public final static int CONTROL_PALETTE_WIDTH = 200;
 
+    private final static boolean SEPARATE_NODE_SHAPE_PANELS = true;
+
     public FigTreePanel(JFrame frame, final ExtendedTreeViewer treeViewer, ControlPalette controlPalette) {
 
         this.treeViewer = treeViewer;
@@ -80,12 +82,30 @@ public class FigTreePanel extends JPanel {
         controlPalette.addController(new LabelPainterController("Tip Labels", "tipLabels", tipLabelPainter, frame, attributeColourController, treeViewer));
         treeViewer.setTipLabelPainter(tipLabelPainter);
 
+        // Create a node shape painter and its controller
+        if (SEPARATE_NODE_SHAPE_PANELS) {
+            final NodeShapePainter tipNodeShapePainter = new NodeShapePainter();
+            tipNodeShapePainter.setVisible(false);
+            controlPalette.addController(new NodeShapeController("Tip Shapes", NodeShapeController.NodeType.EXTERNAL, tipNodeShapePainter, attributeColourController, treeViewer));
+            treeViewer.setTipShapePainter(tipNodeShapePainter);
+        }
         // Create a node label painter and its controller
         final BasicLabelPainter nodeLabelPainter = new BasicLabelPainter(BasicLabelPainter.PainterIntent.NODE);
         nodeLabelPainter.setVisible(false);
         controlPalette.addController(new LabelPainterController("Node Labels", "nodeLabels", nodeLabelPainter, frame, attributeColourController, treeViewer));
         treeViewer.setNodeLabelPainter(nodeLabelPainter);
 
+        // Create a node shape painter and its controller
+        final NodeShapePainter nodeShapePainter = new NodeShapePainter();
+        nodeShapePainter.setVisible(false);
+        controlPalette.addController(new NodeShapeController("Node Shapes",
+                (SEPARATE_NODE_SHAPE_PANELS ? NodeShapeController.NodeType.INTERNAL : NodeShapeController.NodeType.BOTH),
+                nodeShapePainter, attributeColourController, treeViewer));
+        if (!SEPARATE_NODE_SHAPE_PANELS) {
+            treeViewer.setTipShapePainter(nodeShapePainter);
+        }
+        treeViewer.setNodeShapePainter(nodeShapePainter);
+
         // Create a node bar painter and its controller
         final NodeBarPainter nodeBarPainter = new NodeBarPainter();
         nodeBarPainter.setForeground(new Color(24, 32, 228, 128));
@@ -93,12 +113,6 @@ public class FigTreePanel extends JPanel {
         controlPalette.addController(new NodeBarController("Node Bars", nodeBarPainter, treeViewer));
         treeViewer.setNodeBarPainter(nodeBarPainter);
 
-        // Create a node shape painter and its controller
-        final NodeShapePainter nodeShapePainter = new NodeShapePainter();
-        nodeShapePainter.setVisible(false);
-        controlPalette.addController(new NodeShapeController("Node Shapes", nodeShapePainter, attributeColourController, treeViewer));
-        treeViewer.setNodeShapePainter(nodeShapePainter);
-
         // Create a branch label painter and its controller
         final BasicLabelPainter branchLabelPainter = new BasicLabelPainter(BasicLabelPainter.PainterIntent.BRANCH);
         branchLabelPainter.setVisible(false);
@@ -193,17 +207,17 @@ public class FigTreePanel extends JPanel {
         if (utilityPanel == null) {
             return;
         }
-		slideOpenPanel.showUtilityPanel(utilityPanel);
+        slideOpenPanel.showUtilityPanel(utilityPanel);
     }
 
-	public void hideUtilityPanel() {
-		slideOpenPanel.hideUtilityPanel();
-	}
+    public void hideUtilityPanel() {
+        slideOpenPanel.hideUtilityPanel();
+    }
 
 
-	public JPanel getUtilityPanel() {
-		return slideOpenPanel.getUtilityPanel();
-	}
+    public JPanel getUtilityPanel() {
+        return slideOpenPanel.getUtilityPanel();
+    }
 
     public void toggleMidpointRoot() {
         treesController.toggleMidpointRoot();
diff --git a/src/figtree/application/JSONTreeExporter.java b/src/figtree/application/JSONTreeExporter.java
index 5f9f080..5b8c560 100755
--- a/src/figtree/application/JSONTreeExporter.java
+++ b/src/figtree/application/JSONTreeExporter.java
@@ -56,7 +56,8 @@ import java.util.*;
 public class JSONTreeExporter implements TreeExporter {
     public static final String treeNameAttributeKey = "name";
 
-    public final static Set<String> ATTRIBUTE_NAMES = new TreeSet<String>(Arrays.asList(new String[] { "location", "host", "Hx", "Nx", "posterior" }));
+    public final static Set<String> ATTRIBUTE_NAMES = new TreeSet<String>(Arrays.asList(new String[] {
+            "location", "host", "Hx", "Nx", "posterior", "country", "region" }));
     public final static String ORIGIN = "2013.34520547945";
 
     public JSONTreeExporter(Writer writer) {
diff --git a/src/figtree/panel/FigTreePanel.java b/src/figtree/panel/FigTreePanel.java
index 7ae88ce..aa381e9 100755
--- a/src/figtree/panel/FigTreePanel.java
+++ b/src/figtree/panel/FigTreePanel.java
@@ -121,7 +121,8 @@ public class FigTreePanel extends JPanel {
                         treeViewer,
                         "tipLabels", tipLabelPainter,
                         "nodeLabels", nodeLabelPainter,
-                        "branchLabels", branchLabelPainter));
+                        "branchLabels", branchLabelPainter,
+                        true, true));
 
                 controlPalette1.addController(new LabelPainterController(
                         "tipLabels", tipLabelPainter,
@@ -139,7 +140,7 @@ public class FigTreePanel extends JPanel {
                         "tipLabels", tipLabelPainter,
                         "nodeLabels", nodeLabelPainter,
                         "branchLabels", branchLabelPainter,
-                        true));
+                        true, false));
 
                 controlPalette1.addController(new LabelPainterController(
                         "tipLabels", tipLabelPainter,
@@ -160,7 +161,7 @@ public class FigTreePanel extends JPanel {
                         "tipLabels", tipLabelPainter,
                         "nodeLabels", nodeLabelPainter,
                         "branchLabels", branchLabelPainter,
-                        true));
+                        true, false));
 
                 controlPalette2.addController(new TreeColouringController(treeViewer, "Clustering:"));
                 add(controlPalette2.getPanel(), BorderLayout.NORTH);
diff --git a/src/figtree/panel/LabelPainterController.java b/src/figtree/panel/LabelPainterController.java
index ff79687..29b0e91 100755
--- a/src/figtree/panel/LabelPainterController.java
+++ b/src/figtree/panel/LabelPainterController.java
@@ -22,6 +22,7 @@ package figtree.panel;
 
 import figtree.treeviewer.TreeViewer;
 import figtree.treeviewer.painters.AttributeComboHelper;
+import figtree.treeviewer.painters.AttributeComboHelperListener;
 import jam.controlpalettes.AbstractController;
 import jam.panels.OptionsPanel;
 
@@ -31,7 +32,6 @@ import java.awt.event.ItemListener;
 import java.util.Map;
 
 import figtree.treeviewer.painters.LabelPainter;
-import sun.jvm.hotspot.tools.FinalizerInfo;
 
 /**
  * @author Andrew Rambaut
@@ -48,11 +48,11 @@ public class LabelPainterController extends AbstractController {
     private static final String DISPLAY_ATTRIBUTE_KEY = "displayAttribute";
 
     public LabelPainterController(String tipKey,
-                                  final LabelPainter tipLabelPainter,
+                                  final SimpleLabelPainter tipLabelPainter,
                                   String nodeKey,
-                                  final LabelPainter nodeLabelPainter,
+                                  final SimpleLabelPainter nodeLabelPainter,
                                   String branchKey,
-                                  final LabelPainter branchLabelPainter,
+                                  final SimpleLabelPainter branchLabelPainter,
                                   final TreeViewer treeViewer) {
 
         this.tipKey = tipKey;
@@ -68,17 +68,20 @@ public class LabelPainterController extends AbstractController {
 
     }
 
-    private JComboBox setupComboBox(String title, final LabelPainter labelPainter, final TreeViewer treeViewer) {
+    private JComboBox setupComboBox(String title, final SimpleLabelPainter labelPainter, final TreeViewer treeViewer) {
 //		String[] attributes = labelPainter.getAttributes();
         final JComboBox displayAttributeCombo = new JComboBox();
         displayAttributeCombo.addItem("None");
-        new AttributeComboHelper(displayAttributeCombo, treeViewer, "None");
+        for (String attribute : labelPainter.getAttributes()) {
+            displayAttributeCombo.addItem(attribute);
+        }
+        new AttributeComboHelper(displayAttributeCombo, treeViewer, "None", labelPainter.getIntent());
         optionsPanel.addComponentWithLabel(title, displayAttributeCombo);
 
         displayAttributeCombo.addItemListener(new ItemListener() {
             public void itemStateChanged(ItemEvent itemEvent) {
                 String attribute = (String)displayAttributeCombo.getSelectedItem();
-                if (attribute.equals("none")) {
+                if (attribute == null || attribute.equalsIgnoreCase("none")) {
                     labelPainter.setVisible(false);
                 } else {
                     labelPainter.setDisplayAttribute(attribute);
diff --git a/src/figtree/panel/SimpleLabelPainter.java b/src/figtree/panel/SimpleLabelPainter.java
index 172f775..101562a 100755
--- a/src/figtree/panel/SimpleLabelPainter.java
+++ b/src/figtree/panel/SimpleLabelPainter.java
@@ -50,298 +50,296 @@ import java.util.List;
  */
 public class SimpleLabelPainter extends LabelPainter<Node> {
 
-	public static final String TAXON_NAMES = "Taxon names";
-	public static final String NODE_AGES = "Node ages";
-	public static final String BRANCH_LENGTHS = "Branch lengths";
+    public static final String NAMES = "Names";
+    public static final String NODE_AGES = "Node ages";
+    public static final String BRANCH_LENGTHS = "Branch lengths";
 
-	public SimpleLabelPainter(PainterIntent intent) {
+    public SimpleLabelPainter(PainterIntent intent) {
         super(intent);
 
-		setupAttributes(null);
-
-		if (this.displayAttribute == null) {
-			this.displayAttribute = attributes[0];
-		} else {
-			this.displayAttribute = "";
-		}
-
-	}
-
-	public void setupAttributes(Collection<? extends Tree> trees) {
-
-		List<String> attributeNames = new ArrayList<String>();
-		switch( intent ) {
-			case TIP: {
-				attributeNames.add(TAXON_NAMES);
-				attributeNames.add(NODE_AGES);
-				attributeNames.add(BRANCH_LENGTHS);
-				break;
-			}
-			case NODE: {
-				attributeNames.add(NODE_AGES);
-				attributeNames.add(BRANCH_LENGTHS);
-				break;
-			}
-			case BRANCH: {
-				attributeNames.add(BRANCH_LENGTHS);
-				attributeNames.add(NODE_AGES);
-				break;
-			}
-		}
-
-		if (trees != null) {
-			for (Tree tree : trees) {
-				Set<String> nodeAttributes = new TreeSet<String>();
-				if (intent == PainterIntent.TIP) {
-					for (Node node : tree.getExternalNodes()) {
-						nodeAttributes.addAll(node.getAttributeNames());
-					}
-				} else if (intent == PainterIntent.NODE) {
-					for (Node node : tree.getInternalNodes()) {
-						nodeAttributes.addAll(node.getAttributeNames());
-					}
-				} else {
-					for (Node node : tree.getNodes()) {
-						nodeAttributes.addAll(node.getAttributeNames());
-					}
-				}
-				for (String attributeName : nodeAttributes) {
-					if (!attributeName.startsWith("!")) {
-						attributeNames.add(attributeName);
-					}
-				}
-			}
-		}
-
-		this.attributes = new String[attributeNames.size()];
-		attributeNames.toArray(this.attributes);
-
-		firePainterSettingsChanged();
-	}
-
-	public void setTreePane(TreePane treePane) {
-		this.treePane = treePane;
-	}
-
-	public Decorator getBorderDecorator() {
-		return borderDecorator;
-	}
-
-	public void setBorderDecorator(Decorator borderDecorator) {
-		this.borderDecorator = borderDecorator;
-	}
-
-	public Decorator getTextDecorator() {
-		return textDecorator;
-	}
-
-	public void setTextDecorator(Decorator textDecorator) {
-		this.textDecorator = textDecorator;
-	}
+        setupAttributes(null);
+
+        if (this.displayAttribute == null) {
+            this.displayAttribute = attributes[0];
+        } else {
+            this.displayAttribute = "";
+        }
+
+    }
+
+    public void setupAttributes(Collection<? extends Tree> trees) {
+
+        List<String> attributeNames = new ArrayList<String>();
+        switch (getIntent()) {
+            case TIP: {
+                attributeNames.add(NAMES);
+                attributeNames.add(NODE_AGES);
+                attributeNames.add(BRANCH_LENGTHS);
+                break;
+            }
+            case NODE: {
+                attributeNames.add(NODE_AGES);
+                attributeNames.add(BRANCH_LENGTHS);
+                break;
+            }
+            case BRANCH: {
+                attributeNames.add(BRANCH_LENGTHS);
+                attributeNames.add(NODE_AGES);
+                break;
+            }
+        }
+
+        if (trees != null) {
+            for (Tree tree : trees) {
+                Set<String> nodeAttributes = new TreeSet<String>();
+                if (getIntent() == PainterIntent.TIP) {
+                    for (Node node : tree.getExternalNodes()) {
+                        nodeAttributes.addAll(node.getAttributeNames());
+                    }
+                } else if (getIntent() == PainterIntent.NODE) {
+                    for (Node node : tree.getInternalNodes()) {
+                        nodeAttributes.addAll(node.getAttributeNames());
+                    }
+                } else {
+                    for (Node node : tree.getNodes()) {
+                        nodeAttributes.addAll(node.getAttributeNames());
+                    }
+                }
+                for (String attributeName : nodeAttributes) {
+                    if (!attributeName.startsWith("!")) {
+                        attributeNames.add(attributeName);
+                    }
+                }
+            }
+        }
+
+        this.attributes = new String[attributeNames.size()];
+        attributeNames.toArray(this.attributes);
+
+        firePainterSettingsChanged();
+    }
+
+    public void setTreePane(TreePane treePane) {
+        this.treePane = treePane;
+    }
+
+    public Decorator getBorderDecorator() {
+        return borderDecorator;
+    }
+
+    public void setBorderDecorator(Decorator borderDecorator) {
+        this.borderDecorator = borderDecorator;
+    }
+
+    public Decorator getTextDecorator() {
+        return textDecorator;
+    }
+
+    public void setTextDecorator(Decorator textDecorator) {
+        this.textDecorator = textDecorator;
+    }
 
     public Set<Attributable> getAttributableItems() {
         return null;
     }
 
     public Tree getTree() {
-		return treePane.getTree();
-	}
-
-	protected String getLabel(Tree tree, Node node) {
-		if (displayAttribute.equalsIgnoreCase(TAXON_NAMES)) {
-			Taxon taxon = tree.getTaxon(node);
-			if (taxon != null) {
-				if (textDecorator != null) {
-					textDecorator.setItem(taxon);
-				}
-				return taxon.getName();
-			} else {
-				String name = (String)node.getAttribute("name");
-				if (name != null) {
-					return name;
-				}
-				return "unlabelled";
-			}
-		}
-
-		if ( tree instanceof RootedTree) {
-			final RootedTree rtree = (RootedTree) tree;
-
-			if (textDecorator != null) {
-				textDecorator.setItem(node);
-			}
-
-			if (displayAttribute.equalsIgnoreCase(NODE_AGES) ) {
-				return getNumberFormat().format(rtree.getHeight(node));
-			} else if (displayAttribute.equalsIgnoreCase(BRANCH_LENGTHS) ) {
-				return getNumberFormat().format(rtree.getLength(node));
-			}
-		}
-
-		return formatValue(node.getAttribute(displayAttribute));
-	}
-
-	private String formatValue(Object value) {
-		if (value != null) {
-			if (value instanceof Double) {
-				return getNumberFormat().format(value);
-			} else if (value instanceof Object[]) {
-				Object[] values = (Object[])value;
-
-				if (values.length == 0) return null;
-				if (values.length == 1) return formatValue(values[0]);
-
-				StringBuilder builder = new StringBuilder("[");
-				builder.append(formatValue(values[0]));
-				for (int i = 1; i < values.length; i++) {
-					builder.append(",");
-					builder.append(formatValue(values[i]));
-				}
-				builder.append("]");
-				return builder.toString();
-			}
-			return value.toString();
-		}
-		return null;
-	}
-
-	public Rectangle2D calibrate(Graphics2D g2, Node item) {
-		Tree tree = treePane.getTree();
-
-		String label = getLabel(tree, item);
-
-		final Font oldFont = g2.getFont();
-		if (textDecorator != null) {
-			g2.setFont(textDecorator.getFont(getFont()));
-		} else {
-			g2.setFont(getFont());
-		}
-
-		FontMetrics fm = g2.getFontMetrics();
-		preferredHeight = fm.getHeight();
-		preferredWidth = 0;
-
-		if (label != null) {
-			Rectangle2D rect = fm.getStringBounds(label, g2);
-			preferredWidth = rect.getWidth();
-		}
-
-		yOffset = (float)fm.getAscent();
-
-		g2.setFont(oldFont);
-
-		return new Rectangle2D.Double(0.0, 0.0, preferredWidth, preferredHeight);
-	}
-
-	public double getPreferredWidth() {
-		return preferredWidth;
-	}
-
-	public double getPreferredHeight() {
-		return preferredHeight;
-	}
-
-	public double getHeightBound() {
-		return preferredHeight + yOffset;
-	}
-
-	public void paint(Graphics2D g2, Node item, Justification justification, Rectangle2D bounds) {
-		Tree tree = treePane.getTree();
-
-		if (TreePane.DEBUG_OUTLINE) {
-			g2.setPaint(Color.red);
-			g2.draw(bounds);
-		}
-
-		String label = getLabel(tree, item);
-
-		Font oldFont = g2.getFont();
-
-		Paint backgroundPaint = getBackground();
-		Paint borderPaint = getBorderPaint();
-		Stroke borderStroke = getBorderStroke();
-
-		if (borderDecorator != null) {
-			backgroundPaint = borderDecorator.getPaint(backgroundPaint);
-			borderPaint = borderDecorator.getPaint(borderPaint);
-			borderStroke = borderDecorator.getStroke(borderStroke);
-		}
-
-		if (backgroundPaint != null) {
-			g2.setPaint(backgroundPaint);
-			g2.fill(bounds);
-		}
-
-		if (borderPaint != null && borderStroke != null) {
-			g2.setPaint(borderPaint);
-			g2.setStroke(borderStroke);
-			g2.draw(bounds);
-		}
-
-		if (textDecorator != null) {
-			g2.setPaint(textDecorator.getPaint(getForeground()));
-			g2.setFont(textDecorator.getFont(getFont()));
-		} else {
-			g2.setPaint(getForeground());
-			g2.setFont(getFont());
-		}
-
-		if (label != null) {
-
-			Rectangle2D rect = null;
-			if (justification == Justification.CENTER || justification == Justification.RIGHT)
-				rect = g2.getFontMetrics().getStringBounds(label, g2);
-
-			float xOffset;
-			float y = yOffset + (float) bounds.getY();
-			switch (justification) {
-				case CENTER:
-					xOffset = (float)(-rect.getWidth()/2.0);
-					y = yOffset + (float) rect.getY();
+        return treePane.getTree();
+    }
+
+    protected String getLabel(Tree tree, Node node) {
+        if (displayAttribute.equalsIgnoreCase(NAMES)) {
+            Taxon taxon = tree.getTaxon(node);
+            if (taxon != null) {
+                if (textDecorator != null) {
+                    textDecorator.setItem(taxon);
+                }
+                return taxon.getName();
+            } else {
+                String name = (String)node.getAttribute("name");
+                if (name != null) {
+                    return name;
+                }
+                return "unlabelled";
+            }
+        }
+
+        if ( tree instanceof RootedTree) {
+            final RootedTree rtree = (RootedTree) tree;
+
+            if (textDecorator != null) {
+                textDecorator.setItem(node);
+            }
+
+            if (displayAttribute.equalsIgnoreCase(NODE_AGES) ) {
+                return getNumberFormat().format(rtree.getHeight(node));
+            } else if (displayAttribute.equalsIgnoreCase(BRANCH_LENGTHS) ) {
+                return getNumberFormat().format(rtree.getLength(node));
+            }
+        }
+
+        return formatValue(node.getAttribute(displayAttribute));
+    }
+
+    private String formatValue(Object value) {
+        if (value != null) {
+            if (value instanceof Double) {
+                return getNumberFormat().format(value);
+            } else if (value instanceof Object[]) {
+                Object[] values = (Object[])value;
+
+                if (values.length == 0) return null;
+                if (values.length == 1) return formatValue(values[0]);
+
+                StringBuilder builder = new StringBuilder("[");
+                builder.append(formatValue(values[0]));
+                for (int i = 1; i < values.length; i++) {
+                    builder.append(",");
+                    builder.append(formatValue(values[i]));
+                }
+                builder.append("]");
+                return builder.toString();
+            }
+            return value.toString();
+        }
+        return null;
+    }
+
+    public Rectangle2D calibrate(Graphics2D g2, Node item) {
+        Tree tree = treePane.getTree();
+
+        String label = getLabel(tree, item);
+
+        final Font oldFont = g2.getFont();
+        if (textDecorator != null) {
+            g2.setFont(textDecorator.getFont(getFont()));
+        } else {
+            g2.setFont(getFont());
+        }
+
+        FontMetrics fm = g2.getFontMetrics();
+        preferredHeight = fm.getHeight();
+        preferredWidth = 0;
+
+        if (label != null) {
+            Rectangle2D rect = fm.getStringBounds(label, g2);
+            preferredWidth = rect.getWidth();
+        }
+
+        yOffset = (float)fm.getAscent();
+
+        g2.setFont(oldFont);
+
+        return new Rectangle2D.Double(0.0, 0.0, preferredWidth, preferredHeight);
+    }
+
+    public double getPreferredWidth() {
+        return preferredWidth;
+    }
+
+    public double getPreferredHeight() {
+        return preferredHeight;
+    }
+
+    public double getHeightBound() {
+        return preferredHeight + yOffset;
+    }
+
+    public void paint(Graphics2D g2, Node item, Justification justification, Rectangle2D bounds) {
+        Tree tree = treePane.getTree();
+
+        if (TreePane.DEBUG_OUTLINE) {
+            g2.setPaint(Color.red);
+            g2.draw(bounds);
+        }
+
+        String label = getLabel(tree, item);
+
+        Font oldFont = g2.getFont();
+
+        Paint backgroundPaint = getBackground();
+        Paint borderPaint = getBorderPaint();
+        Stroke borderStroke = getBorderStroke();
+
+        if (borderDecorator != null) {
+            backgroundPaint = borderDecorator.getPaint(backgroundPaint);
+            borderPaint = borderDecorator.getPaint(borderPaint);
+            borderStroke = borderDecorator.getStroke(borderStroke);
+        }
+
+        if (backgroundPaint != null) {
+            g2.setPaint(backgroundPaint);
+            g2.fill(bounds);
+        }
+
+        if (borderPaint != null && borderStroke != null) {
+            g2.setPaint(borderPaint);
+            g2.setStroke(borderStroke);
+            g2.draw(bounds);
+        }
+
+        if (textDecorator != null) {
+            g2.setPaint(textDecorator.getPaint(getForeground()));
+            g2.setFont(textDecorator.getFont(getFont()));
+        } else {
+            g2.setPaint(getForeground());
+            g2.setFont(getFont());
+        }
+
+        if (label != null) {
+
+            Rectangle2D rect = null;
+            if (justification == Justification.CENTER || justification == Justification.RIGHT)
+                rect = g2.getFontMetrics().getStringBounds(label, g2);
+
+            float xOffset;
+            float y = yOffset + (float) bounds.getY();
+            switch (justification) {
+                case CENTER:
+                    xOffset = (float)(-rect.getWidth()/2.0);
+                    y = yOffset + (float) rect.getY();
 //xOffset = (float) (bounds.getX() + (bounds.getWidth() - rect.getWidth()) / 2.0);
-					break;
-				case FLUSH:
-				case LEFT:
-					xOffset = (float) bounds.getX();
-					break;
-				case RIGHT:
-					xOffset = (float) (bounds.getX() + bounds.getWidth() - rect.getWidth());
-					break;
-				default:
-					throw new IllegalArgumentException("Unrecognized alignment enum option");
-			}
-
-			g2.drawString(label, xOffset, y);
-		}
-
-		g2.setFont(oldFont);
-	}
-
-	public String[] getAttributes() {
-		return attributes;
-	}
+                    break;
+                case FLUSH:
+                case LEFT:
+                    xOffset = (float) bounds.getX();
+                    break;
+                case RIGHT:
+                    xOffset = (float) (bounds.getX() + bounds.getWidth() - rect.getWidth());
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unrecognized alignment enum option");
+            }
+
+            g2.drawString(label, xOffset, y);
+        }
+
+        g2.setFont(oldFont);
+    }
+
+    public String[] getAttributes() {
+        return attributes;
+    }
 
     public String getDisplayAttribute() {
         return displayAttribute;
     }
 
     public void setDisplayAttribute(String displayAttribute) {
-		this.displayAttribute = displayAttribute;
-		firePainterChanged();
-	}
-
-    private PainterIntent intent;
+        this.displayAttribute = displayAttribute;
+        firePainterChanged();
+    }
 
-	private double preferredWidth;
-	private double preferredHeight;
-	private float yOffset;
+    private double preferredWidth;
+    private double preferredHeight;
+    private float yOffset;
 
-	protected String displayAttribute;
-	protected String[] attributes;
+    protected String displayAttribute;
+    protected String[] attributes;
 
-	protected TreePane treePane;
+    protected TreePane treePane;
 
-	private Decorator textDecorator = null;
-	private Decorator borderDecorator = null;
+    private Decorator textDecorator = null;
+    private Decorator borderDecorator = null;
 
 }
\ No newline at end of file
diff --git a/src/figtree/panel/TreeAppearanceController.java b/src/figtree/panel/TreeAppearanceController.java
index 38f279c..7f40564 100755
--- a/src/figtree/panel/TreeAppearanceController.java
+++ b/src/figtree/panel/TreeAppearanceController.java
@@ -20,6 +20,7 @@
 
 package figtree.panel;
 
+import jebl.evolution.trees.SortedRootedTree;
 import jebl.evolution.trees.Tree;
 import jebl.evolution.graphs.Node;
 import figtree.treeviewer.TreeViewer;
@@ -94,7 +95,7 @@ public class TreeAppearanceController extends AbstractController {
 	                                final LabelPainter nodeLabelPainter,
 	                                String branchKey,
 	                                final LabelPainter branchLabelPainter) {
-		this(treeViewer, tipKey, tipLabelPainter, nodeKey, nodeLabelPainter, branchKey, branchLabelPainter, true);
+		this(treeViewer, tipKey, tipLabelPainter, nodeKey, nodeLabelPainter, branchKey, branchLabelPainter, true, false);
 	}
 
 	public TreeAppearanceController(final TreeViewer treeViewer,
@@ -104,7 +105,8 @@ public class TreeAppearanceController extends AbstractController {
 	                                final LabelPainter nodeLabelPainter,
 	                                String branchKey,
 	                                final LabelPainter branchLabelPainter,
-	                                boolean hideColouring) {
+	                                boolean hideColouring,
+                                    boolean ordering) {
 		this.treeViewer = treeViewer;
 
 		this.hideColouring = hideColouring;
@@ -247,7 +249,30 @@ public class TreeAppearanceController extends AbstractController {
 				}
 			});
 		}
-	}
+
+        if (ordering) {
+            orderCombo = new JComboBox(new String[]{"Off",
+                    SortedRootedTree.BranchOrdering.INCREASING_NODE_DENSITY.toString(),
+                    SortedRootedTree.BranchOrdering.DECREASING_NODE_DENSITY.toString()});
+            orderCombo.setOpaque(false);
+            orderCombo.setSelectedItem(treeViewer.isOrderBranchesOn() ?
+                    treeViewer.getBranchOrdering().ordinal() + 1 : 0);
+            orderCombo.addItemListener(new ItemListener() {
+                public void itemStateChanged(ItemEvent itemEvent) {
+                    if (orderCombo.getSelectedIndex() == 0) {
+                        treeViewer.setOrderBranchesOn(false);
+                    } else {
+                        treeViewer.setOrderBranchesOn(true);
+                        treeViewer.setBranchOrdering(SortedRootedTree.BranchOrdering.values()[orderCombo.getSelectedIndex() - 1]);
+                    }
+                }
+            });
+
+            optionsPanel.addComponentWithLabel("Order:", orderCombo);
+        } else {
+            orderCombo = null;
+        }
+    }
 
 	private void setupAttributes(Collection<? extends Tree> trees) {
 		Object selected = branchColourAttributeCombo.getSelectedItem();
@@ -361,7 +386,9 @@ public class TreeAppearanceController extends AbstractController {
 	private final JSpinner fontSizeSpinner;
 	private final JSpinner digitsSpinner;
 
-	private final TreeViewer treeViewer;
+    private final JComboBox orderCombo;
+
+    private final TreeViewer treeViewer;
 
 	private final String tipKey;
 	private final String nodeKey;
diff --git a/src/figtree/panel/TreesController.java b/src/figtree/panel/TreesController.java
index f64491d..0577cf8 100755
--- a/src/figtree/panel/TreesController.java
+++ b/src/figtree/panel/TreesController.java
@@ -60,69 +60,87 @@ public class TreesController extends AbstractController {
 
 
     public TreesController(final TreeViewer treeViewer) {
+        this(treeViewer, true, true, true);
+    }
+    public TreesController(final TreeViewer treeViewer,
+                           final boolean rooting,
+                           final boolean ordering,
+                           final boolean transforming) {
         this.treeViewer = treeViewer;
 
         titleLabel = new JLabel(CONTROLLER_TITLE);
 
         optionsPanel = new OptionsPanel();
 
-        rootingCheck = new JCheckBox("Midpoint root");
-        rootingCheck.setOpaque(false);
-        optionsPanel.addComponent(rootingCheck);
-
-        rootingCheck.setSelected(treeViewer.isRootingOn());
-
-        rootingCheck.addActionListener(new ActionListener() {
-            @Override
-            public void actionPerformed(ActionEvent actionEvent) {
-                if (rootingCheck.isSelected()) {
-                    treeViewer.setRootingOn(true);
-                    treeViewer.setRootingType(TreePane.RootingType.MID_POINT);
-                } else {
-                    treeViewer.setRootingOn(false);
-                    treeViewer.setRootingType(TreePane.RootingType.USER_ROOTING);
+        if (rooting) {
+            rootingCheck = new JCheckBox("Midpoint root");
+            rootingCheck.setOpaque(false);
+            optionsPanel.addComponent(rootingCheck);
+
+            rootingCheck.setSelected(treeViewer.isRootingOn());
+
+            rootingCheck.addActionListener(new ActionListener() {
+                @Override
+                public void actionPerformed(ActionEvent actionEvent) {
+                    if (rootingCheck.isSelected()) {
+                        treeViewer.setRootingOn(true);
+                        treeViewer.setRootingType(TreePane.RootingType.MID_POINT);
+                    } else {
+                        treeViewer.setRootingOn(false);
+                        treeViewer.setRootingType(TreePane.RootingType.USER_ROOTING);
+                    }
+
                 }
+            });
+        } else {
+            rootingCheck = null;
+        }
 
-            }
-        });
-
-        orderCombo = new JComboBox(new String[] {"Off",
-                SortedRootedTree.BranchOrdering.INCREASING_NODE_DENSITY.toString(),
-                SortedRootedTree.BranchOrdering.DECREASING_NODE_DENSITY.toString()});
-        orderCombo.setOpaque(false);
-        orderCombo.setSelectedItem(treeViewer.isOrderBranchesOn() ?
-                treeViewer.getBranchOrdering().ordinal() + 1 : 0);
-        orderCombo.addItemListener(new ItemListener() {
-            public void itemStateChanged(ItemEvent itemEvent) {
-                if (orderCombo.getSelectedIndex() == 0) {
-                    treeViewer.setOrderBranchesOn(false);
-                } else {
-                    treeViewer.setOrderBranchesOn(true);
-                    treeViewer.setBranchOrdering(SortedRootedTree.BranchOrdering.values()[orderCombo.getSelectedIndex() - 1]);
+        if (ordering) {
+            orderCombo = new JComboBox(new String[]{"Off",
+                    SortedRootedTree.BranchOrdering.INCREASING_NODE_DENSITY.toString(),
+                    SortedRootedTree.BranchOrdering.DECREASING_NODE_DENSITY.toString()});
+            orderCombo.setOpaque(false);
+            orderCombo.setSelectedItem(treeViewer.isOrderBranchesOn() ?
+                    treeViewer.getBranchOrdering().ordinal() + 1 : 0);
+            orderCombo.addItemListener(new ItemListener() {
+                public void itemStateChanged(ItemEvent itemEvent) {
+                    if (orderCombo.getSelectedIndex() == 0) {
+                        treeViewer.setOrderBranchesOn(false);
+                    } else {
+                        treeViewer.setOrderBranchesOn(true);
+                        treeViewer.setBranchOrdering(SortedRootedTree.BranchOrdering.values()[orderCombo.getSelectedIndex() - 1]);
+                    }
                 }
-            }
-        });
-
-        optionsPanel.addComponentWithLabel("Order:", orderCombo);
-
-        transformCombo = new JComboBox(new String[] {"Off",
-                TransformedRootedTree.Transform.CLADOGRAM.toString(),
-                TransformedRootedTree.Transform.PROPORTIONAL.toString(),
-                TransformedRootedTree.Transform.EQUAL_LENGTHS.toString()});
-        transformCombo.setOpaque(false);
-        transformCombo.setSelectedItem(treeViewer.isOrderBranchesOn() ?
-                treeViewer.getBranchTransform().ordinal() + 1 : 0);
-        transformCombo.addItemListener(new ItemListener() {
-            public void itemStateChanged(ItemEvent itemEvent) {
-                if (transformCombo.getSelectedIndex() == 0) {
-                    treeViewer.setTransformBranchesOn(false);
-                } else {
-                    treeViewer.setTransformBranchesOn(true);
-                    treeViewer.setBranchTransform(TransformedRootedTree.Transform.values()[transformCombo.getSelectedIndex() - 1]);
+            });
+
+            optionsPanel.addComponentWithLabel("Order:", orderCombo);
+        } else {
+            orderCombo = null;
+        }
+
+        if (transforming) {
+            transformCombo = new JComboBox(new String[]{"Off",
+                    TransformedRootedTree.Transform.CLADOGRAM.toString(),
+                    TransformedRootedTree.Transform.PROPORTIONAL.toString(),
+                    TransformedRootedTree.Transform.EQUAL_LENGTHS.toString()});
+            transformCombo.setOpaque(false);
+            transformCombo.setSelectedItem(treeViewer.isOrderBranchesOn() ?
+                    treeViewer.getBranchTransform().ordinal() + 1 : 0);
+            transformCombo.addItemListener(new ItemListener() {
+                public void itemStateChanged(ItemEvent itemEvent) {
+                    if (transformCombo.getSelectedIndex() == 0) {
+                        treeViewer.setTransformBranchesOn(false);
+                    } else {
+                        treeViewer.setTransformBranchesOn(true);
+                        treeViewer.setBranchTransform(TransformedRootedTree.Transform.values()[transformCombo.getSelectedIndex() - 1]);
+                    }
                 }
-            }
-        });
-        optionsPanel.addComponentWithLabel("Transform:", transformCombo);
+            });
+            optionsPanel.addComponentWithLabel("Transform:", transformCombo);
+        } else {
+            transformCombo = null;
+        }
     }
 
     public JComponent getTitleComponent() {
diff --git a/src/figtree/treeviewer/DefaultTreeViewer.java b/src/figtree/treeviewer/DefaultTreeViewer.java
index 1163014..0da8c6b 100755
--- a/src/figtree/treeviewer/DefaultTreeViewer.java
+++ b/src/figtree/treeviewer/DefaultTreeViewer.java
@@ -274,6 +274,10 @@ public class DefaultTreeViewer extends TreeViewer {
         return treePane.getSelectedTips();
     }
 
+    public Set<Taxon> getSelectedTaxa() {
+        return treePane.getSelectedTaxa();
+    }
+
     /**
      * Select taxa with a search string matching the currently displayed attribute
      * @param searchType
@@ -288,6 +292,8 @@ public class DefaultTreeViewer extends TreeViewer {
         }
 
         selectTaxa(attributeName, searchType, searchString, caseSensitive);
+
+        scrollToSelectedTips();
     }
 
     public void selectTaxa(String attributeName, TextSearchType searchType, String searchString, boolean caseSensitive) {
@@ -504,6 +510,14 @@ public class DefaultTreeViewer extends TreeViewer {
         return false;
     }
 
+    public void scrollToSelectedTips() {
+        Set<Node> selectedTips = treePane.getSelectedTips();
+        if (selectedTips.size() > 0) {
+            Point point = treePane.getLocationOfTip(selectedTips.iterator().next());
+            treePane.scrollPointToVisible(point);
+        }
+    }
+
     public void cartoonSelectedNodes() {
         treePane.cartoonSelectedNodes();
         fireTreeSettingsChanged();
@@ -591,6 +605,11 @@ public class DefaultTreeViewer extends TreeViewer {
         treePane.removeTreeSelectionListener(treeSelectionListener);
     }
 
+    public TreePaneSelector.SelectionMode getSelectionMode() {
+        return treePaneSelector.getSelectionMode();
+    }
+
+
     public void setSelectionMode(TreePaneSelector.SelectionMode selectionMode) {
         TreePaneSelector.SelectionMode oldSelectionMode = treePaneSelector.getSelectionMode();
 
@@ -629,6 +648,10 @@ public class DefaultTreeViewer extends TreeViewer {
 //        nodeBarPainter.setupAttributes(trees);
     }
 
+    public void setTipShapePainter(NodeShapePainter tipShapePainter) {
+        treePane.setTipShapePainter(tipShapePainter);
+    }
+
     public void setNodeShapePainter(NodeShapePainter nodeShapePainter) {
         treePane.setNodeShapePainter(nodeShapePainter);
     }
diff --git a/src/figtree/treeviewer/ScaleAxis.java b/src/figtree/treeviewer/ScaleAxis.java
index eb93945..ef53440 100755
--- a/src/figtree/treeviewer/ScaleAxis.java
+++ b/src/figtree/treeviewer/ScaleAxis.java
@@ -666,6 +666,10 @@ public class ScaleAxis {
         if (!isCalibrated)
             calibrate();
 
+        if (minorTick <= 0) {
+            return 0;
+        }
+
         if (majorTickIndex == majorTickCount-1)
             return (int)((maxAxis-maxTick)/minorTick);
         else if (majorTickIndex==-1)
diff --git a/src/figtree/treeviewer/TimeScale.java b/src/figtree/treeviewer/TimeScale.java
index 8816713..6f5642f 100755
--- a/src/figtree/treeviewer/TimeScale.java
+++ b/src/figtree/treeviewer/TimeScale.java
@@ -34,7 +34,9 @@ import jebl.evolution.trees.RootedTree;
  */
 public class TimeScale {
 
-	public TimeScale(double rootAge) {
+    private boolean isReversed = false;
+
+    public TimeScale(double rootAge) {
 		this.rootAge = rootAge;
 		this.scaleFactor = Double.NaN;
 		this.offsetAge = 0.0;
@@ -50,7 +52,7 @@ public class TimeScale {
 		if (Double.isNaN(scaleFactor)) {
 			return rootAge / tree.getHeight(tree.getRootNode());
 		}
-		return scaleFactor;
+		return scaleFactor * (isReversed ? -1.0 : 1.0);
 	}
 
 	public double getAge(double height, RootedTree tree) {
@@ -71,7 +73,12 @@ public class TimeScale {
         return (time / getScaleFactor(tree));
     }
 
-	private final double rootAge;
+    public void setReversed(boolean isReversed) {
+        this.isReversed = isReversed;
+    }
+
+    private final double rootAge;
 	private final double offsetAge;
 	private final double scaleFactor;
+
 }
diff --git a/src/figtree/treeviewer/TreePane.java b/src/figtree/treeviewer/TreePane.java
index 5724c86..aae47a7 100755
--- a/src/figtree/treeviewer/TreePane.java
+++ b/src/figtree/treeviewer/TreePane.java
@@ -33,7 +33,6 @@ import javax.swing.*;
 import java.awt.*;
 import java.awt.geom.*;
 import java.awt.print.*;
-import java.io.IOException;
 import java.util.*;
 import java.util.List;
 
@@ -48,6 +47,8 @@ import java.util.List;
  * $LastChangedRevision$
  */
 public class TreePane extends JComponent implements PainterListener, Printable {
+    public final static boolean DEBUG_OUTLINE = false;
+
     public enum RootingType {
         USER_ROOTING("User Selection"),
         MID_POINT("Midpoint");
@@ -63,8 +64,6 @@ public class TreePane extends JComponent implements PainterListener, Printable {
     }
 
 
-    public final static boolean DEBUG_OUTLINE = false;
-
     public final String CARTOON_ATTRIBUTE_NAME = "!cartoon";
     public final String COLLAPSE_ATTRIBUTE_NAME = "!collapse";
     public final String HILIGHT_ATTRIBUTE_NAME = "!hilight";
@@ -91,12 +90,16 @@ public class TreePane extends JComponent implements PainterListener, Printable {
         }
     }
 
+    private void recalibrate() {
+        calibrated = false;
+    }
+
     private void setupTree() {
         tree = constructTransformedTree(originalTree);
 
         recalculateCollapsedNodes();
 
-        calibrated = false;
+        recalibrate();
         invalidate();
         repaint();
     }
@@ -152,11 +155,11 @@ public class TreePane extends JComponent implements PainterListener, Printable {
 
         treeLayout.addTreeLayoutListener(new TreeLayoutListener() {
             public void treeLayoutChanged() {
-                calibrated = false;
+                recalibrate();
                 repaint();
             }
         });
-        calibrated = false;
+        recalibrate();
         invalidate();
         repaint();
     }
@@ -167,7 +170,8 @@ public class TreePane extends JComponent implements PainterListener, Printable {
 
     public void setTimeScale(TimeScale timeScale) {
         this.timeScale = timeScale;
-        calibrated = false;
+        this.timeScale.setReversed(isAxisReversed());
+        recalibrate();
         repaint();
     }
 
@@ -233,7 +237,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
             }
             node.setAttribute("!rotate", rotate);
 
-            calibrated = false;
+            recalibrate();
             invalidate();
             repaint();
         }
@@ -247,7 +251,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
                 node.removeAttribute("!rotate");
             }
 
-            calibrated = false;
+            recalibrate();
             invalidate();
             repaint();
         }
@@ -300,14 +304,19 @@ public class TreePane extends JComponent implements PainterListener, Printable {
      */
     public double scaleOnAxis(double value) {
         double height = timeScale.getHeight(value, tree);
-        if (treeLayout.isAxisReversed()) {
-            return treeBounds.getX() + treeBounds.getWidth() - (height * treeScale);
+        if (isAxisReversed()) {
+            return (treeBounds.getX() + treeBounds.getWidth()) - (height * treeScale);
         } else {
             return treeBounds.getX() + (height * treeScale);
         }
     }
 
     public Shape getAxisLine(double value) {
+        if (isAxisReversed()) {
+            value = maxTreeHeight - value;
+        } else {
+            value -= rootHeightOffset;
+        }
         double height = timeScale.getHeight(value, tree);
         Shape line = treeLayout.getAxisLine(height);
         if (line != null) {
@@ -327,16 +336,21 @@ public class TreePane extends JComponent implements PainterListener, Printable {
 
     public void setAxisOrigin(double axisOrigin) {
         this.axisOrigin = axisOrigin;
-        calibrated = false;
+        recalibrate();
         repaint();
     }
 
     public void setAxisReversed(final boolean isAxisReversed) {
-        treeLayout.setAxisReversed(isAxisReversed);
-        calibrated = false;
+        this.isAxisReversed = isAxisReversed;
+        this.timeScale.setReversed(isAxisReversed());
+        recalibrate();
         repaint();
     }
 
+    public boolean isAxisReversed() {
+        return isAxisReversed;
+    }
+
     private void setupScaleAxis() {
         double minValue = timeScale.getAge(0.0, tree);
         double maxValue = timeScale.getAge(maxTreeHeight, tree);
@@ -357,7 +371,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
     public void setRootAge(double rootAge) {
         double rootLength = timeScale.getHeight(rootAge, tree) - tree.getHeight(tree.getRootNode());
         treeLayout.setRootLength(rootLength);
-        calibrated = false;
+        recalibrate();
         repaint();
     }
 
@@ -376,28 +390,28 @@ public class TreePane extends JComponent implements PainterListener, Printable {
 
     public void setTickSpacing(double userMajorTickSpacing, double userMinorTickSpacing) {
         scaleAxis.setManualAxis(userMajorTickSpacing, userMinorTickSpacing);
-        calibrated = false;
+        recalibrate();
         repaint();
     }
 
     public void setAutomaticScale() {
         scaleAxis.setAutomatic();
-        calibrated = false;
+        recalibrate();
         repaint();
     }
 
     public void painterChanged() {
-        calibrated = false;
+        recalibrate();
         repaint();
     }
 
     public void painterSettingsChanged() {
-        calibrated = false;
+        recalibrate();
         repaint();
     }
 
     public void attributesChanged() {
-        calibrated = false;
+        recalibrate();
         repaint();
     }
 
@@ -496,7 +510,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
 
     public void setShowingTipCallouts(boolean showingTipCallouts) {
         this.showingTipCallouts = showingTipCallouts;
-        calibrated = false;
+        recalibrate();
         repaint();
     }
 
@@ -720,7 +734,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
                     Object[] values = new Object[] { tipCount, height };
                     node.setAttribute(CARTOON_ATTRIBUTE_NAME, values);
                 }
-                calibrated = false;
+                recalibrate();
                 repaint();
             } else {
                 for (Node child : tree.getChildren(node)) {
@@ -746,7 +760,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
                     Object[] values = new Object[] { tipName, height };
                     node.setAttribute(COLLAPSE_ATTRIBUTE_NAME, values);
                 }
-                calibrated = false;
+                recalibrate();
                 repaint();
             } else {
                 for (Node child : tree.getChildren(node)) {
@@ -769,7 +783,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
                 Object[] values = new Object[] { tipCount, height, color };
                 node.setAttribute(HILIGHT_ATTRIBUTE_NAME, values);
 
-                calibrated = false;
+                recalibrate();
                 repaint();
             } else {
                 for (Node child : tree.getChildren(node)) {
@@ -806,7 +820,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
                     Object[] values = new Object[] { tipCount, height, oldValues[2] };
                     node.setAttribute(HILIGHT_ATTRIBUTE_NAME, values);
                 }
-                calibrated = false;
+                recalibrate();
                 repaint();
             } else {
                 for (Node child : tree.getChildren(node)) {
@@ -828,7 +842,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
                     node.removeAttribute(CARTOON_ATTRIBUTE_NAME);
                 }
             }
-            calibrated = false;
+            recalibrate();
             repaint();
         }
     }
@@ -846,7 +860,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
                 if (node.getAttribute(CARTOON_ATTRIBUTE_NAME) != null) {
                     node.removeAttribute(CARTOON_ATTRIBUTE_NAME);
                 }
-                calibrated = false;
+                recalibrate();
                 repaint();
             } else {
                 for (Node child : tree.getChildren(node)) {
@@ -865,7 +879,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
                     node.removeAttribute(HILIGHT_ATTRIBUTE_NAME);
                 }
             }
-            calibrated = false;
+            recalibrate();
             repaint();
         }
     }
@@ -876,7 +890,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
             if (selectedNodes.size() == 0 || selectedNodes.contains(node)) {
                 if (node.getAttribute(HILIGHT_ATTRIBUTE_NAME) != null) {
                     node.removeAttribute(HILIGHT_ATTRIBUTE_NAME);
-                    calibrated = false;
+                    recalibrate();
                     repaint();
                 }
             }
@@ -977,7 +991,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
         if (this.tipLabelPainter != null) {
             this.tipLabelPainter.addPainterListener(this);
         }
-        calibrated = false;
+        recalibrate();
         repaint();
     }
 
@@ -994,7 +1008,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
         if (this.nodeLabelPainter != null) {
             this.nodeLabelPainter.addPainterListener(this);
         }
-        calibrated = false;
+        recalibrate();
         repaint();
     }
 
@@ -1011,7 +1025,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
         if (this.branchLabelPainter != null) {
             this.branchLabelPainter.addPainterListener(this);
         }
-        calibrated = false;
+        recalibrate();
         repaint();
     }
 
@@ -1028,7 +1042,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
         if (this.nodeBarPainter != null) {
             this.nodeBarPainter.addPainterListener(this);
         }
-        calibrated = false;
+        recalibrate();
         repaint();
     }
 
@@ -1036,6 +1050,22 @@ public class TreePane extends JComponent implements PainterListener, Printable {
         return nodeBarPainter;
     }
 
+    public void setTipShapePainter(NodeShapePainter tipShapePainter) {
+        tipShapePainter.setTreePane(this);
+        if (this.tipShapePainter != null) {
+            this.tipShapePainter.removePainterListener(this);
+        }
+        this.tipShapePainter = tipShapePainter;
+        if (this.tipShapePainter != null) {
+            this.tipShapePainter.addPainterListener(this);
+        }
+        recalibrate();
+        repaint();
+    }
+
+    public NodeShapePainter getTipShapePainter() {
+        return tipShapePainter;
+    }
 
     public void setNodeShapePainter(NodeShapePainter nodeShapePainter) {
         nodeShapePainter.setTreePane(this);
@@ -1046,7 +1076,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
         if (this.nodeShapePainter != null) {
             this.nodeShapePainter.addPainterListener(this);
         }
-        calibrated = false;
+        recalibrate();
         repaint();
     }
 
@@ -1062,7 +1092,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
 
         scalePainters.add(scalePainter);
 
-        calibrated = false;
+        recalibrate();
         repaint();
     }
 
@@ -1073,7 +1103,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
 
         scalePainters.remove(scalePainter);
 
-        calibrated = false;
+        recalibrate();
         repaint();
     }
 
@@ -1086,7 +1116,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
         if (this.scaleGridPainter != null) {
             this.scaleGridPainter.addPainterListener(this);
         }
-        calibrated = false;
+        recalibrate();
         repaint();
     }
 
@@ -1096,7 +1126,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
 
         this.legendPainter = legendPainter;
 
-        calibrated = false;
+        recalibrate();
         repaint();
     }
 
@@ -1110,7 +1140,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
 
     public void setLabelSpacing(float labelSpacing) {
         this.labelXOffset = labelSpacing;
-        calibrated = false;
+        recalibrate();
         repaint();
     }
 
@@ -1121,7 +1151,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
             super.setPreferredSize(dimension);
         }
 
-        calibrated = false;
+        recalibrate();
     }
 
     public double getHeightAt(Graphics2D graphics2D, Point2D point) {
@@ -1135,6 +1165,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
 
     public Node getNodeAt(Graphics2D g2, Point point) {
         Rectangle rect = new Rectangle(point.x - 1, point.y - 1, 3, 3);
+        rect.translate(-insets.left, -insets.top);
 
         for (Node node : tree.getExternalNodes()) {
             Shape taxonLabelBound = tipLabelBounds.get(node);
@@ -1192,6 +1223,14 @@ public class TreePane extends JComponent implements PainterListener, Printable {
         return selectedTips;
     }
 
+    public Set<Taxon> getSelectedTaxa() {
+        Set<Taxon> selectedTaxa = new LinkedHashSet<Taxon>();
+        for (Node node : getSelectedTips()) {
+            selectedTaxa.add(tree.getTaxon(node));
+        }
+        return selectedTaxa;
+    }
+
     public RootedTree getSelectedSubtree() {
         if (selectedNodes.size() == 0 && selectedTips.size() == 0) {
             // nothing selected so return the whole tree
@@ -1277,6 +1316,14 @@ public class TreePane extends JComponent implements PainterListener, Printable {
         this.rulerHeight = rulerHeight;
     }
 
+    public Point getLocationOfTip(Node tip) {
+        if (tip == null) {
+            return new Point(0,0);
+        }
+        Shape path = transform.createTransformedShape(treeLayoutCache.getTipLabelPath(tip));
+        return path.getBounds().getLocation();
+    }
+
     public void scrollPointToVisible(Point point) {
         scrollRectToVisible(new Rectangle(point.x, point.y, 0, 0));
     }
@@ -1317,13 +1364,15 @@ public class TreePane extends JComponent implements PainterListener, Printable {
     public void paint(Graphics graphics) {
         if (tree == null) return;
 
-        graphics.setColor(Color.white);
-        Rectangle r = graphics.getClipBounds();
-        if (r != null) {
-            graphics.fillRect(r.x,  r.y, r.width, r.height);
-        }
-
+//        graphics.setColor(Color.white);
+//        Rectangle r = graphics.getClipBounds();
+//        if (r != null) {
+//            graphics.fillRect(r.x,  r.y, r.width, r.height);
+//        }
+//
         final Graphics2D g2 = (Graphics2D) graphics;
+        g2.translate(insets.left, insets.top);
+
         if (!calibrated) {
             calibrate(g2, getWidth(), getHeight());
         }
@@ -1402,13 +1451,13 @@ public class TreePane extends JComponent implements PainterListener, Printable {
         Graphics2D g2 = (Graphics2D) graphics;
         g2.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
 
-        calibrated = false;
+        recalibrate();
         setDoubleBuffered(false);
 
         drawTree(g2, pageFormat.getImageableWidth(), pageFormat.getImageableHeight());
 
         setDoubleBuffered(true);
-        calibrated = false;
+        recalibrate();
 
         return PAGE_EXISTS;
     }
@@ -1445,6 +1494,14 @@ public class TreePane extends JComponent implements PainterListener, Printable {
             }
         }
 
+        if (scaleGridPainter != null && scaleGridPainter.isVisible()) {
+            Rectangle2D gridBounds = new Rectangle2D.Double(
+                    treeBounds.getX(), 0.0,
+                    treeBounds.getWidth(), treeBounds.getHeight());
+            scaleGridPainter.paint(g2, this, null, gridBounds);
+        }
+
+
         // Paint backgrounds
         if (nodeBackgroundDecorator != null) {
             for (Node node : treeLayoutCache.getNodeAreaMap().keySet() ) {
@@ -1457,7 +1514,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
                     g2.setPaint(background);
                     g2.fill(transNodePath);
 
-//                  Experimental outlining - requires order of drawing to be pre-order 
+//                  Experimental outlining - requires order of drawing to be pre-order
 //                    g2.setStroke(new BasicStroke(8, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
 //                    g2.draw(transNodePath);
                 }
@@ -1496,15 +1553,8 @@ public class TreePane extends JComponent implements PainterListener, Printable {
             }
         }
 
-        if (scaleGridPainter != null && scaleGridPainter.isVisible()) {
-            Rectangle2D gridBounds = new Rectangle2D.Double(
-                    treeBounds.getX(), 0.0,
-                    treeBounds.getWidth(), totalScaleBounds.getY());
-            scaleGridPainter.paint(g2, this, null, gridBounds);
-        }
-
         if (DEBUG_OUTLINE) {
-            g2.setPaint(Color.red);
+            g2.setPaint(Color.blue);
             g2.draw(treeBounds);
         }
 
@@ -1627,6 +1677,14 @@ public class TreePane extends JComponent implements PainterListener, Printable {
             }
         }
 
+        if (tipShapePainter != null && tipShapePainter.isVisible()) {
+            for (Node node : tipPoints.keySet()) {
+                Point2D point = tipPoints.get(node);
+                point = transform.transform(point, null);
+                tipShapePainter.paint(g2, node, point, nodeShapeTransforms.get(node));
+            }
+        }
+
         // Paint tip labels
         if (tipLabelPainter != null && tipLabelPainter.isVisible()) {
 
@@ -1637,8 +1695,9 @@ public class TreePane extends JComponent implements PainterListener, Printable {
                 Painter.Justification tipLabelJustification = tipLabelJustifications.get(node);
                 g2.transform(tipLabelTransform);
 
+                double labelWidth = tipLabelWidths.get(node);
                 tipLabelPainter.paint(g2, node, tipLabelJustification,
-                        new Rectangle2D.Double(0.0, 0.0, tipLabelWidth, tipLabelPainter.getPreferredHeight()));
+                        new Rectangle2D.Double(0.0, 0.0, labelWidth, tipLabelPainter.getPreferredHeight()));
 
                 g2.setTransform(oldTransform);
 
@@ -1681,10 +1740,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
                 final double preferredWidth = branchLabelPainter.getPreferredWidth();
                 final double preferredHeight = branchLabelPainter.getPreferredHeight();
 
-                //Line2D labelPath = treeLayout.getBranchLabelPath(node);
-
                 branchLabelPainter.paint(g2, node, Painter.Justification.CENTER,
-                        //new Rectangle2D.Double(-preferredWidth/2, -preferredHeight, preferredWidth, preferredHeight));
                         new Rectangle2D.Double(0, 0, preferredWidth, preferredHeight));
 
                 g2.setTransform(oldTransform);
@@ -1702,6 +1758,8 @@ public class TreePane extends JComponent implements PainterListener, Printable {
         treeLayout.layout(tree, treeLayoutCache);
 
         maxTreeHeight = tree.getHeight(tree.getRootNode()) + treeLayout.getRootLength();
+        rootHeightOffset = 0.0;
+
 
         // First of all get the bounds for the unscaled tree
         treeBounds = null;
@@ -1755,6 +1813,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
         // bounds on node bars
         if (!isTransformBranchesOn() && nodeBarPainter != null && nodeBarPainter.isVisible()) {
             nodeBars.clear();
+
             // Iterate though the nodes
             for (Node node : tree.getInternalNodes()) {
 
@@ -1764,24 +1823,29 @@ public class TreePane extends JComponent implements PainterListener, Printable {
                     nodeBars.put(node, nodeBarPainter.getNodeBar());
                 }
             }
+
             if (nodeBarPainter.getMaxHeight() > maxTreeHeight) {
+                rootHeightOffset = Math.max(nodeBarPainter.getMaxHeight() - maxTreeHeight, 0.0);
                 maxTreeHeight = nodeBarPainter.getMaxHeight();
             }
         }
 
+
         // totalTreeBounds includes all the stuff which is not in a tree scale (like labels and shapes) but in
         // screen pixel scale. This is added to the treeBounds to make space round the edge.
 
         // add the tree bounds
-        final Rectangle2D totalTreeBounds = treeBounds.getBounds2D(); // (YH) same as (Rectangle2D) treeBounds.clone();
+        final Rectangle2D totalTreeBounds = treeBounds.getBounds2D();
+//        final Rectangle2D totalTreeBounds = new Rectangle2D.Double(0.0, 0.0,treeBounds.getWidth(),treeBounds.getHeight());
+
+        tipLabelWidths.clear();
 
         if (tipLabelPainter != null && tipLabelPainter.isVisible()) {
 
-            tipLabelWidth = 0.0;
-            final double tipLabelHeight = tipLabelPainter.getPreferredHeight();
+//            calculateMaxTipLabelWidth(g2, tree.getRootNode());
 
             // put this in a recursive function to allow for collapsed node labels
-            calibrateTipLabels(g2, tree.getRootNode(), tipLabelHeight, totalTreeBounds);
+            calibrateTipLabels(g2, tree.getRootNode(), totalTreeBounds);
         }
 
         if (nodeLabelPainter != null && nodeLabelPainter.isVisible()) {
@@ -1824,10 +1888,24 @@ public class TreePane extends JComponent implements PainterListener, Printable {
         }
 
         // bounds on nodeShapes
+        if (tipShapePainter != null && tipShapePainter.isVisible()) {
+            tipPoints.clear();
+            // Iterate though the external nodes
+            for (Node node : tree.getExternalNodes()) {
+
+                Rectangle2D shapeBounds = tipShapePainter.calibrate(g2, node);
+                if (shapeBounds != null) {
+                    totalTreeBounds.add(shapeBounds);
+
+                    // just at the centroid in here as the actual shape will be reconstructed when drawing
+                    tipPoints.put(node, new Point2D.Double(shapeBounds.getCenterX(), shapeBounds.getCenterY()));
+                }
+            }
+        }
         if (nodeShapePainter != null && nodeShapePainter.isVisible()) {
             nodePoints.clear();
-            // Iterate though the nodes
-            for (Node node : tree.getNodes()) {
+            // Iterate though the internal nodes
+            for (Node node : tree.getInternalNodes()) {
 
                 Rectangle2D shapeBounds = nodeShapePainter.calibrate(g2, node);
                 if (shapeBounds != null) {
@@ -1842,37 +1920,28 @@ public class TreePane extends JComponent implements PainterListener, Printable {
         // Now rescale the scale axis
         setupScaleAxis();
 
-        totalScaleBounds = new Rectangle2D.Double(0.0, 0.0, 0.0, 0.0);
+        bottomPanelBounds = new Rectangle2D.Double();
         double y = totalTreeBounds.getHeight();
         for (ScalePainter scalePainter : scalePainters) {
             if (scalePainter.isVisible()) {
                 scalePainter.calibrate(g2, this);
                 Rectangle2D sb = new Rectangle2D.Double(
-                        totalTreeBounds.getX(), y,
-                        totalTreeBounds.getWidth(), scalePainter.getPreferredHeight());
+                        treeBounds.getX(), y,
+                        treeBounds.getWidth(), scalePainter.getPreferredHeight());
                 y += sb.getHeight();
-                totalScaleBounds.add(sb);
+                bottomPanelBounds.add(sb);
                 scaleBounds.put(scalePainter, sb);
             }
         }
-        totalTreeBounds.add(totalScaleBounds);
 
+        leftPanelBounds = new Rectangle2D.Double();
         if (legendPainter != null && legendPainter.isVisible()) {
             legendPainter.calibrate(g2, this);
             final double w2 = legendPainter.getPreferredWidth();
-            legendBounds = new Rectangle2D.Double(-w2, 0, w2, treeBounds.getHeight());
-            totalTreeBounds.add(legendBounds);
+            legendBounds = new Rectangle2D.Double(0.0, 0.0, w2, height);
+            leftPanelBounds.add(legendBounds);
         }
 
-//        // translate treeBounds to the inset within totalTreeBounds
-//        treeBounds.setRect(-totalTreeBounds.getX(), -totalTreeBounds.getY(), treeBounds.getWidth(), treeBounds.getHeight());
-//
-//        // translate totalTreeBounds so it is at 0, 0
-//        totalTreeBounds.setRect(0, 0,
-//                totalTreeBounds.getWidth(),
-//                totalTreeBounds.getHeight());
-
-
         final double availableW = width - insets.left - insets.right;
         final double availableH = height - insets.top - insets.bottom;
 
@@ -1913,8 +1982,8 @@ public class TreePane extends JComponent implements PainterListener, Printable {
         // Get the amount of canvas that is going to be taken up by the tree -
         // The rest is taken up by taxon labels which don't scale
 
-        final double w = availableW - xDiff;
-        final double h = availableH - yDiff;
+        final double w = availableW - xDiff - leftPanelBounds.getWidth() - rightPanelBounds.getWidth();
+        final double h = availableH - yDiff - topPanelBounds.getHeight() - bottomPanelBounds.getHeight();
 
         double xScale;
         double yScale;
@@ -1948,9 +2017,8 @@ public class TreePane extends JComponent implements PainterListener, Printable {
             yScale = h / treeBounds.getHeight();
 
             // and set the origin in the top left corner
-            xOffset = - treeBounds.getX() * xScale;
-            yOffset = - treeBounds.getY() * yScale;
-
+            xOffset = -treeBounds.getX() * xScale + (treeBounds.getX() - totalTreeBounds.getX());
+            yOffset = -treeBounds.getY() * yScale + (treeBounds.getY() - totalTreeBounds.getY());
             treeScale = xScale;
         }
 
@@ -1958,7 +2026,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
 
         // Create the overall transform
         transform = new AffineTransform();
-        transform.translate(xOffset + insets.left, yOffset + insets.top);
+        transform.translate(xOffset + leftPanelBounds.getWidth(), yOffset + topPanelBounds.getHeight());
         transform.scale(xScale, yScale);
 
         // Get the bounds for the newly scaled tree
@@ -1993,6 +2061,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
 
                 Rectangle2D shapeBounds = nodeBarPainter.calibrate(g2, node);
                 if (shapeBounds != null) {
+                    shapeBounds = transform.createTransformedShape(shapeBounds).getBounds2D();
                     treeBounds.add(shapeBounds);
                     nodeBars.put(node, nodeBarPainter.getNodeBar());
                 }
@@ -2006,15 +2075,17 @@ public class TreePane extends JComponent implements PainterListener, Printable {
 
         if (tipLabelPainter != null && tipLabelPainter.isVisible()) {
             final double labelHeight = tipLabelPainter.getPreferredHeight();
-            Rectangle2D labelBounds = new Rectangle2D.Double(0.0, 0.0, tipLabelWidth, labelHeight);
 
             // Iterate though the external nodes with tip labels
             for (Node node : treeLayoutCache.getTipLabelPathMap().keySet()) {
                 // Get the line that represents the path for the tip label
                 Line2D tipPath = treeLayoutCache.getTipLabelPath(node);
 
+                final double labelWidth = tipLabelWidths.get(node);
+                Rectangle2D labelBounds = new Rectangle2D.Double(0.0, 0.0, labelWidth, labelHeight);
+
                 // Work out how it is rotated and create a transform that matches that
-                AffineTransform taxonTransform = calculateTransform(transform, tipPath, tipLabelWidth, labelHeight, true);
+                AffineTransform taxonTransform = calculateTransform(transform, tipPath, labelWidth, labelHeight, true);
 
                 // Store the transformed bounds in the map for use when selecting
                 tipLabelBounds.put(node, taxonTransform.createTransformedShape(labelBounds));
@@ -2102,8 +2173,8 @@ public class TreePane extends JComponent implements PainterListener, Printable {
             }
         }
 
+        nodeShapeTransforms.clear();
         if (nodeShapePainter != null && nodeShapePainter.isVisible()) {
-            nodeShapeTransforms.clear();
             // Iterate though the nodes
             for (Node node : nodePoints.keySet()) {
                 Line2D shapePath = getTreeLayoutCache().getNodeShapePath(node);
@@ -2112,8 +2183,18 @@ public class TreePane extends JComponent implements PainterListener, Printable {
                 }
             }
         }
+        if (tipShapePainter != null && tipShapePainter.isVisible()) {
+            // Iterate though the nodes
+            for (Node node : tipPoints.keySet()) {
+                Line2D shapePath = getTreeLayoutCache().getNodeShapePath(node);
+                if (shapePath != null) {
+                    nodeShapeTransforms.put(node, calculateTransform(transform, shapePath));
+                }
+            }
+        }
+
 
-        y = height;
+        y = availableH;
         for (ScalePainter scalePainter : scalePainters) {
             if (scalePainter.isVisible()) {
                 scalePainter.calibrate(g2, this);
@@ -2121,22 +2202,25 @@ public class TreePane extends JComponent implements PainterListener, Printable {
             }
         }
 
-        totalScaleBounds = new Rectangle2D.Double(0, y, treeBounds.getWidth(), 0.0);
+        bottomPanelBounds = new Rectangle2D.Double(0, y, treeBounds.getWidth(), 0.0);
         for (ScalePainter scalePainter : scalePainters) {
             if (scalePainter.isVisible()) {
                 scalePainter.calibrate(g2, this);
                 final double h1 = scalePainter.getPreferredHeight();
                 Rectangle2D sb = new Rectangle2D.Double(treeBounds.getX(), y, treeBounds.getWidth(), h1);
                 y += h1;
-                totalScaleBounds.add(sb);
+                bottomPanelBounds.add(sb);
                 scaleBounds.put(scalePainter, sb);
             }
         }
 
+        leftPanelBounds = new Rectangle2D.Double(0, 0, 0.0, 0.0);
         if (legendPainter != null && legendPainter.isVisible()) {
             legendPainter.calibrate(g2, this);
             final double w2 = legendPainter.getPreferredWidth();
-            legendBounds = new Rectangle2D.Double(0, 0, w2, treeBounds.getHeight());
+            legendBounds = new Rectangle2D.Double(0.0, 0.0, w2, availableH);
+            leftPanelBounds.add(legendBounds);
+
         }
 
         calloutPaths.clear();
@@ -2145,34 +2229,49 @@ public class TreePane extends JComponent implements PainterListener, Printable {
         calibrated = true;
     }
 
-    private void calibrateTipLabels(final Graphics2D g2, final Node node, final double tipLabelHeight, final Rectangle2D totalTreeBounds) {
+//    private void calculateMaxTipLabelWidth(final Graphics2D g2, final Node node) {
+//
+//        if (tree.isExternal(node) || node.getAttribute(COLLAPSE_ATTRIBUTE_NAME) != null) {
+//            tipLabelPainter.calibrate(g2, node);
+//            double labelWidth = tipLabelPainter.getPreferredWidth();
+//            tipLabelWidths.put(node, labelWidth);
+//            maxTipLabelWidth = Math.max(maxTipLabelWidth, labelWidth);
+//        } else {
+//            for (Node child : tree.getChildren(node)) {
+//                calculateMaxTipLabelWidth(g2, child);
+//            }
+//        }
+//    }
+
+    private void calibrateTipLabels(final Graphics2D g2, final Node node, final Rectangle2D totalTreeBounds) {
 
         if (tree.isExternal(node) || node.getAttribute(COLLAPSE_ATTRIBUTE_NAME) != null) {
             tipLabelPainter.calibrate(g2, node);
-            double width = tipLabelPainter.getPreferredWidth();
-            tipLabelWidth = Math.max(tipLabelWidth, width);
+            double labelWidth = tipLabelPainter.getPreferredWidth();
+            double labelHeight = tipLabelPainter.getPreferredHeight();
 
-            Rectangle2D labelBounds = new Rectangle2D.Double(0.0, 0.0, width, tipLabelHeight);
+            tipLabelWidths.put(node, labelWidth);
+            Rectangle2D labelBounds = new Rectangle2D.Double(0.0, 0.0, labelWidth, labelHeight);
 
             // Get the line that represents the path for the taxon label
             Line2D taxonPath = treeLayoutCache.getTipLabelPath(node);
 
             if (taxonPath != null) {
                 // Work out how it is rotated and create a transform that matches that
-                AffineTransform taxonTransform = calculateTransform(null, taxonPath, tipLabelWidth, tipLabelHeight, true);
+                AffineTransform taxonTransform = calculateTransform(null, taxonPath, labelWidth, labelHeight, true);
 
                 // and add the translated bounds to the overall bounds
                 totalTreeBounds.add(taxonTransform.createTransformedShape(labelBounds).getBounds2D());
             }
         } else {
             for (Node child : tree.getChildren(node)) {
-                calibrateTipLabels(g2, child, tipLabelHeight, totalTreeBounds);
+                calibrateTipLabels(g2, child, totalTreeBounds);
             }
         }
     }
 
     private AffineTransform calculateTransform(AffineTransform globalTransform, Line2D line,
-                                               double width, double height, boolean just) {
+                                               double width, double height, boolean justify) {
         final Point2D origin = line.getP1();
         if (globalTransform != null) {
             globalTransform.transform(origin, origin);
@@ -2193,7 +2292,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
         // to shift it by the entire width of the string.
         final double ty = origin.getY() - (height / 2.0);
         double tx = origin.getX();
-        if( just) {
+        if (justify) {
             if (line.getX2() > line.getX1()) {
                 tx += labelXOffset;
             } else {
@@ -2227,22 +2326,22 @@ public class TreePane extends JComponent implements PainterListener, Printable {
 
     // Overridden methods to recalibrate tree when bounds change
     public void setBounds(int x, int y, int width, int height) {
-        calibrated = false;
+        recalibrate();
         super.setBounds(x, y, width, height);
     }
 
     public void setBounds(Rectangle rectangle) {
-        calibrated = false;
+        recalibrate();
         super.setBounds(rectangle);
     }
 
     public void setSize(Dimension dimension) {
-        calibrated = false;
+        recalibrate();
         super.setSize(dimension);
     }
 
     public void setSize(int width, int height) {
-        calibrated = false;
+        recalibrate();
         super.setSize(width, height);
     }
 
@@ -2265,16 +2364,18 @@ public class TreePane extends JComponent implements PainterListener, Printable {
     private Rectangle2D treeBounds = new Rectangle2D.Double();
     private double treeScale;
     private double maxTreeHeight;
+    private double rootHeightOffset;
 
     private ScaleAxis scaleAxis = new ScaleAxis(ScaleAxis.AT_DATA, ScaleAxis.AT_DATA);
     private double axisOrigin = 0.0;
     private TimeScale timeScale = new TimeScale(1.0, 0.0);
+    private boolean isAxisReversed = false;
 
     //private Insets insets = new Insets(0, 0, 0, 0);
     private Insets insets = new Insets(6, 6, 6, 6);
 
     private Set<Node> selectedNodes = new HashSet<Node>();
-    private Set<Node> selectedTips = new HashSet<Node>();
+    private Set<Node> selectedTips = new LinkedHashSet<Node>();
 
     private double rulerHeight = -1.0;
     private Rectangle2D dragRectangle = null;
@@ -2291,18 +2392,18 @@ public class TreePane extends JComponent implements PainterListener, Printable {
 
     private Decorator nodeBackgroundDecorator = null;
 
-    private float labelXOffset = 5.0F;
+    private float labelXOffset = 10.0F;
     private LabelPainter<Node> tipLabelPainter = null;
-    private double tipLabelWidth;
+    //private double maxTipLabelWidth;
     private LabelPainter<Node> nodeLabelPainter = null;
     private LabelPainter<Node> branchLabelPainter = null;
 
     private NodeBarPainter nodeBarPainter = null;
 
     private NodeShapePainter nodeShapePainter = null;
+    private NodeShapePainter tipShapePainter = null;
 
     private List<ScalePainter> scalePainters = new ArrayList<ScalePainter>();
-    private Rectangle2D totalScaleBounds = null;
     private Map<ScalePainter, Rectangle2D> scaleBounds = new HashMap<ScalePainter, Rectangle2D>();
 
     private ScaleGridPainter scaleGridPainter = null;
@@ -2310,6 +2411,11 @@ public class TreePane extends JComponent implements PainterListener, Printable {
     private LegendPainter legendPainter = null;
     private Rectangle2D legendBounds = new Rectangle2D.Double();
 
+    private Rectangle2D topPanelBounds = new Rectangle2D.Double();
+    private Rectangle2D leftPanelBounds = new Rectangle2D.Double();
+    private Rectangle2D bottomPanelBounds = new Rectangle2D.Double();
+    private Rectangle2D rightPanelBounds = new Rectangle2D.Double();
+
 
     private BasicStroke branchLineStroke = new BasicStroke(1.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
     private BasicStroke calloutStroke = new BasicStroke(0.5F, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 1.0f, new float[]{0.5f, 2.0f}, 0.0f);
@@ -2325,6 +2431,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
 
     private Map<Node, AffineTransform> tipLabelTransforms = new HashMap<Node, AffineTransform>();
     private Map<Node, Shape> tipLabelBounds = new HashMap<Node, Shape>();
+    private Map<Node, Double> tipLabelWidths = new HashMap<Node, Double>();
     private Map<Node, Painter.Justification> tipLabelJustifications = new HashMap<Node, Painter.Justification>();
 
     private Map<Node, AffineTransform> nodeLabelTransforms = new HashMap<Node, AffineTransform>();
@@ -2336,6 +2443,7 @@ public class TreePane extends JComponent implements PainterListener, Printable {
     private Map<Node, Painter.Justification> branchLabelJustifications = new HashMap<Node, Painter.Justification>();
 
     private Map<Node, Shape> nodeBars = new HashMap<Node, Shape>();
+    private Map<Node, Point2D> tipPoints = new HashMap<Node, Point2D>();
     private Map<Node, Point2D> nodePoints = new HashMap<Node, Point2D>();
     private Map<Node, AffineTransform> nodeShapeTransforms = new HashMap<Node, AffineTransform>();
 
diff --git a/src/figtree/treeviewer/TreePaneRollOver.java b/src/figtree/treeviewer/TreePaneRollOver.java
index 4f11036..d11d704 100755
--- a/src/figtree/treeviewer/TreePaneRollOver.java
+++ b/src/figtree/treeviewer/TreePaneRollOver.java
@@ -46,8 +46,7 @@ public class TreePaneRollOver extends StatusProvider.Helper implements MouseMoti
 	public TreePaneRollOver(TreePane treePane) {
 		this.treePane = treePane;
 		treePane.addMouseMotionListener(this);
-
-	}
+    }
 
 	public void mouseEntered(MouseEvent mouseEvent) {
 	}
@@ -60,25 +59,29 @@ public class TreePaneRollOver extends StatusProvider.Helper implements MouseMoti
 		if (tree != null) {
 			Node node = treePane.getNodeAt((Graphics2D) treePane.getGraphics(), mouseEvent.getPoint());
 			if (node != null) {
-				StringBuilder sb = new StringBuilder();
-				if (!tree.isExternal(node)) {
-					int n = RootedTreeUtils.getTipCount(tree, node);
-					sb.append("Subtree: ").append(n).append(" tips");
-				} else {
-					sb.append("Tip: \"").append(tree.getTaxon(node).toString()).append("\"");
-				}
-				sb.append(" [height = ").append(formatter.getFormattedValue(tree.getHeight(node)));
-				sb.append(", length = ").append(formatter.getFormattedValue(tree.getLength(node)));
-				sb.append("]");
-				fireStatusChanged(StatusPanel.NORMAL, sb.toString());
+				fireStatusChanged(StatusPanel.NORMAL, getNodeText(tree, node));
 			} else {
-				fireStatusChanged(StatusPanel.NORMAL, " ");
+				fireStatusChanged(StatusPanel.NORMAL, getNodeText(tree, tree.getRootNode()));
 			}
 		} else {
 			fireStatusChanged(StatusPanel.NORMAL, " ");
 		}
 	}
 
+    private String getNodeText(RootedTree tree, Node node) {
+        StringBuilder sb = new StringBuilder();
+        if (!tree.isExternal(node)) {
+            int n = RootedTreeUtils.getTipCount(tree, node);
+            sb.append(tree.isRoot(node) ? "Tree: " : "Subtree: ").append(n).append(" tips");
+        } else {
+            sb.append("Tip: \"").append(tree.getTaxon(node).toString()).append("\"");
+        }
+        sb.append(" [height = ").append(formatter.getFormattedValue(tree.getHeight(node)));
+        sb.append(", length = ").append(formatter.getFormattedValue(tree.getLength(node)));
+        sb.append("]");
+        return sb.toString();
+    }
+
 	public void mouseDragged(MouseEvent mouseEvent) {
 	}
 
diff --git a/src/figtree/treeviewer/decorators/ContinuousScale.java b/src/figtree/treeviewer/decorators/ContinuousScale.java
index 20e91b8..abade31 100755
--- a/src/figtree/treeviewer/decorators/ContinuousScale.java
+++ b/src/figtree/treeviewer/decorators/ContinuousScale.java
@@ -43,7 +43,7 @@ public class ContinuousScale {
      */
     public ContinuousScale(String settings) {
         if (settings.startsWith("{")) {
-           settings = settings.substring(1, settings.length());
+            settings = settings.substring(1, settings.length());
         }
         if (settings.endsWith("}")) {
             settings = settings.substring(0, settings.length() - 1);
@@ -88,47 +88,46 @@ public class ContinuousScale {
 
         // First collect the set of all attribute values
         Set<Object> values = new TreeSet<Object>();
-        for (Attributable item : items) {
-            Object value = item.getAttribute(attributeName);
-            if (value != null) {
-                values.add(value);
-            }
-        }
 
         boolean isNumber = true;
 
         // Find the range of numbers
-        for (Object value : values) {
-            double realValue = -1.0;
-
-            if (value instanceof Boolean) {
-                realValue = ((Boolean)value ? 1 : 0);
-            } else if (value instanceof Number) {
-                realValue = ((Number)value).doubleValue();
-            } else if (value instanceof String) {
-                // it is a string but it could still code for
-                // a boolean, integer or real
-                if (value.toString().equalsIgnoreCase("true")) {
-                    realValue = 1;
-                } else if (value.toString().equalsIgnoreCase("false")) {
-                    realValue = 0;
-                } else {
-                    try {
-                        realValue = Double.parseDouble(value.toString());
-                    } catch(NumberFormatException nfe) {
-                        isNumber = false;
+        for (Attributable item : items) {
+            Object value = item.getAttribute(attributeName);
+            if (value != null) {
+
+                double realValue = -1.0;
+
+                if (value instanceof Boolean) {
+                    realValue = ((Boolean)value ? 1 : 0);
+                } else if (value instanceof Number) {
+                    realValue = ((Number)value).doubleValue();
+                } else if (value instanceof String) {
+                    // it is a string but it could still code for
+                    // a boolean, integer or real
+                    if (value.toString().equalsIgnoreCase("true")) {
+                        realValue = 1;
+                    } else if (value.toString().equalsIgnoreCase("false")) {
+                        realValue = 0;
+                    } else {
+                        try {
+                            realValue = Double.parseDouble(value.toString());
+                        } catch(NumberFormatException nfe) {
+                            isNumber = false;
+                        }
                     }
                 }
-            }
 
-            if (isNumber) {
-                if (realValue < minValue) {
-                    minValue = realValue;
-                }
-                if (realValue > maxValue) {
-                    maxValue = realValue;
-                }
+                if (isNumber) {
+                    if (realValue < minValue) {
+                        minValue = realValue;
+                    }
+                    if (realValue > maxValue) {
+                        maxValue = realValue;
+                    }
 
+                }
+                values.add(realValue);
             }
         }
 
diff --git a/src/figtree/treeviewer/painters/LegendPainter.java b/src/figtree/treeviewer/painters/LegendPainter.java
index b96aacf..8e2b822 100755
--- a/src/figtree/treeviewer/painters/LegendPainter.java
+++ b/src/figtree/treeviewer/painters/LegendPainter.java
@@ -122,6 +122,11 @@ public class LegendPainter extends LabelPainter<TreePane> implements ScalePainte
         }
         String attribute = colourDecorator.getAttributeName();
 
+        if (TreePane.DEBUG_OUTLINE) {
+            g2.setPaint(Color.red);
+            g2.draw(bounds);
+        }
+
         Font oldFont = g2.getFont();
         Paint oldPaint = g2.getPaint();
         Stroke oldStroke = g2.getStroke();
diff --git a/src/figtree/treeviewer/painters/NodeBarController.java b/src/figtree/treeviewer/painters/NodeBarController.java
index ff47690..12e27a5 100755
--- a/src/figtree/treeviewer/painters/NodeBarController.java
+++ b/src/figtree/treeviewer/painters/NodeBarController.java
@@ -50,7 +50,7 @@ import jebl.util.Attributable;
  */
 public class NodeBarController extends AbstractController {
 
-    private static Preferences PREFS = Preferences.userNodeForPackage(NodeBarController.class);
+    private static Preferences PREFS = Preferences.userNodeForPackage(TreeViewer.class);
 
     private static final String NODE_BARS_KEY = "nodeBars";
     public static final String DISPLAY_ATTRIBUTE_KEY = "displayAttribute";
diff --git a/src/figtree/treeviewer/painters/NodeShapeController.java b/src/figtree/treeviewer/painters/NodeShapeController.java
index c86fbbc..7b8103a 100755
--- a/src/figtree/treeviewer/painters/NodeShapeController.java
+++ b/src/figtree/treeviewer/painters/NodeShapeController.java
@@ -24,12 +24,12 @@ import figtree.treeviewer.AttributeColourController;
 import figtree.treeviewer.TreeViewer;
 import figtree.treeviewer.decorators.Decorator;
 import jam.controlpalettes.AbstractController;
-import jam.controlpalettes.ControllerListener;
 import jam.panels.OptionsPanel;
 
 import javax.swing.*;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
+import java.awt.*;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.util.Map;
@@ -49,8 +49,14 @@ import figtree.treeviewer.ControllerOptionsPanel;
  */
 public class NodeShapeController extends AbstractController {
 
-    private static Preferences PREFS = Preferences.userNodeForPackage(NodeBarController.class);
+    public enum NodeType {
+        INTERNAL, EXTERNAL, BOTH
+    }
+
+    private static Preferences PREFS = Preferences.userNodeForPackage(TreeViewer.class);
 
+    private static final String IS_EXTERNAL = "isExternal";
+    private static final String IS_INTERNAL = "isInternal";
     private static final String NODE_SHAPE_KEY = "nodeShape";
     public static final String SHAPE_TYPE_KEY = "shapeType";
     public static final String SCALE_TYPE_KEY = "scaleType";
@@ -59,18 +65,19 @@ public class NodeShapeController extends AbstractController {
     private static final String SHAPE_SIZE_KEY = "size";
     private static final String SHAPE_MIN_SIZE_KEY = "minSize";
 
-    public NodeShapeController(String title, final NodeShapePainter nodeShapePainter,
+    public NodeShapeController(final String title, final NodeType type, final NodeShapePainter nodeShapePainter,
                                final AttributeColourController colourController,
                                final TreeViewer treeViewer) {
         this.title = title;
-        this.nodeShapePainter = nodeShapePainter;
+
+        this.type = type;
 
         final float defaultShapeSize = PREFS.getFloat(SHAPE_SIZE_KEY, (float)NodeShapePainter.MAX_SIZE);
 
         optionsPanel = new ControllerOptionsPanel(2, 2);
 
         titleCheckBox = new JCheckBox(getTitle());
-        titleCheckBox.setSelected(this.nodeShapePainter.isVisible());
+        titleCheckBox.setSelected(nodeShapePainter.isVisible());
 
         titleCheckBox.addChangeListener(new ChangeListener() {
             public void stateChanged(ChangeEvent changeEvent) {
@@ -79,6 +86,35 @@ public class NodeShapeController extends AbstractController {
             }
         });
 
+        final ControllerOptionsPanel nodeCheckPanel;
+        if (type == NodeType.BOTH) {
+            externalNodeCheck = new JCheckBox("external");
+            internalNodeCheck = new JCheckBox("internal");
+            ActionListener listener = new ActionListener() {
+                public void actionPerformed(ActionEvent event) {
+                    nodeShapePainter.setNodeType(externalNodeCheck.isSelected(), internalNodeCheck.isSelected());
+                }
+            };
+            externalNodeCheck.addActionListener(listener);
+            internalNodeCheck.addActionListener(listener);
+            nodeCheckPanel = new ControllerOptionsPanel(2, 2);
+            nodeCheckPanel.setBorder(BorderFactory.createEmptyBorder());
+            nodeCheckPanel.addSpanningComponent(externalNodeCheck);
+            nodeCheckPanel.addSpanningComponent(internalNodeCheck);
+
+            externalNodeCheck.setSelected(nodeShapePainter.isExternal());
+            internalNodeCheck.setSelected(nodeShapePainter.isInternal());
+        } else {
+            nodeCheckPanel = null;
+            externalNodeCheck = null;
+            internalNodeCheck = null;
+            if (type == NodeType.EXTERNAL) {
+                nodeShapePainter.setNodeType(true, false);
+            } else if (type == NodeType.INTERNAL) {
+                nodeShapePainter.setNodeType(false, true);
+            }
+        }
+
         shapeTypeCombo = new JComboBox(NodeShapePainter.ShapeType.values());
         shapeTypeCombo.addActionListener(new ActionListener() {
             public void actionPerformed(ActionEvent event) {
@@ -116,6 +152,33 @@ public class NodeShapeController extends AbstractController {
             }
         });
 
+        outlineStrokeCombo = new JComboBox(new String[] {"None", "0.25", "0.5", "1.0", "2.0", "3.0", "4.0", "5.0"});
+        outlineStrokeCombo.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent event) {
+                nodeShapePainter.setOutline((outlineStrokeCombo.getSelectedIndex() == 0 ? 0.0f :
+                    Float.parseFloat(outlineStrokeCombo.getSelectedItem().toString())),
+                        (Paint)outlinePaintCombo.getSelectedItem()
+                );
+            }
+        });
+
+        final Paint[] outlinePaints = {Color.black, Color.white};
+        outlinePaintCombo = new JComboBox(new String[] {"black", "white"});
+        outlinePaintCombo.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent event) {
+                nodeShapePainter.setOutline((outlineStrokeCombo.getSelectedIndex() == 0 ? 0.0f :
+                                Float.parseFloat(outlineStrokeCombo.getSelectedItem().toString())),
+                        outlinePaints[outlinePaintCombo.getSelectedIndex()]
+                );
+            }
+        });
+
+        final JLabel label8;
+        if (type == NodeType.BOTH) {
+            label8 = optionsPanel.addComponentWithLabel("Show:", nodeCheckPanel);
+        } else {
+            label8 = null;
+        }
 
         final JLabel label1 = optionsPanel.addComponentWithLabel("Shape:", shapeTypeCombo);
         final JLabel label2 = optionsPanel.addComponentWithLabel("Max size:", shapeSizeSpinner);
@@ -126,6 +189,9 @@ public class NodeShapeController extends AbstractController {
         final JLabel label6 = optionsPanel.addComponentWithLabel("Colour by:", colourAttributeCombo);
         final JLabel label7 = optionsPanel.addComponentWithLabel("Setup:", setupColourButton);
 
+        final JLabel label9 = optionsPanel.addComponentWithLabel("Outline width:", outlineStrokeCombo);
+        final JLabel label10 = optionsPanel.addComponentWithLabel("Outline colour:", outlinePaintCombo);
+
         new AttributeComboHelper(colourAttributeCombo, treeViewer, "User selection").addListener(new AttributeComboHelperListener() {
             @Override
             public void attributeComboChanged() {
@@ -162,6 +228,11 @@ public class NodeShapeController extends AbstractController {
 
         addComponent(label1);
         addComponent(shapeTypeCombo);
+        if (type == NodeType.BOTH) {
+            addComponent(label8);
+            addComponent(externalNodeCheck);
+            addComponent(internalNodeCheck);
+        }
         addComponent(label2);
         addComponent(shapeSizeSpinner);
         addComponent(label3);
@@ -174,6 +245,10 @@ public class NodeShapeController extends AbstractController {
         addComponent(colourAttributeCombo);
         addComponent(label7);
         addComponent(setupColourButton);
+        addComponent(label9);
+        addComponent(outlineStrokeCombo);
+        addComponent(label10);
+        addComponent(outlinePaintCombo);
         enableComponents(titleCheckBox.isSelected());
 
         titleCheckBox.addChangeListener(new ChangeListener() {
@@ -206,40 +281,55 @@ public class NodeShapeController extends AbstractController {
     }
 
     public void setSettings(Map<String,Object> settings) {
-        titleCheckBox.setSelected((Boolean)settings.get(NODE_SHAPE_KEY + "." + IS_SHOWN));
-
-        shapeTypeCombo.setSelectedItem((NodeShapePainter.ShapeType.valueOf(settings.get(NODE_SHAPE_KEY + "." + SHAPE_TYPE_KEY).toString().toUpperCase())));
-        scaleTypeCombo.setSelectedItem((NodeShapePainter.ScaleType.valueOf(settings.get(NODE_SHAPE_KEY + "." + SCALE_TYPE_KEY).toString().toUpperCase())));
-        colourAttributeCombo.setSelectedItem((String) settings.get(NODE_SHAPE_KEY + "." + COLOUR_ATTRIBUTE_KEY));
-        sizeAttributeCombo.setSelectedItem((String) settings.get(NODE_SHAPE_KEY + "." + SIZE_ATTRIBUTE_KEY));
-        shapeSizeSpinner.setValue((Double)settings.get(NODE_SHAPE_KEY + "." + SHAPE_SIZE_KEY));
-        shapeMinSizeSpinner.setValue((Double) settings.get(NODE_SHAPE_KEY + "." + SHAPE_MIN_SIZE_KEY));
+        String key = NODE_SHAPE_KEY + (type == NodeType.INTERNAL ? "Internal" : (type == NodeType.EXTERNAL ? "External" : ""));
+
+        titleCheckBox.setSelected((Boolean)settings.get(key + "." + IS_SHOWN));
+
+        if (type == NodeType.BOTH) {
+            externalNodeCheck.setSelected((Boolean) settings.get(key + "." + IS_EXTERNAL));
+            internalNodeCheck.setSelected((Boolean) settings.get(key + "." + IS_INTERNAL));
+        }
+        shapeTypeCombo.setSelectedItem((NodeShapePainter.ShapeType.valueOf(settings.get(key + "." + SHAPE_TYPE_KEY).toString().toUpperCase())));
+        scaleTypeCombo.setSelectedItem((NodeShapePainter.ScaleType.valueOf(settings.get(key + "." + SCALE_TYPE_KEY).toString().toUpperCase())));
+        colourAttributeCombo.setSelectedItem((String) settings.get(key + "." + COLOUR_ATTRIBUTE_KEY));
+        sizeAttributeCombo.setSelectedItem((String) settings.get(key + "." + SIZE_ATTRIBUTE_KEY));
+        shapeSizeSpinner.setValue((Double)settings.get(key + "." + SHAPE_SIZE_KEY));
+        shapeMinSizeSpinner.setValue((Double) settings.get(key + "." + SHAPE_MIN_SIZE_KEY));
     }
 
     public void getSettings(Map<String, Object> settings) {
-        settings.put(NODE_SHAPE_KEY + "." + IS_SHOWN, titleCheckBox.isSelected());
-        settings.put(NODE_SHAPE_KEY + "." + SHAPE_TYPE_KEY, shapeTypeCombo.getSelectedItem());
-        settings.put(NODE_SHAPE_KEY + "." + SCALE_TYPE_KEY, scaleTypeCombo.getSelectedItem());
-        settings.put(NODE_SHAPE_KEY + "." + COLOUR_ATTRIBUTE_KEY, colourAttributeCombo.getSelectedItem());
-        settings.put(NODE_SHAPE_KEY + "." + SIZE_ATTRIBUTE_KEY, sizeAttributeCombo.getSelectedItem());
-        settings.put(NODE_SHAPE_KEY + "." + SHAPE_SIZE_KEY, shapeSizeSpinner.getValue());
-        settings.put(NODE_SHAPE_KEY + "." + SHAPE_MIN_SIZE_KEY, shapeMinSizeSpinner.getValue());
+        String key = NODE_SHAPE_KEY + (type == NodeType.INTERNAL ? "Internal" : (type == NodeType.EXTERNAL ? "External" : ""));
+        settings.put(key + "." + IS_SHOWN, titleCheckBox.isSelected());
+        if (type == NodeType.BOTH) {
+            settings.put(key + "." + IS_EXTERNAL, externalNodeCheck.isSelected());
+            settings.put(key + "." + IS_INTERNAL, internalNodeCheck.isSelected());
+        }
+        settings.put(key + "." + SHAPE_TYPE_KEY, shapeTypeCombo.getSelectedItem());
+        settings.put(key + "." + SCALE_TYPE_KEY, scaleTypeCombo.getSelectedItem());
+        settings.put(key + "." + COLOUR_ATTRIBUTE_KEY, colourAttributeCombo.getSelectedItem());
+        settings.put(key + "." + SIZE_ATTRIBUTE_KEY, sizeAttributeCombo.getSelectedItem());
+        settings.put(key + "." + SHAPE_SIZE_KEY, shapeSizeSpinner.getValue());
+        settings.put(key + "." + SHAPE_MIN_SIZE_KEY, shapeMinSizeSpinner.getValue());
     }
 
     private final JCheckBox titleCheckBox;
     private final OptionsPanel optionsPanel;
 
+    private final JCheckBox externalNodeCheck;
+    private final JCheckBox internalNodeCheck;
     private final JComboBox shapeTypeCombo;
     private final JComboBox scaleTypeCombo;
     private final JComboBox sizeAttributeCombo;
     private final JComboBox colourAttributeCombo;
     private final JSpinner shapeSizeSpinner;
     private final JSpinner shapeMinSizeSpinner;
+    private final JComboBox outlineStrokeCombo;
+    private final JComboBox outlinePaintCombo;
 
     public String getTitle() {
         return title;
     }
 
     private final String title;
-    private final NodeShapePainter nodeShapePainter;
+    private final NodeType type;
 }
diff --git a/src/figtree/treeviewer/painters/NodeShapePainter.java b/src/figtree/treeviewer/painters/NodeShapePainter.java
index 05e724f..0baf3bf 100755
--- a/src/figtree/treeviewer/painters/NodeShapePainter.java
+++ b/src/figtree/treeviewer/painters/NodeShapePainter.java
@@ -117,6 +117,20 @@ public class NodeShapePainter extends NodePainter {
         firePainterChanged();
     }
 
+    public void setNodeType(boolean external, boolean internal) {
+        this.external = external;
+        this.internal = internal;
+        firePainterChanged();
+    }
+
+    public boolean isExternal() {
+        return external;
+    }
+
+    public boolean isInternal() {
+        return internal;
+    }
+
     public double getPreferredWidth() {
         return 1.0;
     }
@@ -147,9 +161,11 @@ public class NodeShapePainter extends NodePainter {
         g2.setPaint(paint);
         g2.fill(nodeShape);
 
-        g2.setPaint(Color.black);
-        g2.setStroke(new BasicStroke(0.5F));
-        g2.draw(nodeShape);
+        if (outlineStroke > 0.0F) {
+            g2.setPaint(outlinePaint);
+            g2.setStroke(new BasicStroke(outlineStroke));
+            g2.draw(nodeShape);
+        }
     }
 
     /**
@@ -184,6 +200,12 @@ public class NodeShapePainter extends NodePainter {
         firePainterChanged();
     }
 
+    public void setOutline(final float outlineStroke, final Paint outlinePaint) {
+        this.outlineStroke = outlineStroke;
+        this.outlinePaint = outlinePaint;
+        firePainterChanged();
+    }
+
     private Shape createNodeShape(Node node, double x, double y) {
 
         double size = maxSize;
@@ -247,8 +269,14 @@ public class NodeShapePainter extends NodePainter {
     private ScaleType scaleType = ScaleType.WIDTH;
     private String sizeAttribute = null;
 
+    private boolean external = true;
+    private boolean internal = true;
+
     private Decorator colourDecorator = null;
     private ContinuousScale sizeScale = null;
 
+    private float outlineStroke = 0.5f;
+    private Paint outlinePaint = Color.black;
+
     private TreePane treePane;
 }
diff --git a/src/figtree/treeviewer/painters/ScaleAxisPainter.java b/src/figtree/treeviewer/painters/ScaleAxisPainter.java
index d2f05a0..fed070a 100755
--- a/src/figtree/treeviewer/painters/ScaleAxisPainter.java
+++ b/src/figtree/treeviewer/painters/ScaleAxisPainter.java
@@ -192,55 +192,55 @@ public class ScaleAxisPainter extends LabelPainter<TreePane> implements ScalePai
 
 	protected void paintAxis(Graphics2D g2, Rectangle2D axisBounds)
 	{
-		ScaleAxis axis = treePane.getScaleAxis();
+        ScaleAxis axis = treePane.getScaleAxis();
 
-		g2.setPaint(getForeground());
-		g2.setStroke(getScaleBarStroke());
+        g2.setPaint(getForeground());
+        g2.setStroke(getScaleBarStroke());
 
-		double minX = treePane.scaleOnAxis(axis.getMinAxis());
-		double maxX = treePane.scaleOnAxis(axis.getMaxAxis());
+        double minX = treePane.scaleOnAxis(axis.getMinAxis());
+        double maxX = treePane.scaleOnAxis(axis.getMaxAxis());
 
-		Line2D line = new Line2D.Double(minX, axisBounds.getMinY(), maxX, axisBounds.getMinY());
-		g2.draw(line);
+        Line2D line = new Line2D.Double(minX, axisBounds.getY() + topMargin, maxX, axisBounds.getY() + topMargin);
+        g2.draw(line);
 
-		int n1 = axis.getMajorTickCount();
-		int n2, i, j;
+        int n1 = axis.getMajorTickCount();
+        int n2, i, j;
 
-		n2 = axis.getMinorTickCount(-1);
-		if (axis.getLabelFirst()) { // Draw first minor tick as a major one (with a label)
+        n2 = axis.getMinorTickCount(-1);
+        if (axis.getLabelFirst()) { // Draw first minor tick as a major one (with a label)
 
-			paintMajorTick(g2, axisBounds, axis, axis.getMinorTickValue(0, -1));
+            paintMajorTick(g2, axisBounds, axis, axis.getMinorTickValue(0, -1));
 
-			for (j = 1; j < n2; j++) {
-				paintMinorTick(g2, axisBounds, axis.getMinorTickValue(j, -1));
-			}
-		} else {
+            for (j = 1; j < n2; j++) {
+                paintMinorTick(g2, axisBounds, axis.getMinorTickValue(j, -1));
+            }
+        } else {
 
-			for (j = 0; j < n2; j++) {
-				paintMinorTick(g2, axisBounds, axis.getMinorTickValue(j, -1));
-			}
-		}
+            for (j = 0; j < n2; j++) {
+                paintMinorTick(g2, axisBounds, axis.getMinorTickValue(j, -1));
+            }
+        }
 
-		for (i = 0; i < n1; i++) {
+        for (i = 0; i < n1; i++) {
 
-			paintMajorTick(g2, axisBounds, axis, axis.getMajorTickValue(i));
-			n2 = axis.getMinorTickCount(i);
+            paintMajorTick(g2, axisBounds, axis, axis.getMajorTickValue(i));
+            n2 = axis.getMinorTickCount(i);
 
-			if (i == (n1-1) && axis.getLabelLast()) { // Draw last minor tick as a major one
+            if (i == (n1-1) && axis.getLabelLast()) { // Draw last minor tick as a major one
 
-				paintMajorTick(g2, axisBounds, axis, axis.getMinorTickValue(0, i));
+                paintMajorTick(g2, axisBounds, axis, axis.getMinorTickValue(0, i));
 
-				for (j = 1; j < n2; j++) {
-					paintMinorTick(g2, axisBounds, axis.getMinorTickValue(j, i));
-				}
-			} else {
+                for (j = 1; j < n2; j++) {
+                    paintMinorTick(g2, axisBounds, axis.getMinorTickValue(j, i));
+                }
+            } else {
 
-				for (j = 0; j <  n2; j++) {
-					paintMinorTick(g2, axisBounds, axis.getMinorTickValue(j, i));
-				}
-			}
-		}
-	}
+                for (j = 0; j <  n2; j++) {
+                    paintMinorTick(g2, axisBounds, axis.getMinorTickValue(j, i));
+                }
+            }
+        }
+    }
 
 	protected void paintMajorTick(Graphics2D g2, Rectangle2D axisBounds, ScaleAxis axis, double value)
 	{
@@ -251,12 +251,12 @@ public class ScaleAxisPainter extends LabelPainter<TreePane> implements ScalePai
 		String label = getNumberFormat().format(value);
 		double pos = treePane.scaleOnAxis(value);
 
-		Line2D line = new Line2D.Double(pos, axisBounds.getMinY(), pos, axisBounds.getMinY() + majorTickSize);
+		Line2D line = new Line2D.Double(pos, axisBounds.getMinY() + topMargin, pos, axisBounds.getMinY() + majorTickSize + topMargin);
 		g2.draw(line);
 
 		g2.setPaint(getForeground());
 		double width = g2.getFontMetrics().stringWidth(label);
-		g2.drawString(label, (float)(pos - (width / 2)), (float)(axisBounds.getMinY() + tickLabelOffset));
+		g2.drawString(label, (float)(pos - (width / 2)), (float)(axisBounds.getMinY() + tickLabelOffset + topMargin));
 	}
 
 	protected void paintMinorTick(Graphics2D g2, Rectangle2D axisBounds, double value)
@@ -267,7 +267,7 @@ public class ScaleAxisPainter extends LabelPainter<TreePane> implements ScalePai
 
 		double pos = treePane.scaleOnAxis(value);
 
-		Line2D line = new Line2D.Double(pos, axisBounds.getMinY(), pos, axisBounds.getMinY() + minorTickSize);
+		Line2D line = new Line2D.Double(pos, axisBounds.getMinY() + topMargin, pos, axisBounds.getMinY() + minorTickSize + topMargin);
 		g2.draw(line);
 	}
 
diff --git a/src/figtree/treeviewer/painters/ScaleAxisPainterController.java b/src/figtree/treeviewer/painters/ScaleAxisPainterController.java
index c3bd7de..60c36ab 100755
--- a/src/figtree/treeviewer/painters/ScaleAxisPainterController.java
+++ b/src/figtree/treeviewer/painters/ScaleAxisPainterController.java
@@ -314,7 +314,7 @@ public class ScaleAxisPainterController extends AbstractController {
         titleCheckBox.setSelected((Boolean)settings.get(SCALE_AXIS_KEY + "." + IS_SHOWN));
         reverseAxisCheck.setSelected((Boolean)settings.get(SCALE_AXIS_KEY + "." + REVERSE_AXIS_KEY));
         showGridCheck.setSelected((Boolean)settings.get(SCALE_AXIS_KEY + "." + SHOW_GRID_KEY));
-//		minorTicksText.setValue((Double)settings.get(SCALE_AXIS_KEY + "." + MINOR_TICKS_KEY));
+		minorTicksText.setValue((Double)settings.get(SCALE_AXIS_KEY + "." + MINOR_TICKS_KEY));
         majorTicksText.setValue((Double)settings.get(SCALE_AXIS_KEY + "." + MAJOR_TICKS_KEY));
         originText.setValue((Double)settings.get(SCALE_AXIS_KEY + "." + ORIGIN_KEY));
         autoScaleCheck.setSelected((Boolean)settings.get(SCALE_AXIS_KEY + "." + AUTOMATIC_SCALE_KEY));
@@ -329,7 +329,7 @@ public class ScaleAxisPainterController extends AbstractController {
         settings.put(SCALE_AXIS_KEY + "." + REVERSE_AXIS_KEY, reverseAxisCheck.isSelected());
         settings.put(SCALE_AXIS_KEY + "." + SHOW_GRID_KEY, showGridCheck.isSelected());
         settings.put(SCALE_AXIS_KEY + "." + AUTOMATIC_SCALE_KEY, autoScaleCheck.isSelected());
-//		settings.put(SCALE_AXIS_KEY + "." + MINOR_TICKS_KEY, minorTicksText.getValue());
+		settings.put(SCALE_AXIS_KEY + "." + MINOR_TICKS_KEY, minorTicksText.getValue());
         settings.put(SCALE_AXIS_KEY + "." + MAJOR_TICKS_KEY, majorTicksText.getValue());
         settings.put(SCALE_AXIS_KEY + "." + ORIGIN_KEY, originText.getValue());
         settings.put(SCALE_AXIS_KEY + "." + FONT_SIZE_KEY, fontSizeSpinner.getValue());
diff --git a/src/figtree/treeviewer/painters/ScaleGridPainter.java b/src/figtree/treeviewer/painters/ScaleGridPainter.java
index 7236b45..8a65b58 100755
--- a/src/figtree/treeviewer/painters/ScaleGridPainter.java
+++ b/src/figtree/treeviewer/painters/ScaleGridPainter.java
@@ -57,7 +57,7 @@ public class ScaleGridPainter extends LabelPainter<TreePane> implements ScalePai
 		preferredWidth = treePane.getTreeBounds().getWidth();
 		preferredHeight = treePane.getTreeBounds().getHeight();
 
-		return new Rectangle2D.Double(0.0, 0.0, preferredWidth, preferredHeight);
+        return new Rectangle2D.Double(0.0, 0.0, preferredWidth, preferredHeight);
 	}
 
 	public void paint(Graphics2D g2, TreePane treePane, Justification justification, Rectangle2D bounds) {
@@ -66,7 +66,7 @@ public class ScaleGridPainter extends LabelPainter<TreePane> implements ScalePai
 		Stroke oldStroke = g2.getStroke();
 
 		if (TreePane.DEBUG_OUTLINE) {
-			g2.setPaint(Color.red);
+			g2.setPaint(Color.blue);
 			g2.draw(bounds);
 		}
 
diff --git a/src/figtree/treeviewer/treelayouts/AbstractTreeLayout.java b/src/figtree/treeviewer/treelayouts/AbstractTreeLayout.java
index 3ff5057..225a115 100755
--- a/src/figtree/treeviewer/treelayouts/AbstractTreeLayout.java
+++ b/src/figtree/treeviewer/treelayouts/AbstractTreeLayout.java
@@ -35,15 +35,6 @@ import java.util.Set;
  */
 public abstract class AbstractTreeLayout implements TreeLayout {
 	private double rootLength = 0.0;
-    private boolean isAxisReversed;
-
-    public boolean isAxisReversed() {
-        return isAxisReversed;
-    }
-
-    public void setAxisReversed(final boolean axisReversed) {
-        isAxisReversed = axisReversed;
-    }
 
     public double getRootLength() {
 		return rootLength;
diff --git a/src/figtree/treeviewer/treelayouts/PolarTreeLayout.java b/src/figtree/treeviewer/treelayouts/PolarTreeLayout.java
index 1cadcd3..3ae9350 100755
--- a/src/figtree/treeviewer/treelayouts/PolarTreeLayout.java
+++ b/src/figtree/treeviewer/treelayouts/PolarTreeLayout.java
@@ -93,9 +93,6 @@ public class PolarTreeLayout extends AbstractTreeLayout {
 
     public Shape getAxisLine(double height) {
         double x = height;
-        if (isAxisReversed()) {
-            x = maxXPosition - x;
-        }
         return new Ellipse2D.Double(-x, -x, x * 2.0, x * 2.0);
     }
 
diff --git a/src/figtree/treeviewer/treelayouts/RectilinearTreeLayout.java b/src/figtree/treeviewer/treelayouts/RectilinearTreeLayout.java
index 21a6b27..47be98e 100755
--- a/src/figtree/treeviewer/treelayouts/RectilinearTreeLayout.java
+++ b/src/figtree/treeviewer/treelayouts/RectilinearTreeLayout.java
@@ -85,9 +85,6 @@ public class RectilinearTreeLayout extends AbstractTreeLayout {
 
     public Shape getAxisLine(double height) {
         double x = height;
-        if (isAxisReversed()) {
-            x = maxXPosition - x;
-        }
         double y1 = 0.0;
         double y2 = 1.0;
         return new Line2D.Double(x, y1, x, y2);
@@ -95,9 +92,6 @@ public class RectilinearTreeLayout extends AbstractTreeLayout {
 
     public Shape getHeightArea(double height1, double height2) {
         double x = height1;
-        if (isAxisReversed()) {
-            x = maxXPosition - x;
-        }
         double y = 0.0;
         double w = Math.abs(height2 - height1);
         double h = 1.0;
diff --git a/src/figtree/treeviewer/treelayouts/TreeLayout.java b/src/figtree/treeviewer/treelayouts/TreeLayout.java
index 1b62e6d..8cac641 100755
--- a/src/figtree/treeviewer/treelayouts/TreeLayout.java
+++ b/src/figtree/treeviewer/treelayouts/TreeLayout.java
@@ -82,11 +82,6 @@ public interface TreeLayout {
 
     void setRootLength(double rootLength);
 
-    boolean isAxisReversed();
-
-    void setAxisReversed(final boolean axisReversed);
-
-
     /**
      * Return whether this layout is showing a branch colouring
      * @return showing colouring?

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/figtree.git



More information about the debian-med-commit mailing list