Merge branch 'develop' into features/JAL-2094_colourInterface features/JAL-2094_colourInterface
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Mon, 19 Sep 2016 16:56:18 +0000 (17:56 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Mon, 19 Sep 2016 16:56:18 +0000 (17:56 +0100)
Conflicts:
src/jalview/api/FeatureColourI.java
src/jalview/api/FeatureRenderer.java
src/jalview/appletgui/FeatureColourChooser.java
src/jalview/appletgui/FeatureRenderer.java
src/jalview/appletgui/FeatureSettings.java
src/jalview/appletgui/SeqCanvas.java
src/jalview/appletgui/SequenceRenderer.java
src/jalview/appletgui/UserDefinedColours.java
src/jalview/ext/ensembl/EnsemblGene.java
src/jalview/gui/FeatureColourChooser.java
src/jalview/gui/FeatureRenderer.java
src/jalview/gui/FeatureSettings.java
src/jalview/gui/Jalview2XML.java
src/jalview/gui/SeqCanvas.java
src/jalview/gui/TreeCanvas.java
src/jalview/io/FeaturesFile.java
src/jalview/io/PDBFeatureSettings.java
src/jalview/renderer/seqfeatures/FeatureRenderer.java
src/jalview/schemes/FeatureColour.java
src/jalview/schemes/FeatureColourAdapter.java
src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java
src/jalview/viewmodel/seqfeatures/FeatureRendererSettings.java
src/jalview/ws/jws1/SeqSearchWSThread.java
src/jalview/ws/jws2/AADisorderClient.java
test/jalview/io/FeaturesFileTest.java
test/jalview/schemes/FeatureColourTest.java

40 files changed:
1  2 
examples/exampleFeatures.txt
src/MCview/Atom.java
src/MCview/PDBChain.java
src/jalview/api/AlignViewportI.java
src/jalview/api/FeatureColourI.java
src/jalview/appletgui/AnnotationColourChooser.java
src/jalview/appletgui/FeatureColourChooser.java
src/jalview/appletgui/FeatureRenderer.java
src/jalview/appletgui/FeatureSettings.java
src/jalview/appletgui/OverviewPanel.java
src/jalview/appletgui/SeqCanvas.java
src/jalview/appletgui/SequenceRenderer.java
src/jalview/appletgui/TreeCanvas.java
src/jalview/controller/AlignViewController.java
src/jalview/datamodel/SequenceGroup.java
src/jalview/ext/ensembl/EnsemblGene.java
src/jalview/gui/AlignmentPanel.java
src/jalview/gui/FeatureColourChooser.java
src/jalview/gui/FeatureRenderer.java
src/jalview/gui/FeatureSettings.java
src/jalview/gui/Jalview2XML.java
src/jalview/gui/SeqCanvas.java
src/jalview/gui/SequenceRenderer.java
src/jalview/gui/TextColourChooser.java
src/jalview/gui/TreeCanvas.java
src/jalview/io/AnnotationFile.java
src/jalview/io/FeaturesFile.java
src/jalview/io/HtmlSvgOutput.java
src/jalview/io/JSONFile.java
src/jalview/io/PDBFeatureSettings.java
src/jalview/renderer/seqfeatures/FeatureRenderer.java
src/jalview/schemes/FeatureColour.java
src/jalview/schemes/FeatureColourAdapter.java
src/jalview/viewmodel/AlignmentViewport.java
src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java
src/jalview/viewmodel/seqfeatures/FeatureRendererSettings.java
test/MCview/PDBChainTest.java
test/jalview/ext/ensembl/EnsemblGeneTest.java
test/jalview/io/FeaturesFileTest.java
test/jalview/schemes/FeatureColourTest.java

@@@ -22,79 -22,78 +22,80 @@@ NEST-RL    3e16b
  ASX-TURN-IIL  a67c98
  BETA-TURN-IIR c79792
  PHOSPHORYLATION (T)   c88395
 -BETA-TURN-IIL 8b5b50
 -ST-MOTIF      ac25a1
 +BETA-TURN-IIL label
 +#8b5b50
 +ST-MOTIF      label|||0|0
 +#ac25a1
  
  STARTGROUP    uniprot
+ <html><a href="http://pfam.xfam.org/family/PF00111">Pfam family</a></html>    FER_CAPAA       -1      0       0       Pfam
  Iron-sulfur (2Fe-2S)  FER_CAPAA       -1      39      39      METAL
  Iron-sulfur (2Fe-2S)  FER_CAPAA       -1      44      44      METAL
  Iron-sulfur (2Fe-2S)  FER_CAPAA       -1      47      47      METAL
  Iron-sulfur (2Fe-2S)  FER_CAPAA       -1      77      77      METAL
- <html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 8_8</a></html>        FER_CAPAA       -1      8       83      Pfam
+ <html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 8_8</a></html>    FER_CAPAA       -1      8       83      Pfam
  Ferredoxin_fold Status: True Positive         FER_CAPAA       -1      3       93      Cath
  Iron-sulfur (2Fe-2S)  FER_CAPAN       -1      86      86      METAL
  Iron-sulfur (2Fe-2S)  FER_CAPAN       -1      91      91      METAL
  Iron-sulfur (2Fe-2S)  FER_CAPAN       -1      94      94      METAL
  Iron-sulfur (2Fe-2S)  FER_CAPAN       -1      124     124     METAL
- <html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 55_13</a></html>      FER_CAPAN       -1      55      130     Pfam
+ <html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 55_13</a></html>  FER_CAPAN       -1      55      130     Pfam
  Ferredoxin_fold Status: True Positive         FER_CAPAN       -1      45      140     Cath
  Iron-sulfur (2Fe-2S)  FER1_SOLLC      -1      86      86      METAL
  Iron-sulfur (2Fe-2S)  FER1_SOLLC      -1      91      91      METAL
  Iron-sulfur (2Fe-2S)  FER1_SOLLC      -1      94      94      METAL
  Iron-sulfur (2Fe-2S)  FER1_SOLLC      -1      124     124     METAL
- <html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 55_13</a></html>      FER1_SOLLC      -1      55      130     Pfam
+ <html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 55_13</a></html>  FER1_SOLLC      -1      55      130     Pfam
  Ferredoxin_fold Status: True Positive         FER1_SOLLC      -1      45      140     Cath
- <html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 55_13</a></html>      Q93XJ9_SOLTU    -1      55      130     Pfam
+ <html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 55_13</a></html>  Q93XJ9_SOLTU    -1      55      130     Pfam
  Ferredoxin_fold Status: True Positive         Q93XJ9_SOLTU    -1      45      140     Cath
  Iron-sulfur (2Fe-2S)  FER1_PEA        -1      91      91      METAL
  Iron-sulfur (2Fe-2S)  FER1_PEA        -1      96      96      METAL
  Iron-sulfur (2Fe-2S)  FER1_PEA        -1      99      99      METAL
  Iron-sulfur (2Fe-2S)  FER1_PEA        -1      129     129     METAL
- <html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 60_13</a></html>      FER1_PEA        -1      60      135     Pfam
+ <html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 60_13</a></html>  FER1_PEA        -1      60      135     Pfam
  Ferredoxin_fold Status: True Positive         FER1_PEA        -1      50      145     Cath
- <html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 63_13</a></html>      Q7XA98_TRIPR    -1      63      138     Pfam
+ <html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 63_13</a></html>  Q7XA98_TRIPR    -1      63      138     Pfam
  Ferredoxin_fold Status: True Positive         Q7XA98_TRIPR    -1      53      148     Cath
  Iron-sulfur (2Fe-2S)  FER1_MESCR      -1      90      90      METAL
  Iron-sulfur (2Fe-2S)  FER1_MESCR      -1      95      95      METAL
  Iron-sulfur (2Fe-2S)  FER1_MESCR      -1      98      98      METAL
  Iron-sulfur (2Fe-2S)  FER1_MESCR      -1      128     128     METAL
- <html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 59_13</a></html>      FER1_MESCR      -1      59      134     Pfam
+ <html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 59_13</a></html>  FER1_MESCR      -1      59      134     Pfam
  Ferredoxin_fold Status: True Positive         FER1_MESCR      -1      49      144     Cath
  Iron-sulfur (2Fe-2S)  FER1_SPIOL      -1      89      89      METAL
  Iron-sulfur (2Fe-2S)  FER1_SPIOL      -1      94      94      METAL
  Iron-sulfur (2Fe-2S)  FER1_SPIOL      -1      97      97      METAL
  Iron-sulfur (2Fe-2S)  FER1_SPIOL      -1      127     127     METAL
- <html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 58_13</a></html>      FER1_SPIOL      -1      58      133     Pfam
+ <html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 58_13</a></html>  FER1_SPIOL      -1      58      133     Pfam
  Ferredoxin_fold Status: True Positive         FER1_SPIOL      -1      48      143     Cath
  Iron-sulfur (2Fe-2S)  FER3_RAPSA      -1      39      39      METAL
  Iron-sulfur (2Fe-2S)  FER3_RAPSA      -1      44      44      METAL
  Iron-sulfur (2Fe-2S)  FER3_RAPSA      -1      47      47      METAL
  Iron-sulfur (2Fe-2S)  FER3_RAPSA      -1      77      77      METAL
- <html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 8_8</a></html>        FER3_RAPSA      -1      8       83      Pfam
+ <html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 8_8</a></html>    FER3_RAPSA      -1      8       83      Pfam
  Ferredoxin_fold Status: True Positive         FER3_RAPSA      -1      3       93      Cath
  Iron-sulfur (2Fe-2S)  FER_BRANA       -1      39      39      METAL
  Iron-sulfur (2Fe-2S)  FER_BRANA       -1      44      44      METAL
  Iron-sulfur (2Fe-2S)  FER_BRANA       -1      47      47      METAL
  Iron-sulfur (2Fe-2S)  FER_BRANA       -1      77      77      METAL
- <html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 8_8</a></html>        FER_BRANA       -1      8       83      Pfam
+ <html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 8_8</a></html>    FER_BRANA       -1      8       83      Pfam
  Ferredoxin_fold Status: True Positive         FER_BRANA       -1      2       96      Cath
  Iron-sulfur (2Fe-2S)  FER2_ARATH      -1      91      91      METAL
  Iron-sulfur (2Fe-2S)  FER2_ARATH      -1      96      96      METAL
  Iron-sulfur (2Fe-2S)  FER2_ARATH      -1      99      99      METAL
  Iron-sulfur (2Fe-2S)  FER2_ARATH      -1      129     129     METAL
- <html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 60_13</a></html>      FER2_ARATH      -1      60      135     Pfam
+ <html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 60_13</a></html>  FER2_ARATH      -1      60      135     Pfam
  Ferredoxin_fold Status: True Positive         FER2_ARATH      -1      50      145     Cath
- <html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 60_11</a></html>      Q93Z60_ARATH    -1      60      118     Pfam
+ <html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 60_11</a></html>  Q93Z60_ARATH    -1      60      118     Pfam
  Ferredoxin_fold Status: True Positive         Q93Z60_ARATH    -1      52      118     Cath
  Iron-sulfur (2Fe-2S)  FER1_MAIZE      -1      91      91      METAL
  Iron-sulfur (2Fe-2S)  FER1_MAIZE      -1      96      96      METAL
  Iron-sulfur (2Fe-2S)  FER1_MAIZE      -1      99      99      METAL
  Iron-sulfur (2Fe-2S)  FER1_MAIZE      -1      129     129     METAL
- <html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 60_13</a></html>      FER1_MAIZE      -1      60      135     Pfam
+ <html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 60_13</a></html>  FER1_MAIZE      -1      60      135     Pfam
  Ferredoxin_fold Status: True Positive         FER1_MAIZE      -1      50      145     Cath
- <html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 52_12</a></html>      O80429_MAIZE    -1      52      127     Pfam
+ <html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 52_12</a></html>  O80429_MAIZE    -1      52      127     Pfam
  Ferredoxin_fold Status: True Positive         O80429_MAIZE    -1      42      137     Cath
  ENDGROUP      uniprot
  
diff --combined src/MCview/Atom.java
   */
  package MCview;
  
 +import jalview.api.ColorI;
 +import jalview.schemes.Colour;
  import jalview.schemes.ResidueProperties;
  
 -import java.awt.Color;
 -
  public class Atom
  {
    public float x;
@@@ -46,7 -46,7 +46,7 @@@
  
    public int type;
  
 -  Color color = Color.lightGray;
 +  ColorI color = Colour.lightGray;
  
    public String chain;
  
@@@ -81,7 -81,7 +81,7 @@@
      chain = str.substring(21, 22);
  
      resNumber = Integer.parseInt(str.substring(22, 26).trim());
-     resNumIns = str.substring(22, 27);
+     resNumIns = str.substring(22, 27).trim();
      insCode = str.substring(26, 27).charAt(0);
      this.x = (new Float(str.substring(30, 38).trim()).floatValue());
      this.y = (new Float(str.substring(38, 46).trim()).floatValue());
      }
    }
  
+   @Override
+   public boolean equals(Object that)
+   {
+     if (this == that || that == null)
+     {
+       return true;
+     }
+     if (that instanceof Atom)
+     {
+       Atom other = (Atom) that;
+       return other.resName.equals(this.resName)
+               && other.resNumber == this.resNumber
+               && other.resNumIns.equals(this.resNumIns)
+               && other.chain.equals(this.chain);
+     }
+     return false;
+   }
    public Atom(float x, float y, float z)
    {
      this.x = x;
diff --combined src/MCview/PDBChain.java
  package MCview;
  
  import jalview.analysis.AlignSeq;
 +import jalview.api.ColorI;
  import jalview.datamodel.AlignmentAnnotation;
  import jalview.datamodel.Annotation;
  import jalview.datamodel.Mapping;
  import jalview.datamodel.Sequence;
  import jalview.datamodel.SequenceFeature;
  import jalview.datamodel.SequenceI;
 +import jalview.schemes.Colour;
  import jalview.schemes.ColourSchemeI;
  import jalview.schemes.ResidueProperties;
+ import jalview.structure.StructureImportSettings;
  import jalview.structure.StructureMapping;
- import jalview.structure.StructureViewSettings;
  
  import java.awt.Color;
  import java.util.List;
@@@ -84,7 -82,7 +84,7 @@@ public class PDBChai
  
    public PDBChain(String pdbid, String id)
    {
-     this.pdbid = pdbid.toLowerCase();
+     this.pdbid = pdbid == null ? pdbid : pdbid.toLowerCase();
      this.id = id;
    }
  
        else
        {
  
-         // boolean baseDetected = false;
-         // for (Atom resAtom : resAtoms)
-         // {
-         // if (resAtom.insCode == ' ')
-         // {
-         // baseDetected = true;
-         // }
-         // }
-         // if (!baseDetected)
-         // {
-         // continue;
-         // }
        // Make a new Residue object with the new atoms vector
        residues.addElement(new Residue(resAtoms, resNumber - 1, count));
  
          SequenceFeature sf = new SequenceFeature("RESNUM", tmpat.resName
                + ":" + tmpat.resNumIns + " " + pdbid + id, "", offset
                + count, offset + count, pdbid);
-       // MCview.PDBChain.PDBFILEFEATURE);
        resFeatures.addElement(sf);
        resAnnotation.addElement(new Annotation(tmpat.tfactor));
        // Keep totting up the sequence
      // System.out.println("PDB Sequence is :\nSequence = " + seq);
      // System.out.println("No of residues = " + residues.size());
  
-     if (StructureViewSettings.isShowSeqFeatures())
+     if (StructureImportSettings.isShowSeqFeatures())
      {
      for (i = 0, iSize = resFeatures.size(); i < iSize; i++)
      {
        }
        else
        {
 -        b.startCol = Color.gray;
 -        b.endCol = Color.gray;
 +        b.startCol = Colour.gray;
 +        b.endCol = Colour.gray;
        }
      }
    }
  
 -  public static Color getChargeColour(String resName)
 +  public static ColorI getChargeColour(String resName)
    {
 -    Color result = Color.lightGray;
 +    ColorI result = Colour.lightGray;
      if ("ASP".equals(resName) || "GLU".equals(resName))
      {
 -      result = Color.red;
 +      result = Colour.red;
      }
      else if ("LYS".equals(resName) || "ARG".equals(resName))
      {
 -      result = Color.blue;
 +      result = Colour.blue;
      }
      else if ("CYS".equals(resName))
      {
 -      result = Color.yellow;
 +      result = Colour.yellow;
      }
      return result;
    }
        try
        {
          index = ResidueProperties.aa3Hash.get(b.at1.resName).intValue();
 -        b.startCol = cs.findColour(ResidueProperties.aa[index].charAt(0));
 +        b.startCol = new Colour(cs.findColour(ResidueProperties.aa[index]
 +                .charAt(0)));
  
          index = ResidueProperties.aa3Hash.get(b.at2.resName).intValue();
 -        b.endCol = cs.findColour(ResidueProperties.aa[index].charAt(0));
 +        b.endCol = new Colour(cs.findColour(ResidueProperties.aa[index]
 +                .charAt(0)));
  
        } catch (Exception e)
        {
 -        b.startCol = Color.gray;
 -        b.endCol = Color.gray;
 +        b.startCol = Colour.gray;
 +        b.endCol = Colour.gray;
        }
      }
    }
    {
      for (Bond b : bonds)
      {
 -      b.startCol = col;
 -      b.endCol = col;
 +      b.startCol = new Colour(col);
 +      b.endCol = new Colour(col);
      }
    }
  
          {
            for (AlignmentAnnotation ana : sequence.getAnnotation())
            {
-             List<AlignmentAnnotation> transfer = sq
+             List<AlignmentAnnotation> transfer = dsq
                      .getAlignmentAnnotations(ana.getCalcId(), ana.label);
              if (transfer == null || transfer.size() == 0)
              {
                ana = new AlignmentAnnotation(ana);
                ana.liftOver(dsq, sqmpping);
+               dsq.addAlignmentAnnotation(ana);
                // mapping.transfer(ana);
              }
              else
@@@ -31,6 -31,7 +31,6 @@@ import jalview.datamodel.SequenceGroup
  import jalview.datamodel.SequenceI;
  import jalview.schemes.ColourSchemeI;
  
 -import java.awt.Color;
  import java.util.Hashtable;
  import java.util.List;
  import java.util.Map;
@@@ -52,6 -53,18 +52,18 @@@ public interface AlignViewportI extend
     */
    public int calcPanelHeight();
  
+   /**
+    * Answers true if the viewport has at least one column selected
+    * 
+    * @return
+    */
+   boolean hasSelectedColumns();
+   /**
+    * Answers true if the viewport has at least one hidden column
+    * 
+    * @return
+    */
    boolean hasHiddenColumns();
  
    boolean isValidCharWidth();
    void updateGroupAnnotationSettings(boolean applyGlobalSettings,
            boolean preserveNewGroupSettings);
  
 -  void setSequenceColour(SequenceI seq, Color col);
 +  void setSequenceColour(SequenceI seq, ColorI col);
  
 -  Color getSequenceColour(SequenceI seq);
 +  ColorI getSequenceColour(SequenceI seq);
  
    void updateSequenceIdColours();
  
     * This method returns the visible alignment as text, as seen on the GUI, ie
     * if columns are hidden they will not be returned in the result. Use this for
     * calculating trees, PCA, redundancy etc on views which contain hidden
+    * columns. This method doesn't exclude hidden sequences from the output.
+    *
+    * @param selectedRegionOnly
+    *          - determines if only the selected region or entire alignment is
+    *          exported
+    * @return String[]
+    */
+   String[] getViewAsString(boolean selectedRegionOnly);
+   
+   /**
+    * This method returns the visible alignment as text, as seen on the GUI, ie
+    * if columns are hidden they will not be returned in the result. Use this for
+    * calculating trees, PCA, redundancy etc on views which contain hidden
     * columns.
     * 
+    * @param selectedRegionOnly
+    *          - determines if only the selected region or entire alignment is
+    *          exported
+    * @param isExportHiddenSeqs
+    *          - determines if hidden sequences would be exported or not.
+    * 
     * @return String[]
     */
-   String[] getViewAsString(boolean selectedRegionOnly);
+   String[] getViewAsString(boolean selectedRegionOnly, boolean isExportHiddenSeqs);
  
    void setSelectionGroup(SequenceGroup sg);
  
     */
    void setFollowHighlight(boolean b);
  
    public void applyFeaturesStyle(FeatureSettingsModelI featureSettings);
+   /**
+    * check if current selection group is defined on the view, or is simply a
+    * temporary group.
+    * 
+    * @return true if group is defined on the alignment
+    */
+   boolean isSelectionDefinedGroup();
  }
@@@ -2,12 -2,13 +2,11 @@@ package jalview.api
  
  import jalview.datamodel.SequenceFeature;
  
 -import java.awt.Color;
 -
  public interface FeatureColourI
  {
  
    /**
-    * Answers true when either isColourByLabel, isAboveThreshold or
-    * isBelowThreshold answers true
+    * Answers true when the feature colour varies across the score range
     * 
     * @return
     */
     * 
     * @return
     */
 -  Color getColour();
 +  ColorI getColour();
  
    /**
     * Returns the minimum colour (when isGraduatedColour answers true)
     * 
     * @return
     */
 -  Color getMinColour();
 +  ColorI getMinColour();
  
    /**
     * Returns the maximum colour (when isGraduatedColour answers true)
     * 
     * @return
     */
 -  Color getMaxColour();
 +  ColorI getMaxColour();
  
    /**
-    * Answers true if the feature is coloured by label (description); only
-    * applicable when isGraduatedColour answers true
+    * Answers true if the feature has a single colour, i.e. if isColourByLabel()
+    * and isGraduatedColour() both answer false
+    * 
+    * @return
+    */
+   boolean isSimpleColour();
+   /**
+    * Answers true if the feature is coloured by label (description)
     * 
     * @return
     */
    boolean isColourByLabel();
  
+   void setColourByLabel(boolean b);
    /**
     * Answers true if the feature is coloured below a threshold value; only
     * applicable when isGraduatedColour answers true
@@@ -50,6 -60,8 +58,8 @@@
     */
    boolean isBelowThreshold();
  
+   void setBelowThreshold(boolean b);
    /**
     * Answers true if the feature is coloured above a threshold value; only
     * applicable when isGraduatedColour answers true
     */
    boolean isAboveThreshold();
  
+   void setAboveThreshold(boolean b);
    /**
-    * Answers true if the threshold is the min (or max) of the colour range; only
-    * applicable when isGraduatedColour answers true
+    * Answers true if the threshold is the minimum value (when
+    * isAboveThreshold()) or maximum value (when isBelowThreshold()) of the
+    * colour range; only applicable when isGraduatedColour and either
+    * isAboveThreshold() or isBelowThreshold() answers true
     * 
     * @return
     */
    boolean isThresholdMinMax();
  
+   void setThresholdMinMax(boolean b);
    /**
     * Returns the threshold value (if any), else zero
     * 
     */
    float getThreshold();
  
-   float getMax();
-   /**
-    * Returns the minimum score of the graduated colour range
-    * 
-    * @return
-    */
-   float getMin();
+   void setThreshold(float f);
  
    /**
 +   * Answers true if either isAboveThreshold or isBelowThreshold answers true
 +   * 
 +   * @return
 +   */
 +  boolean hasThreshold();
 +
 +  /**
-    * Returns the computed colour for the given sequence feature
+    * Answers true if the colour varies between the actual minimum and maximum
+    * score values of the feature, or false if between absolute minimum and
+    * maximum values (or if not a graduated colour).
     * 
-    * @param feature
     * @return
     */
-   ColorI getColor(SequenceFeature feature);
+   boolean isAutoScaled();
+   void setAutoScaled(boolean b);
  
    /**
 -   * Returns the maximum score of the graduated colour range
 +   * Answers true if the feature has a simple colour, or is coloured by label,
 +   * or has a graduated colour and the score of this feature instance is within
 +   * the range to render (if any), i.e. does not lie below or above any
 +   * threshold set.
     * 
 +   * @param feature
     * @return
     */
 -  float getMax();
 +  boolean isColored(SequenceFeature feature);
  
    /**
 -   * Returns the minimum score of the graduated colour range
 +   * Update the min-max range for a graduated colour scheme
     * 
 -   * @return
 +   * @param min
 +   * @param max
     */
 -  float getMin();
 +  void updateBounds(float min, float max);
  
    /**
 -   * Answers true if either isAboveThreshold or isBelowThreshold answers true
 +   * Returns the colour in Jalview features file format
     * 
     * @return
     */
 -  boolean hasThreshold();
 +  String toJalviewFormat(String featureType);
  
-   void setThreshold(float f);
-   boolean isAutoScaled();
-   void setAutoScaled(boolean b);
-   boolean isSimpleColour();
-   void setAboveThreshold(boolean b);
-   void setThresholdMinMax(boolean b);
-   void setBelowThreshold(boolean b);
+   /**
 -   * Returns the computed colour for the given sequence feature
++   * Returns the maximum score of the graduated colour range
+    * 
 -   * @param feature
+    * @return
+    */
 -  Color getColor(SequenceFeature feature);
++  float getMax();
  
-   void setColourByLabel(boolean b);
+   /**
 -   * Answers true if the feature has a simple colour, or is coloured by label,
 -   * or has a graduated colour and the score of this feature instance is within
 -   * the range to render (if any), i.e. does not lie below or above any
 -   * threshold set.
++   * Returns the minimum score of the graduated colour range
+    * 
 -   * @param feature
+    * @return
+    */
 -  boolean isColored(SequenceFeature feature);
 -
 -  /**
 -   * Update the min-max range for a graduated colour scheme
 -   * 
 -   * @param min
 -   * @param max
 -   */
 -  void updateBounds(float min, float max);
++  float getMin();
  
-   void setGraduatedColour(boolean b);
+   /**
 -   * Returns the colour in Jalview features file format
++   * Returns the computed colour for the given sequence feature
+    * 
++   * @param feature
+    * @return
+    */
 -  String toJalviewFormat(String featureType);
++  ColorI getColor(SequenceFeature feature);
  }
@@@ -138,11 -138,11 +138,11 @@@ public class AnnotationColourChooser ex
      }
  
      threshold.addItem(MessageManager
-             .getString("label.threshold_feature_no_thereshold"));
+             .getString("label.threshold_feature_no_threshold"));
      threshold.addItem(MessageManager
-             .getString("label.threshold_feature_above_thereshold"));
+             .getString("label.threshold_feature_above_threshold"));
      threshold.addItem(MessageManager
-             .getString("label.threshold_feature_below_thereshold"));
+             .getString("label.threshold_feature_below_threshold"));
  
      if (oldcs instanceof AnnotationColourGradient)
      {
        default:
          throw new Error(
                  MessageManager
-                         .getString("error.implementation_error_dont_know_thereshold_annotationcolourgradient"));
+                         .getString("error.implementation_error_dont_know_threshold_annotationcolourgradient"));
        }
        thresholdIsMin.setState(acg.thresholdIsMinMax);
        thresholdValue.setText("" + acg.getAnnotationThreshold());
  
    Checkbox thresholdIsMin = new Checkbox();
  
 +  @Override
    public void actionPerformed(ActionEvent evt)
    {
      if (evt.getSource() == thresholdValue)
      }
    }
  
 +  @Override
    public void itemStateChanged(ItemEvent evt)
    {
      if (evt.getSource() == currentColours)
      changeColour();
    }
  
 +  @Override
    public void adjustmentValueChanged(AdjustmentEvent evt)
    {
      if (!adjusting)
  
    }
  
 +  @Override
    public void mouseClicked(MouseEvent evt)
    {
    }
  
 +  @Override
    public void mousePressed(MouseEvent evt)
    {
    }
  
 +  @Override
    public void mouseReleased(MouseEvent evt)
    {
      ap.paintAlignment(true);
    }
  
 +  @Override
    public void mouseEntered(MouseEvent evt)
    {
    }
  
 +  @Override
    public void mouseExited(MouseEvent evt)
    {
    }
@@@ -23,9 -23,7 +23,9 @@@ package jalview.appletgui
  import jalview.api.FeatureColourI;
  import jalview.datamodel.GraphLine;
  import jalview.schemes.AnnotationColourGradient;
 +import jalview.schemes.Colour;
  import jalview.schemes.FeatureColour;
 +import jalview.util.ColorUtils;
  import jalview.util.MessageManager;
  
  import java.awt.Checkbox;
@@@ -108,16 -106,15 +108,16 @@@ public class FeatureColourChooser exten
        Color bl = Color.black;
        if (oldcs.isSimpleColour())
        {
 -        bl = oldcs.getColour();
 +        bl = ColorUtils.getColor(oldcs.getColour());
        }
        // original colour becomes the maximum colour
 -      cs = new FeatureColour(Color.white, bl, mm[0], mm[1]);
 +      cs = new FeatureColour(new Colour(Color.white), new Colour(bl),
 +              mm[0], mm[1]);
      }
 -    minColour.setBackground(cs.getMinColour());
 -    maxColour.setBackground(cs.getMaxColour());
 -    minColour.setForeground(cs.getMinColour());
 -    maxColour.setForeground(cs.getMaxColour());
 +    minColour.setBackground(ColorUtils.getColor(cs.getMinColour()));
 +    maxColour.setBackground(ColorUtils.getColor(cs.getMaxColour()));
 +    minColour.setForeground(ColorUtils.getColor(cs.getMinColour()));
 +    maxColour.setForeground(ColorUtils.getColor(cs.getMaxColour()));
      colourFromLabel.setState(cs.isColourByLabel());
      adjusting = true;
  
      jPanel4.setBackground(Color.white);
      threshold.addItemListener(this);
      threshold.addItem(MessageManager
-             .getString("label.threshold_feature_no_thereshold"));
+             .getString("label.threshold_feature_no_threshold"));
      threshold.addItem(MessageManager
-             .getString("label.threshold_feature_above_thereshold"));
+             .getString("label.threshold_feature_above_threshold"));
      threshold.addItem(MessageManager
-             .getString("label.threshold_feature_below_thereshold"));
+             .getString("label.threshold_feature_below_threshold"));
      thresholdValue.addActionListener(this);
      slider.setBackground(Color.white);
      slider.setEnabled(false);
      // ap.paintAlignment(false);
    }
  
 -  public void minColour_actionPerformed(Color newCol)
 +  public void minColour_actionPerformed(Color c)
    {
 -    if (newCol == null)
 +    if (c == null)
      {
        UserDefinedColours udc = new UserDefinedColours(this,
                minColour.getBackground(), owner,
      }
      else
      {
 -      minColour.setBackground(newCol);
 -      minColour.setForeground(newCol);
 +      minColour.setBackground(c);
 +      minColour.setForeground(c);
        minColour.repaint();
        changeColour();
      }
  
      slider.setEnabled(true);
      thresholdValue.setEnabled(true);
 -    FeatureColour acg = new FeatureColour(minColour.getBackground(),
 -            maxColour.getBackground(), min, max);
 +    FeatureColour acg = new FeatureColour(new Colour(
 +            minColour.getBackground()), new Colour(
 +            maxColour.getBackground()), min, max);
  
      acg.setColourByLabel(colourFromLabel.getState());
      maxColour.setEnabled(!colourFromLabel.getState());
@@@ -25,10 -25,8 +25,10 @@@ import jalview.datamodel.SearchResults
  import jalview.datamodel.SequenceFeature;
  import jalview.datamodel.SequenceI;
  import jalview.io.FeaturesFile;
 +import jalview.schemes.Colour;
  import jalview.schemes.FeatureColour;
  import jalview.schemes.UserColourScheme;
 +import jalview.util.ColorUtils;
  import jalview.util.MessageManager;
  import jalview.viewmodel.AlignmentViewport;
  
@@@ -60,18 -58,22 +60,10 @@@ import java.util.Hashtable
  public class FeatureRenderer extends
          jalview.renderer.seqfeatures.FeatureRenderer
  {
-   /**
-    * Creates a new FeatureRenderer object.
-    * 
-    * @param av
-    *          DOCUMENT ME!
-    */
-   public FeatureRenderer(AlignmentViewport av)
-   {
-     super();
-     this.av = av;
-   }
 -
+   // Holds web links for feature groups and feature types
+   // in the form label|link
+   Hashtable featureLinks = null;
  
 -  /**
 -   * Creates a new FeatureRenderer object.
 -   * 
 -   * @param av
 -   */
 -  public FeatureRenderer(AlignmentViewport av)
 -  {
 -    super(av);
 -
 -  }
 -
    static String lastFeatureAdded;
  
    static String lastFeatureGroupAdded;
  
    FeatureColourPanel colourPanel;
  
++  /**
++   * Creates a new FeatureRenderer object.
++   * 
++   * @param av
++   */
++  public FeatureRenderer(AlignmentViewport av)
++  {
++    super(av);
++  }
++
    class FeatureColourPanel extends Panel
    {
      String label = "";
        String vlabel = "";
        if (newcol.isSimpleColour())
        {
 -        bg = newcol.getColour();
 +        bg = ColorUtils.getColor(newcol.getColour());
          setBackground(bg);
        }
        else
          }
          else
          {
 -          setBackground(bg = newcol.getMinColour());
 -          maxCol = newcol.getMaxColour();
 +          setBackground(bg = ColorUtils.getColor(newcol.getMinColour()));
 +          maxCol = ColorUtils.getColor(newcol.getMaxColour());
          }
        }
        label = vlabel;
            {
              Color generatedColour = UserColourScheme
                      .createColourFromName(name.getText());
 -            col = new FeatureColour(generatedColour);
 +            col = new FeatureColour(new Colour(generatedColour));
            }
  
            colourPanel.updateColor(col);
  
      tmp = new Panel();
      panel.add(tmp);
-     tmp.add(new Label("Name: ", Label.RIGHT));
+     tmp.add(new Label(MessageManager.getString("label.name:"), Label.RIGHT));
      tmp.add(name);
  
      tmp = new Panel();
      panel.add(tmp);
-     tmp.add(new Label("Group: ", Label.RIGHT));
+     tmp.add(new Label(MessageManager.getString("label.group:"), Label.RIGHT));
      tmp.add(source);
  
      tmp = new Panel();
      panel.add(tmp);
-     tmp.add(new Label("Colour: ", Label.RIGHT));
+     tmp.add(new Label(MessageManager.getString("label.colour"), Label.RIGHT));
      tmp.add(colourPanel);
  
      bigPanel.add(panel, BorderLayout.NORTH);
  
      panel = new Panel();
-     panel.add(new Label("Description: ", Label.RIGHT));
+     panel.add(new Label(MessageManager.getString("label.description:"),
+             Label.RIGHT));
      panel.add(new ScrollPane().add(description));
  
      if (!newFeatures)
        bigPanel.add(panel, BorderLayout.SOUTH);
  
        panel = new Panel();
-       panel.add(new Label(" Start:", Label.RIGHT));
+       panel.add(new Label(MessageManager.getString("label.start"),
+               Label.RIGHT));
        panel.add(start);
-       panel.add(new Label("  End:", Label.RIGHT));
+       panel.add(new Label(MessageManager.getString("label.end"),
+               Label.RIGHT));
        panel.add(end);
        bigPanel.add(panel, BorderLayout.CENTER);
      }
          if (!colourPanel.isGcol)
          {
            // update colour - otherwise its already done.
 -          setColour(sf.type, new FeatureColour(colourPanel.getBackground()));
 +          setColour(
 +                  sf.type,
 +                  new FeatureColour(new Colour(colourPanel.getBackground())));
          }
          try
          {
          {
            setGroupVisibility(lastFeatureGroupAdded, true);
          }
 -        setColour(lastFeatureAdded, new FeatureColour(newColour)); // was fcol
 +        setColour(lastFeatureAdded,
 +                new FeatureColour(new Colour(newColour))); // was fcol
          setVisible(lastFeatureAdded);
          findAllFeatures(false); // different to original applet behaviour ?
          // findAllFeatures();
@@@ -24,7 -24,6 +24,7 @@@ import jalview.api.FeatureColourI
  import jalview.api.FeatureSettingsControllerI;
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.SequenceFeature;
 +import jalview.util.ColorUtils;
  import jalview.util.MessageManager;
  
  import java.awt.BorderLayout;
@@@ -193,7 -192,7 +193,7 @@@ public class FeatureSettings extends Pa
      final FeatureColourI typeCol = fr.getFeatureStyle(type);
      PopupMenu men = new PopupMenu(MessageManager.formatMessage(
              "label.settings_for_type", new String[] { type }));
 -    java.awt.MenuItem scr = new MenuItem(
 +    MenuItem scr = new MenuItem(
              MessageManager.getString("label.sort_by_score"));
      men.add(scr);
      final FeatureSettings me = this;
        FeatureColourI fcol = fr.getFeatureStyle(check.type);
        if (fcol.isSimpleColour())
        {
 -        new UserDefinedColours(this, check.type, fcol.getColour());
 +        new UserDefinedColours(this, check.type, ColorUtils.getColor(fcol
 +                .getColour()));
        }
        else
        {
        col = newcol;
        if (col.isSimpleColour())
        {
 -        setBackground(col.getColour());
 +        setBackground(ColorUtils.getColor(col.getColour()));
        }
        else
        {
          }
          else
          {
 -          setBackground(col.getMinColour());
 +          setBackground(ColorUtils.getColor(col.getMinColour()));
          }
          this.setLabel(vlabel);
        }
      public void paint(Graphics g)
      {
        Dimension d = getSize();
-       if (col.isColourByLabel())
+       if (col != null)
        {
-         g.setColor(Color.white);
-         g.fillRect(d.width / 2, 0, d.width / 2, d.height);
-         /*
-          * g.setColor(Color.black); Font f=g.getFont().deriveFont(9);
-          * g.setFont(f);
-          * 
-          * // g.setFont(g.getFont().deriveFont( //
-          * AffineTransform.getScaleInstance( //
-          * width/g.getFontMetrics().stringWidth("Label"), //
-          * height/g.getFontMetrics().getHeight()))); g.drawString("Label",
-          * width/2, 0);
-          */
-       }
-       else if (col.isGraduatedColour())
-       {
-         Color maxCol = ColorUtils.getColor(col.getMaxColour());
-         g.setColor(maxCol);
-         g.fillRect(d.width / 2, 0, d.width / 2, d.height);
+         if (col.isColourByLabel())
+         {
+           g.setColor(Color.white);
+           g.fillRect(d.width / 2, 0, d.width / 2, d.height);
+           /*
+            * g.setColor(Color.black); Font f=g.getFont().deriveFont(9);
+            * g.setFont(f);
+            * 
+            * // g.setFont(g.getFont().deriveFont( //
+            * AffineTransform.getScaleInstance( //
+            * width/g.getFontMetrics().stringWidth("Label"), //
+            * height/g.getFontMetrics().getHeight()))); g.drawString("Label",
+            * width/2, 0);
+            */
 -
+         }
+         else if (col.isGraduatedColour())
+         {
 -          Color maxCol = col.getMaxColour();
++          Color maxCol = ColorUtils.getColor(col.getMaxColour());
+           g.setColor(maxCol);
+           g.fillRect(d.width / 2, 0, d.width / 2, d.height);
 -
+         }
        }
  
        if (hasLink)
      }
    }
  
-   @Override
-   public void mousePressed(MouseEvent e)
-   {
-   }
    /**
     * Hide columns containing (or not containing) a given feature type
     * 
      }
    }
  
+   @Override
+   public void mousePressed(MouseEvent e)
+   {
+     // TODO Auto-generated method stub
+   }
  }
@@@ -21,7 -21,6 +21,7 @@@
  package jalview.appletgui;
  
  import jalview.datamodel.AlignmentI;
 +import jalview.util.ColorUtils;
  
  import java.awt.Color;
  import java.awt.Dimension;
@@@ -266,7 -265,8 +266,8 @@@ public class OverviewPanel extends Pane
    {
      miniMe = null;
      int alwidth = av.getAlignment().getWidth();
-     int alheight = av.getAlignment().getHeight();
+     int alheight = av.getAlignment().getHeight()
+             + av.getAlignment().getHiddenSequences().getSize();
  
      if (av.isShowSequenceFeatures())
      {
      AlignmentI alignment = av.getAlignment();
      for (row = 0; row <= sequencesHeight; row++)
      {
+       if (resizeAgain)
+       {
+         break;
+       }
        if ((int) (row * sampleRow) == lastrow)
        {
          sameRow++;
  
          if (seq.getLength() > lastcol)
          {
 -          color = sr.getResidueBoxColour(seq, lastcol);
 +          color = ColorUtils.getColor(sr.getResidueBoxColour(seq, lastcol));
  
            if (av.isShowSequenceFeatures())
            {
 -            color = fr.findFeatureColour(color, seq, lastcol);
 +            color = ColorUtils.getColor(fr.findFeatureColour(color, seq,
 +                    lastcol));
            }
          }
          else
      {
        for (col = 0; col < width; col++)
        {
+         if (resizeAgain)
+         {
+           break;
+         }
          lastcol = (int) (col * sampleCol);
          {
            mg.translate(col, sequencesHeight);
@@@ -24,7 -24,8 +24,9 @@@ import jalview.datamodel.AlignmentI
  import jalview.datamodel.SearchResults;
  import jalview.datamodel.SequenceGroup;
  import jalview.datamodel.SequenceI;
+ import jalview.renderer.ScaleRenderer;
+ import jalview.renderer.ScaleRenderer.ScaleMark;
 +import jalview.util.ColorUtils;
  import jalview.viewmodel.AlignmentViewport;
  
  import java.awt.Color;
@@@ -91,26 -92,30 +93,30 @@@ public class SeqCanvas extends Pane
  
    private void drawNorthScale(Graphics g, int startx, int endx, int ypos)
    {
-     int scalestartx = startx - startx % 10 + 10;
+     updateViewport();
      g.setColor(Color.black);
-     // NORTH SCALE
-     for (int i = scalestartx; i < endx; i += 10)
+     for (ScaleMark mark : new ScaleRenderer().calculateMarks(av, startx,
+             endx))
      {
-       int value = i;
-       if (av.hasHiddenColumns())
+       int mpos = mark.column; // (i - startx - 1)
+       if (mpos < 0)
        {
-         value = av.getColumnSelection().adjustForHiddenColumns(value);
+         continue;
        }
+       String mstring = mark.text;
  
-       g.drawString(String.valueOf(value), (i - startx - 1) * avcharWidth,
-               ypos - (avcharHeight / 2));
-       g.drawLine(((i - startx - 1) * avcharWidth) + (avcharWidth / 2),
-               (ypos + 2) - (avcharHeight / 2),
-               ((i - startx - 1) * avcharWidth) + (avcharWidth / 2),
-               ypos - 2);
+       if (mark.major)
+       {
+         if (mstring != null)
+         {
+           g.drawString(mstring, mpos * avcharWidth, ypos
+                   - (avcharHeight / 2));
+         }
+         g.drawLine((mpos * avcharWidth) + (avcharWidth / 2), (ypos + 2)
+                 - (avcharHeight / 2), (mpos * avcharWidth)
+                 + (avcharWidth / 2),
+                 ypos - 2);
+       }
      }
    }
  
                }
                else
                {
 -                g.setColor(group.getOutlineColour());
 +                g.setColor(ColorUtils.getColor(group.getOutlineColour()));
                }
              }
            }
   */
  package jalview.appletgui;
  
 +import jalview.api.ColorI;
  import jalview.api.FeatureRenderer;
  import jalview.datamodel.SequenceGroup;
  import jalview.datamodel.SequenceI;
 +import jalview.schemes.Colour;
  import jalview.schemes.ColourSchemeI;
  
  import java.awt.Color;
@@@ -34,6 -32,8 +34,8 @@@ import java.awt.Graphics
  
  public class SequenceRenderer implements jalview.api.SequenceRenderer
  {
+   final static int CHAR_TO_UPPER = 'A' - 'a';
    AlignViewport av;
  
    FontMetrics fm;
@@@ -70,7 -70,7 +72,7 @@@
    }
  
    @Override
 -  public Color getResidueBoxColour(SequenceI seq, int i)
 +  public ColorI getResidueBoxColour(SequenceI seq, int i)
    {
      allGroups = av.getAlignment().findAllGroups(seq);
  
@@@ -86,7 -86,7 +88,7 @@@
        getBoxColour(av.getGlobalColourScheme(), seq, i);
      }
  
 -    return resBoxColour;
 +    return new Colour(resBoxColour);
    }
  
    /**
     * @return
     */
    @Override
 -  public Color getResidueColour(final SequenceI seq, int position,
 +  public ColorI getResidueColour(final SequenceI seq, int position,
            FeatureRenderer fr)
    {
      // TODO replace 8 or so code duplications with calls to this method
      // (refactored as needed)
 -    Color col = getResidueBoxColour(seq, position);
 +    ColorI col = getResidueBoxColour(seq, position);
  
      if (fr != null)
      {
          }
          if (currentSequenceGroup.getShowNonconserved())
          {
-           s = getDisplayChar(srep, i, s, '.');
+           s = getDisplayChar(srep, i, s, '.', currentSequenceGroup);
          }
        }
        else
          }
          if (av.getShowUnconserved())
          {
-           s = getDisplayChar(srep, i, s, '.');
+           s = getDisplayChar(srep, i, s, '.', null);
  
          }
        }
  
    }
  
-   private char getDisplayChar(final boolean usesrep, int position, char s,
-           char c)
+   /**
+    * Returns 'conservedChar' to represent the given position if the sequence
+    * character at that position is equal to the consensus (ignoring case), else
+    * returns the sequence character
+    * 
+    * @param usesrep
+    * @param position
+    * @param sequenceChar
+    * @param conservedChar
+    * @return
+    */
+   private char getDisplayChar(final boolean usesrep, int position,
+           char sequenceChar, char conservedChar, SequenceGroup currentGroup)
    {
      // TODO - use currentSequenceGroup rather than alignment
      // currentSequenceGroup.getConsensus()
-     char conschar = (usesrep) ? av.getAlignment().getSeqrep()
-             .getCharAt(position)
-             : av.getAlignmentConsensusAnnotation().annotations[position].displayCharacter
-                     .charAt(0);
-     if (!jalview.util.Comparison.isGap(conschar) && s == conschar)
+     char conschar = (usesrep) ? (currentGroup == null
+             || position < currentGroup.getStartRes()
+             || position > currentGroup.getEndRes() ? av.getAlignment()
+             .getSeqrep().getCharAt(position)
+             : (currentGroup.getSeqrep() != null ? currentGroup.getSeqrep()
+                     .getCharAt(position) : av.getAlignment().getSeqrep()
+                     .getCharAt(position)))
+             : (currentGroup != null && currentGroup.getConsensus() != null
+                     && position >= currentGroup.getStartRes()
+                     && position <= currentGroup.getEndRes() && currentGroup
+                     .getConsensus().annotations.length > position) ? currentGroup
+                     .getConsensus().annotations[position].displayCharacter
+                     .charAt(0)
+                     : av.getAlignmentConsensusAnnotation().annotations[position].displayCharacter
+                             .charAt(0);
+     if (!jalview.util.Comparison.isGap(conschar)
+             && (sequenceChar == conschar || sequenceChar + CHAR_TO_UPPER == conschar))
      {
-       s = c;
+       sequenceChar = conservedChar;
      }
-     return s;
+     return sequenceChar;
    }
  
    boolean inCurrentSequenceGroup(int res)
@@@ -27,12 -27,10 +27,12 @@@ import jalview.datamodel.Sequence
  import jalview.datamodel.SequenceGroup;
  import jalview.datamodel.SequenceI;
  import jalview.datamodel.SequenceNode;
 +import jalview.schemes.Colour;
  import jalview.schemes.ColourSchemeI;
  import jalview.schemes.ColourSchemeProperty;
  import jalview.schemes.ResidueProperties;
  import jalview.schemes.UserColourScheme;
 +import jalview.util.ColorUtils;
  import jalview.util.Format;
  import jalview.util.MappingUtils;
  import jalview.viewmodel.AlignmentViewport;
@@@ -124,13 -122,13 +124,13 @@@ public class TreeCanvas extends Panel i
      tree.findHeight(tree.getTopNode());
  
      // Now have to calculate longest name based on the leaves
-     Vector leaves = tree.findLeaves(tree.getTopNode(), new Vector());
+     Vector<SequenceNode> leaves = tree.findLeaves(tree.getTopNode());
      boolean has_placeholders = false;
      longestName = "";
  
      for (int i = 0; i < leaves.size(); i++)
      {
-       SequenceNode lf = (SequenceNode) leaves.elementAt(i);
+       SequenceNode lf = leaves.elementAt(i);
  
        if (lf.isPlaceholder())
        {
          }
          else
          {
 -          g.setColor(av.getSequenceColour(seq).darker());
 +          g.setColor(ColorUtils.getColor(av.getSequenceColour(seq))
 +                  .darker());
          }
  
        }
  
        if (node.element() instanceof SequenceI)
        {
 -        av.setSequenceColour((SequenceI) node.element(), c);
 +        av.setSequenceColour((SequenceI) node.element(), new Colour(c));
        }
      }
      else
        }
        else
        {
-         Vector leaves = new Vector();
-         tree.findLeaves(highlightNode, leaves);
+         Vector<SequenceNode> leaves = tree.findLeaves(highlightNode);
  
          for (int i = 0; i < leaves.size(); i++)
          {
-           SequenceI seq = (SequenceI) ((SequenceNode) leaves.elementAt(i))
+           SequenceI seq = (SequenceI) leaves.elementAt(i)
                    .element();
            treeSelectionChanged(seq);
          }
                (int) (Math.random() * 255), (int) (Math.random() * 255));
        setColor((SequenceNode) tree.getGroups().elementAt(i), col.brighter());
  
-       Vector l = tree.findLeaves(
-               (SequenceNode) tree.getGroups().elementAt(i), new Vector());
+       Vector<SequenceNode> l = tree.findLeaves((SequenceNode) tree
+               .getGroups().elementAt(i));
  
-       Vector sequences = new Vector();
+       Vector<SequenceI> sequences = new Vector<SequenceI>();
        for (int j = 0; j < l.size(); j++)
        {
-         SequenceI s1 = (SequenceI) ((SequenceNode) l.elementAt(j))
+         SequenceI s1 = (SequenceI) l.elementAt(j)
                  .element();
          if (!sequences.contains(s1))
          {
            for (SequenceI seq : mappedGroup.getSequences())
            {
              // TODO why does gui require col.brighter() here??
 -            codingComplement.setSequenceColour(seq, col);
 +            codingComplement.setSequenceColour(seq, new Colour(col));
            }
          }
        }
@@@ -25,7 -25,6 +25,7 @@@ import jalview.api.AlignViewControllerG
  import jalview.api.AlignViewControllerI;
  import jalview.api.AlignViewportI;
  import jalview.api.AlignmentViewPanel;
 +import jalview.api.ColorI;
  import jalview.api.FeatureRenderer;
  import jalview.commands.OrderCommand;
  import jalview.datamodel.AlignmentI;
@@@ -35,7 -34,6 +35,7 @@@ import jalview.datamodel.SequenceFeatur
  import jalview.datamodel.SequenceGroup;
  import jalview.datamodel.SequenceI;
  import jalview.io.FeaturesFile;
 +import jalview.schemes.Colour;
  import jalview.util.MessageManager;
  
  import java.awt.Color;
@@@ -114,10 -112,10 +114,10 @@@ public class AlignViewController implem
          viewport.getAlignment().addGroup(gps[g]);
          Color col = new Color((int) (Math.random() * 255),
                  (int) (Math.random() * 255), (int) (Math.random() * 255));
 -        col = col.brighter();
 +        ColorI newcol = new Colour(col.brighter());
          for (SequenceI sq : gps[g].getSequences(null))
          {
 -          viewport.setSequenceColour(sq, col);
 +          viewport.setSequenceColour(sq, newcol);
          }
        }
        return true;
      // JBPNote this routine could also mark rows, not just columns.
      // need a decent query structure to allow all types of feature searches
      BitSet bs = new BitSet();
-     int alw, alStart;
-     SequenceCollectionI sqcol = (viewport.getSelectionGroup() == null ? viewport
-             .getAlignment() : viewport.getSelectionGroup());
-     alStart = sqcol.getStartRes();
-     alw = sqcol.getEndRes() + 1;
+     SequenceCollectionI sqcol = (viewport.getSelectionGroup() == null || extendCurrent) ? viewport
+             .getAlignment() : viewport.getSelectionGroup();
+     int nseq = findColumnsWithFeature(featureType, sqcol, bs);
+     ColumnSelection cs = viewport.getColumnSelection();
+     if (cs == null)
+     {
+       cs = new ColumnSelection();
+     }
+     if (bs.cardinality() > 0 || invert)
+     {
+       boolean changed = cs.markColumns(bs, sqcol.getStartRes(),
+               sqcol.getEndRes(), invert, extendCurrent, toggle);
+       if (changed)
+       {
+         viewport.setColumnSelection(cs);
+         alignPanel.paintAlignment(true);
+         int columnCount = invert ? (sqcol.getEndRes() - sqcol.getStartRes() + 1)
+                 - bs.cardinality()
+                 : bs.cardinality();
+         avcg.setStatus(MessageManager.formatMessage(
+                 "label.view_controller_toggled_marked",
+                 new String[] {
+                     toggle ? MessageManager.getString("label.toggled")
+                             : MessageManager.getString("label.marked"),
+                     String.valueOf(columnCount),
+                     invert ? MessageManager
+                             .getString("label.not_containing")
+                             : MessageManager.getString("label.containing"),
+                     featureType, Integer.valueOf(nseq).toString() }));
+         return true;
+       }
+     }
+     else
+     {
+       avcg.setStatus(MessageManager.formatMessage(
+               "label.no_feature_of_type_found",
+               new String[] { featureType }));
+       if (!extendCurrent)
+       {
+         cs.clear();
+         alignPanel.paintAlignment(true);
+       }
+     }
+     return false;
+   }
+   /**
+    * Sets a bit in the BitSet for each column (base 0) in the sequence
+    * collection which includes the specified feature type. Returns the number of
+    * sequences which have the feature in the selected range.
+    * 
+    * @param featureType
+    * @param sqcol
+    * @param bs
+    * @return
+    */
+   static int findColumnsWithFeature(String featureType,
+           SequenceCollectionI sqcol, BitSet bs)
+   {
+     final int startPosition = sqcol.getStartRes() + 1; // converted to base 1
+     final int endPosition = sqcol.getEndRes() + 1;
      List<SequenceI> seqs = sqcol.getSequences();
      int nseq = 0;
      for (SequenceI sq : seqs)
      {
-       int tfeat = 0;
+       boolean sequenceHasFeature = false;
        if (sq != null)
        {
-         SequenceFeature[] sf = sq.getSequenceFeatures();
-         if (sf != null)
+         SequenceFeature[] sfs = sq.getSequenceFeatures();
+         if (sfs != null)
          {
+           /*
+            * check whether the feature start/end (base 1) 
+            * overlaps the selection start/end
+            */
            int ist = sq.findIndex(sq.getStart());
            int iend = sq.findIndex(sq.getEnd());
-           if (iend < alStart || ist > alw)
+           if (iend < startPosition || ist > endPosition)
            {
              // sequence not in region
              continue;
            }
-           for (SequenceFeature sfpos : sf)
+           for (SequenceFeature sf : sfs)
            {
-             // future functionalty - featureType == null means mark columns
+             // future functionality - featureType == null means mark columns
              // containing all displayed features
-             if (sfpos != null && (featureType.equals(sfpos.getType())))
+             if (sf != null && (featureType.equals(sf.getType())))
              {
-               tfeat++;
                // optimisation - could consider 'spos,apos' like cursor argument
                // - findIndex wastes time by starting from first character and
                // counting
  
-               int i = sq.findIndex(sfpos.getBegin());
-               int j = sq.findIndex(sfpos.getEnd());
-               if (j < alStart || i > alw)
+               int i = sq.findIndex(sf.getBegin());
+               int j = sq.findIndex(sf.getEnd());
+               if (j < startPosition || i > endPosition)
                {
                  // feature is outside selected region
                  continue;
                }
-               if (i < alStart)
+               sequenceHasFeature = true;
+               if (i < startPosition)
                {
-                 i = alStart;
+                 i = startPosition;
                }
                if (i < ist)
                {
                  i = ist;
                }
-               if (j > alw)
+               if (j > endPosition)
                {
-                 j = alw;
+                 j = endPosition;
                }
                for (; i <= j; i++)
                {
-                 bs.set(i - 1);
+                 bs.set(i - 1); // convert to base 0
                }
              }
            }
          }
  
-         if (tfeat > 0)
+         if (sequenceHasFeature)
          {
            nseq++;
          }
        }
      }
-     ColumnSelection cs = viewport.getColumnSelection();
-     if (bs.cardinality() > 0 || invert)
-     {
-       boolean changed = false;
-       if (cs == null)
-       {
-         cs = new ColumnSelection();
-       }
-       else
-       {
-         if (!extendCurrent)
-         {
-           changed = !cs.isEmpty();
-           cs.clear();
-         }
-       }
-       if (invert)
-       {
-         // invert only in the currently selected sequence region
-         for (int i = bs.nextClearBit(alStart), ibs = bs.nextSetBit(alStart); i >= alStart
-                 && i < (alw);)
-         {
-           if (ibs < 0 || i < ibs)
-           {
-             changed = true;
-             if (toggle && cs.contains(i))
-             {
-               cs.removeElement(i++);
-             }
-             else
-             {
-               cs.addElement(i++);
-             }
-           }
-           else
-           {
-             i = bs.nextClearBit(ibs);
-             ibs = bs.nextSetBit(i);
-           }
-         }
-       }
-       else
-       {
-         for (int i = bs.nextSetBit(alStart); i >= alStart; i = bs
-                 .nextSetBit(i + 1))
-         {
-           changed = true;
-           if (toggle && cs.contains(i))
-           {
-             cs.removeElement(i);
-           }
-           else
-           {
-             cs.addElement(i);
-           }
-         }
-       }
-       if (changed)
-       {
-         viewport.setColumnSelection(cs);
-         alignPanel.paintAlignment(true);
-         avcg.setStatus(MessageManager.formatMessage(
-                 "label.view_controller_toggled_marked",
-                 new String[] {
-                     (toggle ? MessageManager.getString("label.toggled")
-                             : MessageManager.getString("label.marked")),
-                     (invert ? (Integer.valueOf((alw - alStart)
-                             - bs.cardinality()).toString()) : (Integer
-                             .valueOf(bs.cardinality()).toString())),
-                     featureType, Integer.valueOf(nseq).toString() }));
-         return true;
-       }
-     }
-     else
-     {
-       avcg.setStatus(MessageManager.formatMessage(
-               "label.no_feature_of_type_found",
-               new String[] { featureType }));
-       if (!extendCurrent && cs != null)
-       {
-         cs.clear();
-         alignPanel.paintAlignment(true);
-       }
-     }
-     return false;
+     return nseq;
    }
  
    @Override
@@@ -22,8 -22,6 +22,8 @@@ package jalview.datamodel
  
  import jalview.analysis.AAFrequency;
  import jalview.analysis.Conservation;
 +import jalview.api.ColorI;
 +import jalview.schemes.Colour;
  import jalview.schemes.ColourSchemeI;
  import jalview.schemes.ResidueProperties;
  
@@@ -84,15 -82,15 +84,15 @@@ public class SequenceGroup implements A
    // end column (base 0)
    int endRes = 0;
  
 -  public Color outlineColour = Color.black;
 +  public ColorI outlineColour = Colour.black;
  
 -  public Color idColour = null;
 +  public ColorI idColour = null;
  
    public int thresholdTextColour = 0;
  
 -  public Color textColour = Color.black;
 +  public ColorI textColour = Colour.black;
  
 -  public Color textColour2 = Color.white;
 +  public ColorI textColour2 = Colour.white;
  
    /**
     * consensus calculation property
    }
  
    /**
-    * calculate residue conservation for group - but only if necessary.
+    * calculate residue conservation and colourschemes for group - but only if
+    * necessary. returns true if the calculation resulted in a visible change to
+    * group
     */
-   public void recalcConservation()
+   public boolean recalcConservation()
+   {
+     return recalcConservation(false);
+   }
+   /**
+    * calculate residue conservation for group - but only if necessary. returns
+    * true if the calculation resulted in a visible change to group
+    * 
+    * @param defer
+    *          when set, colourschemes for this group are not refreshed after
+    *          recalculation
+    */
+   public boolean recalcConservation(boolean defer)
    {
      if (cs == null && consensus == null && conservation == null)
      {
-       return;
+       return false;
      }
+     // TODO: try harder to detect changes in state in order to minimise
+     // recalculation effort
+     boolean upd = false;
      try
      {
        Hashtable cnsns[] = AAFrequency.calculate(sequences, startRes,
        if (consensus != null)
        {
          _updateConsensusRow(cnsns, sequences.size());
+         upd = true;
        }
        if (cs != null)
        {
          cs.setConsensus(cnsns);
+         upd = true;
        }
  
        if ((conservation != null)
              cs.setConservation(c);
            }
          }
+         // eager update - will cause a refresh of overview regardless
+         upd = true;
        }
-       if (cs != null)
+       if (cs != null && !defer)
        {
+         // TODO: JAL-2034 should cs.alignmentChanged modify return state
          cs.alignmentChanged(context != null ? context : this, null);
+         return true;
+       }
+       else
+       {
+         return upd;
        }
      } catch (java.lang.OutOfMemoryError err)
      {
        // TODO: catch OOM
        System.out.println("Out of memory loading groups: " + err);
      }
+     return upd;
    }
  
    private void _updateConservationRow(Conservation c)
     */
    public void setOutlineColour(Color c)
    {
 -    outlineColour = c;
 +    outlineColour = new Colour(c);
    }
  
    /**
     * 
     * @return DOCUMENT ME!
     */
 -  public Color getOutlineColour()
 +  public ColorI getOutlineColour()
    {
      return outlineColour;
    }
    /**
     * @return the idColour
     */
 -  public Color getIdColour()
 +  public ColorI getIdColour()
    {
      return idColour;
    }
     */
    public void setIdColour(Color idColour)
    {
 -    this.idColour = idColour;
 +    this.idColour = new Colour(idColour);
    }
  
    /**
  
    /**
     * 
-    * @return automatically calculated consensus row
+    * @return automatically calculated consensus row note: the row is a stub if a
+    *         consensus calculation has not yet been performed on the group
     */
    public AlignmentAnnotation getConsensus()
    {
@@@ -1,6 -1,5 +1,6 @@@
  package jalview.ext.ensembl;
  
 +import jalview.api.ColorI;
  import jalview.api.FeatureColourI;
  import jalview.api.FeatureSettingsModelI;
  import jalview.datamodel.AlignmentI;
@@@ -9,11 -8,11 +9,11 @@@ import jalview.datamodel.SequenceFeatur
  import jalview.datamodel.SequenceI;
  import jalview.io.gff.SequenceOntologyFactory;
  import jalview.io.gff.SequenceOntologyI;
 -import jalview.schemes.FeatureColour;
 +import jalview.schemes.Colour;
 +import jalview.schemes.FeatureColourAdapter;
  import jalview.schemes.FeatureSettingsAdapter;
  import jalview.util.MapList;
  
 -import java.awt.Color;
  import java.io.UnsupportedEncodingException;
  import java.net.URLDecoder;
  import java.util.ArrayList;
@@@ -119,7 -118,10 +119,10 @@@ public class EnsemblGene extends Ensemb
         * fetch the gene sequence(s) with features and xrefs
         */
        AlignmentI geneAlignment = super.getSequenceRecords(geneId);
+       if (geneAlignment == null)
+       {
+         continue;
+       }
        if (geneAlignment.getHeight() == 1)
        {
          getTranscripts(geneAlignment, geneId);
         */
        else
        {
-         List<String> ids = new EnsemblSymbol(getDomain()).getIds(acc);
+         List<String> ids = new EnsemblSymbol(getDomain(), getDbSource(),
+                 getDbVersion()).getIds(acc);
          for (String geneId : ids)
          {
            if (!geneIds.contains(geneId))
     */
    protected String getGeneIdentifiersForName(String query)
    {
-     List<String> ids = new EnsemblSymbol(getDomain()).getIds(query);
+     List<String> ids = new EnsemblSymbol(getDomain(), getDbSource(),
+             getDbVersion()).getIds(query);
      if (ids != null)
      {
        for (String id : ids)
        {
          if (so.isA(type, SequenceOntologyI.EXON))
          {
 -          return new FeatureColour()
 +          return new FeatureColourAdapter()
            {
              @Override
              public boolean isColourByLabel()
          }
          if (so.isA(type, SequenceOntologyI.SEQUENCE_VARIANT))
          {
 -          return new FeatureColour()
 +          return new FeatureColourAdapter()
            {
  
              @Override
 -            public Color getColour()
 +            public ColorI getColour()
              {
 -              return Color.RED;
 +              return Colour.red;
              }
            };
          }
@@@ -33,7 -33,6 +33,7 @@@ import jalview.jbgui.GAlignmentPanel
  import jalview.math.AlignmentDimension;
  import jalview.schemes.ResidueProperties;
  import jalview.structure.StructureSelectionManager;
 +import jalview.util.ColorUtils;
  import jalview.util.MessageManager;
  import jalview.util.Platform;
  
@@@ -1069,7 -1068,7 +1069,7 @@@ public class AlignmentPanel extends GAl
        }
        else
        {
 -        currentColor = av.getSequenceColour(seq);
 +        currentColor = ColorUtils.getColor(av.getSequenceColour(seq));
          currentTextColor = Color.black;
        }
  
  
    void makeAlignmentImage(jalview.util.ImageMaker.TYPE type, File file)
    {
+     int boarderBottomOffset = 5;
      long pSessionId = System.currentTimeMillis();
      headless = (System.getProperty("java.awt.headless") != null && System
              .getProperty("java.awt.headless").equals("true"));
          }
  
          im = new jalview.util.ImageMaker(this, type, imageAction,
-                 aDimension.getWidth(), aDimension.getHeight(), file,
+                 aDimension.getWidth(), aDimension.getHeight()
+                         + boarderBottomOffset, file,
                  imageTitle, alignFrame, pSessionId, headless);
          if (av.getWrapAlignment())
          {
            if (im.getGraphics() != null)
            {
              printWrappedAlignment(im.getGraphics(), aDimension.getWidth(),
-                     aDimension.getHeight(), 0);
+                     aDimension.getHeight() + boarderBottomOffset, 0);
              im.writeImage();
            }
          }
@@@ -22,9 -22,7 +22,9 @@@ package jalview.gui
  
  import jalview.api.FeatureColourI;
  import jalview.datamodel.GraphLine;
 +import jalview.schemes.Colour;
  import jalview.schemes.FeatureColour;
 +import jalview.util.ColorUtils;
  import jalview.util.MessageManager;
  
  import java.awt.BorderLayout;
@@@ -153,20 -151,17 +153,20 @@@ public class FeatureColourChooser exten
      else
      {
        // promote original color to a graduated color
 -      Color bl = oldcs.getColour();
 +      Color bl = ColorUtils.getColor(oldcs.getColour());
        if (bl == null)
        {
          bl = Color.BLACK;
        }
        // original colour becomes the maximum colour
 -      cs = new FeatureColour(Color.white, bl, mm[0], mm[1]);
 +      cs = new FeatureColour(new Colour(Color.white), new Colour(bl),
 +              mm[0], mm[1]);
        cs.setColourByLabel(false);
      }
 -    minColour.setBackground(oldminColour = cs.getMinColour());
 -    maxColour.setBackground(oldmaxColour = cs.getMaxColour());
 +    minColour.setBackground(oldminColour = ColorUtils.getColor(cs
 +            .getMinColour()));
 +    maxColour.setBackground(oldmaxColour = ColorUtils.getColor(cs
 +            .getMaxColour()));
      adjusting = true;
  
      try
      threshold.setToolTipText(MessageManager
              .getString("label.threshold_feature_display_by_score"));
      threshold.addItem(MessageManager
-             .getString("label.threshold_feature_no_thereshold")); // index 0
+             .getString("label.threshold_feature_no_threshold")); // index 0
      threshold.addItem(MessageManager
-             .getString("label.threshold_feature_above_thereshold")); // index 1
+             .getString("label.threshold_feature_above_threshold")); // index 1
      threshold.addItem(MessageManager
-             .getString("label.threshold_feature_below_thereshold")); // index 2
+             .getString("label.threshold_feature_below_threshold")); // index 2
      jPanel3.setLayout(flowLayout2);
      thresholdValue.addActionListener(new ActionListener()
      {
      slider.setOpaque(false);
      slider.setPreferredSize(new Dimension(100, 32));
      slider.setToolTipText(MessageManager
-             .getString("label.adjust_thereshold"));
+             .getString("label.adjust_threshold"));
      thresholdValue.setEnabled(false);
      thresholdValue.setColumns(7);
      jPanel3.setBackground(Color.white);
      FeatureColourI acg;
      if (cs.isColourByLabel())
      {
 -      acg = new FeatureColour(oldminColour, oldmaxColour, min, max);
 +      acg = new FeatureColour(new Colour(oldminColour), new Colour(
 +              oldmaxColour), min, max);
      }
      else
      {
 -      acg = new FeatureColour(oldminColour = minColour.getBackground(),
 -              oldmaxColour = maxColour.getBackground(), min, max);
 -
 +      acg = new FeatureColour(new Colour(
 +              oldminColour = minColour.getBackground()), new Colour(
 +              oldmaxColour = maxColour.getBackground()), min, max);
      }
  
      if (!hasThreshold)
@@@ -24,10 -24,8 +24,10 @@@ import jalview.api.FeatureColourI
  import jalview.datamodel.SearchResults;
  import jalview.datamodel.SequenceFeature;
  import jalview.datamodel.SequenceI;
 +import jalview.schemes.Colour;
  import jalview.schemes.FeatureColour;
  import jalview.schemes.UserColourScheme;
 +import jalview.util.ColorUtils;
  import jalview.util.MessageManager;
  
  import java.awt.BorderLayout;
@@@ -77,9 -75,8 +77,8 @@@ public class FeatureRenderer extend
     */
    public FeatureRenderer(AlignmentPanel ap)
    {
-     super();
+     super(ap.av);
      this.ap = ap;
-     this.av = ap.av;
      if (ap != null && ap.getSeqPanel() != null
              && ap.getSeqPanel().seqCanvas != null
              && ap.getSeqPanel().seqCanvas.fr != null)
          {
            Color col = JColorChooser.showDialog(Desktop.desktop,
                    MessageManager.getString("label.select_feature_colour"),
 -                  fcol.getColour());
 +                  ColorUtils.getColor(fcol.getColour()));
            if (col != null)
            {
 -            fcol = new FeatureColour(col);
 -            updateColourButton(bigPanel, colour, new FeatureColour(col));
 +            fcol = new FeatureColour(new Colour(col));
 +            updateColourButton(bigPanel, colour, fcol);
            }
          }
          else
      {
        panel = new JPanel(new GridLayout(4, 1));
        tmp = new JPanel();
-       tmp.add(new JLabel(MessageManager.getString("label.select_feature")));
+       tmp.add(new JLabel(MessageManager.getString("label.select_feature")
+               + ":"));
        overlaps = new JComboBox();
        for (int i = 0; i < features.length; i++)
        {
            FeatureColourI col = getFeatureStyle(name.getText());
            if (col == null)
            {
 -            col = new FeatureColour(UserColourScheme
 -                    .createColourFromName(name.getText()));
 +            col = new FeatureColour(new Colour(UserColourScheme
 +                    .createColourFromName(name.getText())));
            }
            oldcol = fcol = col;
            updateColourButton(bigPanel, colour, col);
  
      tmp = new JPanel();
      panel.add(tmp);
-     tmp.add(new JLabel(MessageManager.getString("label.name"), JLabel.RIGHT));
+     tmp.add(new JLabel(MessageManager.getString("label.name:"),
+             JLabel.RIGHT));
      tmp.add(name);
  
      tmp = new JPanel();
      panel.add(tmp);
-     tmp.add(new JLabel(MessageManager.getString("label.group") + ":",
+     tmp.add(new JLabel(MessageManager.getString("label.group:"),
              JLabel.RIGHT));
      tmp.add(source);
  
      bigPanel.add(panel, BorderLayout.NORTH);
  
      panel = new JPanel();
-     panel.add(new JLabel(MessageManager.getString("label.description"),
+     panel.add(new JLabel(MessageManager.getString("label.description:"),
              JLabel.RIGHT));
      description.setFont(JvSwingUtils.getTextAreaFont());
      description.setLineWrap(true);
     * update the amend feature button dependent on the given style
     * 
     * @param bigPanel
 +   * @param colour
     * @param col
++<<<<<<< HEAD
++=======
+    * @param col
++>>>>>>> refs/heads/develop
     */
    protected void updateColourButton(JPanel bigPanel, JLabel colour,
            FeatureColourI col)
  
      if (col.isSimpleColour())
      {
 -      colour.setBackground(col.getColour());
 +      colour.setBackground(ColorUtils.getColor(col.getColour()));
      }
      else
      {
@@@ -28,9 -28,7 +28,9 @@@ import jalview.datamodel.SequenceI
  import jalview.gui.Help.HelpId;
  import jalview.io.JalviewFileChooser;
  import jalview.schemabinding.version2.JalviewUserColours;
 +import jalview.schemes.Colour;
  import jalview.schemes.FeatureColour;
 +import jalview.util.ColorUtils;
  import jalview.util.Format;
  import jalview.util.MessageManager;
  import jalview.util.Platform;
@@@ -184,14 -182,16 +184,16 @@@ public class FeatureSettings extends JP
          }
          else if (evt.getClickCount() == 2)
          {
+           boolean invertSelection = evt.isAltDown();
+           boolean toggleSelection = Platform.isControlDown(evt);
+           boolean extendSelection = evt.isShiftDown();
            fr.ap.alignFrame.avc.markColumnsContainingFeatures(
-                   evt.isAltDown(), evt.isShiftDown() || evt.isMetaDown(),
-                   evt.isMetaDown(),
+                   invertSelection, extendSelection, toggleSelection,
                    (String) table.getValueAt(selectedRow, 0));
          }
        }
  
-       // isPopupTrigger fires on mouseReleased on Mac
+       // isPopupTrigger fires on mouseReleased on Windows
        @Override
        public void mouseReleased(MouseEvent evt)
        {
          int newRow = table.rowAtPoint(evt.getPoint());
          if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
          {
+           /*
+            * reposition 'selectedRow' to 'newRow' (the dragged to location)
+            * this could be more than one row away for a very fast drag action
+            * so just swap it with adjacent rows until we get it there
+            */
            Object[][] data = ((FeatureTableModel) table.getModel())
                    .getData();
-           Object[] temp = data[selectedRow];
-           data[selectedRow] = data[newRow];
-           data[newRow] = temp;
+           int direction = newRow < selectedRow ? -1 : 1;
+           for (int i = selectedRow; i != newRow; i += direction)
+           {
+             Object[] temp = data[i];
+             data[i] = data[i + direction];
+             data[i + direction] = temp;
+           }
            updateFeatureRenderer(data);
            table.repaint();
            selectedRow = newRow;
                          "Select new Colour", true, // modal
                          colorChooser, this, // OK button handler
                          null); // no CANCEL button handler
 -                colorChooser.setColor(featureColour.getMaxColour());
 +                colorChooser.setColor(ColorUtils.getColor(featureColour
 +                        .getMaxColour()));
                  dialog.setVisible(true);
                }
              }
                else
                {
                  // probably the color chooser!
-                 table.setValueAt(colorChooser.getColor(), selectedRow, 1);
+                 table.setValueAt(
+                         new FeatureColour(colorChooser.getColor()),
+                         selectedRow, 1);
                  table.validate();
                  me.updateFeatureRenderer(
                          ((FeatureTableModel) table.getModel()).getData(),
                Cache.log.warn("Couldn't parse out graduated feature color.",
                        e);
              }
 -            FeatureColourI gcol = new FeatureColour(mincol, maxcol,
 +            FeatureColourI gcol = new FeatureColour(new Colour(mincol),
 +                    new Colour(maxcol),
                      newcol.getMin(), newcol.getMax());
              if (newcol.hasAutoScale())
              {
              Color color = new Color(
                      Integer.parseInt(jucs.getColour(i).getRGB(), 16));
              fr.setColour(name = jucs.getColour(i).getName(),
 -                    new FeatureColour(color));
 +                    new FeatureColour(new Colour(color)));
            }
            fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
          }
    void save()
    {
      JalviewFileChooser chooser = new JalviewFileChooser(
-             jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
+             Cache.getProperty("LAST_DIRECTORY"),
              new String[] { "fc" },
              new String[] { "Sequence Feature Colours" },
              "Sequence Feature Colours");
        {
          this.setText("");
          this.setIcon(null);
 -        newColor = cellColour.getColour();
 +        newColor = ColorUtils.getColor(cellColour.getColour());
          setBackground(newColor);
-         // comp.setToolTipText("RGB value: " + newColor.getRed() + ", "
-         // + newColor.getGreen() + ", " + newColor.getBlue());
        }
        if (isSelected)
        {
      }
      else
      {
 -      Color newColor = gcol.getMaxColour();
 +      Color newColor = ColorUtils.getColor(gcol.getMaxColour());
        comp.setBackground(newColor);
        // System.err.println("Width is " + w / 2);
        Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
@@@ -1683,7 -1690,7 +1694,7 @@@ class FeatureIcon implements Ico
        g.setColor(backg);
        g.fillRect(0, 0, width, height);
        // need an icon here.
 -      g.setColor(gcol.getMaxColour());
 +      g.setColor(ColorUtils.getColor(gcol.getMaxColour()));
  
        g.setFont(new Font("Verdana", Font.PLAIN, 9));
  
      }
      else
      {
 -      Color minCol = gcol.getMinColour();
 +      Color minCol = ColorUtils.getColor(gcol.getMinColour());
        g.setColor(minCol);
        g.fillRect(0, 0, s1, height);
        if (midspace)
          g.setColor(Color.white);
          g.fillRect(s1, 0, e1 - s1, height);
        }
 -      g.setColor(gcol.getMaxColour());
 +      g.setColor(ColorUtils.getColor(gcol.getMaxColour()));
        g.fillRect(0, e1, width - e1, height);
      }
    }
@@@ -1764,9 -1771,8 +1775,9 @@@ class ColorEditor extends AbstractCellE
        if (currentColor.isSimpleColour())
        {
          // bring up simple color chooser
 -        button.setBackground(currentColor.getColour());
 -        colorChooser.setColor(currentColor.getColour());
 +        button.setBackground(ColorUtils.getColor(currentColor.getColour()));
 +        colorChooser
 +                .setColor(ColorUtils.getColor(currentColor.getColour()));
          dialog.setVisible(true);
        }
        else
      { // User pressed dialog's "OK" button.
        if (currentColor.isSimpleColour())
        {
 -        currentColor = new FeatureColour(colorChooser.getColor());
 +        currentColor = new FeatureColour(
 +                new Colour(colorChooser.getColor()));
        }
        else
        {
      {
        button.setText("");
        button.setIcon(null);
 -      button.setBackground(currentColor.getColour());
 +      button.setBackground(ColorUtils.getColor(currentColor.getColour()));
      }
      return button;
    }
@@@ -70,7 -70,6 +70,7 @@@ import jalview.schemabinding.version2.T
  import jalview.schemabinding.version2.UserColours;
  import jalview.schemabinding.version2.Viewport;
  import jalview.schemes.AnnotationColourGradient;
 +import jalview.schemes.Colour;
  import jalview.schemes.ColourSchemeI;
  import jalview.schemes.ColourSchemeProperty;
  import jalview.schemes.FeatureColour;
@@@ -81,6 -80,7 +81,7 @@@ import jalview.structure.StructureSelec
  import jalview.structures.models.AAStructureBindingModel;
  import jalview.util.MessageManager;
  import jalview.util.Platform;
+ import jalview.util.StringUtils;
  import jalview.util.jarInputStreamProvider;
  import jalview.viewmodel.AlignmentViewport;
  import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
@@@ -108,6 -108,7 +109,7 @@@ import java.lang.reflect.InvocationTarg
  import java.net.MalformedURLException;
  import java.net.URL;
  import java.util.ArrayList;
+ import java.util.Arrays;
  import java.util.Enumeration;
  import java.util.HashMap;
  import java.util.HashSet;
@@@ -119,7 -120,6 +121,6 @@@ import java.util.List
  import java.util.Map;
  import java.util.Map.Entry;
  import java.util.Set;
- import java.util.StringTokenizer;
  import java.util.Vector;
  import java.util.jar.JarEntry;
  import java.util.jar.JarInputStream;
@@@ -166,7 -166,9 +167,9 @@@ public class Jalview2XM
     */
    Map<String, SequenceI> seqRefIds = null;
  
-   Vector<Object[]> frefedSequence = null;
+   Map<String, SequenceI> incompleteSeqs = null;
+   List<SeqFref> frefedSequence = null;
  
    boolean raiseGUI = true; // whether errors are raised in dialog boxes or not
  
        {
          seqsToIds.clear();
        }
+       if (incompleteSeqs != null)
+       {
+         incompleteSeqs.clear();
+       }
        // seqRefIds = null;
        // seqsToIds = null;
      }
      {
        seqRefIds = new HashMap<String, SequenceI>();
      }
+     if (incompleteSeqs == null)
+     {
+       incompleteSeqs = new HashMap<String, SequenceI>();
+     }
+     if (frefedSequence == null)
+     {
+       frefedSequence = new ArrayList<SeqFref>();
+     }
    }
  
    public Jalview2XML()
      this.raiseGUI = raiseGUI;
    }
  
-   public void resolveFrefedSequences()
+   /**
+    * base class for resolving forward references to sequences by their ID
+    * 
+    * @author jprocter
+    *
+    */
+   abstract class SeqFref
    {
-     if (frefedSequence.size() > 0)
+     String sref;
+     String type;
+     public SeqFref(String _sref, String type)
+     {
+       sref = _sref;
+       this.type = type;
+     }
+     public String getSref()
+     {
+       return sref;
+     }
+     public SequenceI getSrefSeq()
+     {
+       return seqRefIds.get(sref);
+     }
+     public boolean isResolvable()
+     {
+       return seqRefIds.get(sref) != null;
+     }
+     public SequenceI getSrefDatasetSeq()
      {
-       int r = 0, rSize = frefedSequence.size();
-       while (r < rSize)
+       SequenceI sq = seqRefIds.get(sref);
+       if (sq != null)
        {
-         Object[] ref = frefedSequence.elementAt(r);
-         if (ref != null)
+         while (sq.getDatasetSequence() != null)
          {
-           String sref = (String) ref[0];
-           if (seqRefIds.containsKey(sref))
-           {
-             if (ref[1] instanceof jalview.datamodel.Mapping)
-             {
-               SequenceI seq = seqRefIds.get(sref);
-               while (seq.getDatasetSequence() != null)
-               {
-                 seq = seq.getDatasetSequence();
-               }
-               ((jalview.datamodel.Mapping) ref[1]).setTo(seq);
-             }
-             else
-             {
-               if (ref[1] instanceof jalview.datamodel.AlignedCodonFrame)
-               {
-                 SequenceI seq = seqRefIds.get(sref);
-                 while (seq.getDatasetSequence() != null)
-                 {
-                   seq = seq.getDatasetSequence();
-                 }
-                 if (ref[2] != null
-                         && ref[2] instanceof jalview.datamodel.Mapping)
-                 {
-                   jalview.datamodel.Mapping mp = (jalview.datamodel.Mapping) ref[2];
-                   ((jalview.datamodel.AlignedCodonFrame) ref[1]).addMap(
-                           seq, mp.getTo(), mp.getMap());
-                 }
-                 else
-                 {
-                   System.err
-                           .println("IMPLEMENTATION ERROR: Unimplemented forward sequence references for AlcodonFrames involving "
-                                   + ref[2].getClass() + " type objects.");
-                 }
-               }
-               else
-               {
-                 System.err
-                         .println("IMPLEMENTATION ERROR: Unimplemented forward sequence references for "
-                                 + ref[1].getClass() + " type objects.");
-               }
-             }
-             frefedSequence.remove(r);
-             rSize--;
-           }
-           else
+           sq = sq.getDatasetSequence();
+         }
+       }
+       return sq;
+     }
+     /**
+      * @return true if the forward reference was fully resolved
+      */
+     abstract boolean resolve();
+     @Override
+     public String toString()
+     {
+       return type + " reference to " + sref;
+     }
+   }
+   /**
+    * create forward reference for a mapping
+    * 
+    * @param sref
+    * @param _jmap
+    * @return
+    */
+   public SeqFref newMappingRef(final String sref,
+           final jalview.datamodel.Mapping _jmap)
+   {
+     SeqFref fref = new SeqFref(sref, "Mapping")
+     {
+       public jalview.datamodel.Mapping jmap = _jmap;
+       @Override
+       boolean resolve()
+       {
+         SequenceI seq = getSrefDatasetSeq();
+         if (seq == null)
+         {
+           return false;
+         }
+         jmap.setTo(seq);
+         return true;
+       }
+     };
+     return fref;
+   }
+   public SeqFref newAlcodMapRef(final String sref,
+           final AlignedCodonFrame _cf, final jalview.datamodel.Mapping _jmap)
+   {
+     SeqFref fref = new SeqFref(sref, "Codon Frame")
+     {
+       AlignedCodonFrame cf = _cf;
+       public jalview.datamodel.Mapping mp = _jmap;
+       @Override
+       public boolean isResolvable()
+       {
+         return super.isResolvable() && mp.getTo() != null;
+       };
+       @Override
+       boolean resolve()
+       {
+         SequenceI seq = getSrefDatasetSeq();
+         if (seq == null)
+         {
+           return false;
+         }
+         cf.addMap(seq, mp.getTo(), mp.getMap());
+         return true;
+       }
+     };
+     return fref;
+   }
+   public void resolveFrefedSequences()
+   {
+     Iterator<SeqFref> nextFref=frefedSequence.iterator();
+     int toresolve=frefedSequence.size();
+     int unresolved=0,failedtoresolve=0;
+     while (nextFref.hasNext()) {
+       SeqFref ref = nextFref.next();
+       if (ref.isResolvable())
+       {
+         try {
+           if (ref.resolve())
            {
-             System.err
-                     .println("IMPLEMENTATION WARNING: Unresolved forward reference for hash string "
-                             + ref[0]
-                             + " with objecttype "
-                             + ref[1].getClass());
-             r++;
+             nextFref.remove();
+           } else {
+             failedtoresolve++;
            }
-         }
-         else
+         } catch (Exception x) {
+           System.err.println("IMPLEMENTATION ERROR: Failed to resolve forward reference for sequence "+ref.getSref());
+           x.printStackTrace();
+           failedtoresolve++;
+         } 
+       } else {
+         unresolved++;
+       }
+     }
+     if (unresolved>0)
+     {
+       System.err.println("Jalview Project Import: There were " + unresolved
+               + " forward references left unresolved on the stack.");
+     }
+     if (failedtoresolve>0)
+     {
+       System.err.println("SERIOUS! " + failedtoresolve
+               + " resolvable forward references failed to resolve.");
+     }
+     if (incompleteSeqs != null && incompleteSeqs.size() > 0)
+     {
+       System.err.println("Jalview Project Import: There are "
+               + incompleteSeqs.size()
+               + " sequences which may have incomplete metadata.");
+       if (incompleteSeqs.size() < 10)
+       {
+         for (SequenceI s : incompleteSeqs.values())
          {
-           // empty reference
-           frefedSequence.remove(r);
-           rSize--;
+           System.err.println(s.toString());
          }
        }
+       else
+       {
+         System.err
+                 .println("Too many to report. Skipping output of incomplete sequences.");
+       }
      }
    }
  
      {
        return;
      }
+     saveAllFrames(Arrays.asList(frames), jout);
+   }
  
+   /**
+    * core method for storing state for a set of AlignFrames.
+    * 
+    * @param frames
+    *          - frames involving all data to be exported (including containing
+    *          splitframes)
+    * @param jout
+    *          - project output stream
+    */
+   private void saveAllFrames(List<AlignFrame> frames, JarOutputStream jout)
+   {
      Hashtable<String, AlignFrame> dsses = new Hashtable<String, AlignFrame>();
  
      /*
        List<String> viewIds = new ArrayList<String>();
  
        // REVERSE ORDER
-       for (int i = frames.length - 1; i > -1; i--)
+       for (int i = frames.size() - 1; i > -1; i--)
        {
-         AlignFrame af = frames[i];
+         AlignFrame af = frames.get(i);
          // skip ?
          if (skipList != null
                  && skipList
    {
      try
      {
-       int ap = 0;
-       int apSize = af.alignPanels.size();
        FileOutputStream fos = new FileOutputStream(jarFile);
        JarOutputStream jout = new JarOutputStream(fos);
-       Hashtable<String, AlignFrame> dsses = new Hashtable<String, AlignFrame>();
-       List<String> viewIds = new ArrayList<String>();
+       List<AlignFrame> frames = new ArrayList<AlignFrame>();
  
-       for (AlignmentPanel apanel : af.alignPanels)
+       // resolve splitframes
+       if (af.getViewport().getCodingComplement() != null)
        {
-         String jfileName = apSize == 1 ? fileName : fileName + ap;
-         ap++;
-         if (!jfileName.endsWith(".xml"))
-         {
-           jfileName = jfileName + ".xml";
-         }
-         saveState(apanel, jfileName, jout, viewIds);
-         String dssid = getDatasetIdRef(af.getViewport().getAlignment()
-                 .getDataset());
-         if (!dsses.containsKey(dssid))
-         {
-           dsses.put(dssid, af);
-         }
+         frames = ((SplitFrame) af.getSplitViewContainer()).getAlignFrames();
+       }
+       else
+       {
+         frames.add(af);
        }
-       writeDatasetFor(dsses, fileName, jout);
+       saveAllFrames(frames, jout);
        try
        {
          jout.flush();
      object.setVersion(jalview.bin.Cache.getDefault("VERSION",
              "Development Build"));
  
-     jalview.datamodel.AlignmentI jal = av.getAlignment();
+     /**
+      * rjal is full height alignment, jal is actual alignment with full metadata
+      * but excludes hidden sequences.
+      */
+     jalview.datamodel.AlignmentI rjal = av.getAlignment(), jal = rjal;
  
      if (av.hasHiddenRows())
      {
-       jal = jal.getHiddenSequences().getFullAlignment();
+       rjal = jal.getHiddenSequences().getFullAlignment();
      }
  
      SequenceSet vamsasSet = new SequenceSet();
        {
          // switch jal and the dataset
          jal = jal.getDataset();
+         rjal = jal;
        }
      }
      if (jal.getProperties() != null)
  
      JSeq jseq;
      Set<String> calcIdSet = new HashSet<String>();
+     // record the set of vamsas sequence XML POJO we create.
+     HashMap<String,Sequence> vamsasSetIds = new HashMap<String,Sequence>(); 
      // SAVE SEQUENCES
-     for (int i = 0; i < jal.getHeight(); i++)
+     for (final SequenceI jds : rjal.getSequences())
      {
        final SequenceI jdatasq = jds.getDatasetSequence() == null ? jds
                : jds.getDatasetSequence();
        String id = seqHash(jds);
-       if (seqRefIds.get(id) != null)
-       {
-         // This happens for two reasons: 1. multiple views are being serialised.
-         // 2. the hashCode has collided with another sequence's code. This DOES
-         // HAPPEN! (PF00072.15.stk does this)
-         // JBPNote: Uncomment to debug writing out of files that do not read
-         // back in due to ArrayOutOfBoundExceptions.
-         // System.err.println("vamsasSeq backref: "+id+"");
-         // System.err.println(jds.getName()+"
-         // "+jds.getStart()+"-"+jds.getEnd()+" "+jds.getSequenceAsString());
-         // System.err.println("Hashcode: "+seqHash(jds));
-         // SequenceI rsq = (SequenceI) seqRefIds.get(id + "");
-         // System.err.println(rsq.getName()+"
-         // "+rsq.getStart()+"-"+rsq.getEnd()+" "+rsq.getSequenceAsString());
-         // System.err.println("Hashcode: "+seqHash(rsq));
-       }
-       else
-       {
-         vamsasSeq = createVamsasSequence(id, jds);
-         vamsasSet.addSequence(vamsasSeq);
-         seqRefIds.put(id, jds);
+       if (vamsasSetIds.get(id) == null)
+       {
+         if (seqRefIds.get(id) != null && !storeDS)
+         {
+           // This happens for two reasons: 1. multiple views are being
+           // serialised.
+           // 2. the hashCode has collided with another sequence's code. This
+           // DOES
+           // HAPPEN! (PF00072.15.stk does this)
+           // JBPNote: Uncomment to debug writing out of files that do not read
+           // back in due to ArrayOutOfBoundExceptions.
+           // System.err.println("vamsasSeq backref: "+id+"");
+           // System.err.println(jds.getName()+"
+           // "+jds.getStart()+"-"+jds.getEnd()+" "+jds.getSequenceAsString());
+           // System.err.println("Hashcode: "+seqHash(jds));
+           // SequenceI rsq = (SequenceI) seqRefIds.get(id + "");
+           // System.err.println(rsq.getName()+"
+           // "+rsq.getStart()+"-"+rsq.getEnd()+" "+rsq.getSequenceAsString());
+           // System.err.println("Hashcode: "+seqHash(rsq));
+         }
+         else
+         {
+           vamsasSeq = createVamsasSequence(id, jds);
+           vamsasSet.addSequence(vamsasSeq);
+           vamsasSetIds.put(id, vamsasSeq);
+           seqRefIds.put(id, jds);
+         }
        }
        jseq = new JSeq();
        jseq.setStart(jds.getStart());
        jseq.setEnd(jds.getEnd());
          // Store any sequences this sequence represents
          if (av.hasHiddenRows())
          {
+           // use rjal, contains the full height alignment
            jseq.setHidden(av.getAlignment().getHiddenSequences()
                    .isHidden(jds));
  
-           if (av.isHiddenRepSequence(jal.getSequenceAt(i)))
+           if (av.isHiddenRepSequence(jds))
            {
              jalview.datamodel.SequenceI[] reps = av
-                     .getRepresentedSequences(jal.getSequenceAt(i))
-                     .getSequencesInOrder(jal);
+                     .getRepresentedSequences(jds)
+                     .getSequencesInOrder(rjal);
  
              for (int h = 0; h < reps.length; h++)
              {
-               if (reps[h] != jal.getSequenceAt(i))
+               if (reps[h] != jds)
                {
-                 jseq.addHiddenSequences(jal.findIndex(reps[h]));
+                 jseq.addHiddenSequences(rjal.findIndex(reps[h]));
                }
              }
            }
          }
+         // mark sequence as reference - if it is the reference for this view
+         if (jal.hasSeqrep())
+         {
+           jseq.setViewreference(jds == jal.getSeqrep());
+         }
        }
  
+       // TODO: omit sequence features from each alignment view's XML dump if we
+       // are storing dataset
        if (jds.getSequenceFeatures() != null)
        {
          jalview.datamodel.SequenceFeature[] sf = jds.getSequenceFeatures();
        jal = av.getAlignment();
      }
      // SAVE MAPPINGS
-     if (jal.getCodonFrames() != null)
+     // FOR DATASET
+     if (storeDS && jal.getCodonFrames() != null)
      {
        List<AlignedCodonFrame> jac = jal.getCodonFrames();
        for (AlignedCodonFrame acf : jac)
        {
          AlcodonFrame alc = new AlcodonFrame();
          if (acf.getProtMappings() != null
                  && acf.getProtMappings().length > 0)
          {
+           boolean hasMap = false;
            SequenceI[] dnas = acf.getdnaSeqs();
            jalview.datamodel.Mapping[] pmaps = acf.getProtMappings();
            for (int m = 0; m < pmaps.length; m++)
              alcmap.setMapping(createVamsasMapping(pmaps[m], dnas[m], null,
                      false));
              alc.addAlcodMap(alcmap);
+             hasMap = true;
+           }
+           if (hasMap)
+           {
+             vamsasSet.addAlcodonFrame(alc);
            }
          }
+         // TODO: delete this ? dead code from 2.8.3->2.9 ?
          // {
          // AlcodonFrame alc = new AlcodonFrame();
          // vamsasSet.addAlcodonFrame(alc);
          Vector<String> settingsAdded = new Vector<String>();
          if (renderOrder != null)
          {
-           for (int ro = 0; ro < renderOrder.length; ro++)
+           for (String featureType : renderOrder)
            {
-             FeatureColourI gstyle = ap.getSeqPanel().seqCanvas
+             FeatureColourI fcol = ap.getSeqPanel().seqCanvas
                      .getFeatureRenderer()
-                     .getFeatureStyle(renderOrder[ro]);
+                     .getFeatureStyle(featureType);
              Setting setting = new Setting();
-             setting.setType(renderOrder[ro]);
-             if (!gstyle.isSimpleColour())
+             setting.setType(featureType);
+             if (!fcol.isSimpleColour())
              {
-               setting.setColour(gstyle.getMaxColour().getRGB());
-               setting.setMincolour(gstyle.getMinColour().getRGB());
-               setting.setMin(gstyle.getMin());
-               setting.setMax(gstyle.getMax());
-               setting.setColourByLabel(gstyle.isColourByLabel());
-               setting.setAutoScale(gstyle.isAutoScaled());
-               setting.setThreshold(gstyle.getThreshold());
+               setting.setColour(fcol.getMaxColour().getRGB());
+               setting.setMincolour(fcol.getMinColour().getRGB());
+               setting.setMin(fcol.getMin());
+               setting.setMax(fcol.getMax());
+               setting.setColourByLabel(fcol.isColourByLabel());
+               setting.setAutoScale(fcol.isAutoScaled());
+               setting.setThreshold(fcol.getThreshold());
                // -1 = No threshold, 0 = Below, 1 = Above
-               setting.setThreshstate(gstyle.isAboveThreshold() ? 1
-                       : (gstyle.isBelowThreshold() ? 0 : -1));
+               setting.setThreshstate(fcol.isAboveThreshold() ? 1
+                       : (fcol.isBelowThreshold() ? 0 : -1));
              }
              else
              {
-               setting.setColour(gstyle.getColour().getRGB());
+               setting.setColour(fcol.getColour().getRGB());
              }
  
              setting.setDisplay(av.getFeaturesDisplayed().isVisible(
-                     renderOrder[ro]));
+                     featureType));
              float rorder = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
-                     .getOrder(renderOrder[ro]);
+                     .getOrder(featureType);
              if (rorder > -1)
              {
                setting.setOrder(rorder);
              }
              fs.addSetting(setting);
-             settingsAdded.addElement(renderOrder[ro]);
+             settingsAdded.addElement(featureType);
            }
          }
  
          Vector<String> groupsAdded = new Vector<String>();
          while (en.hasNext())
          {
-           String grp = en.next().toString();
+           String grp = en.next();
            if (groupsAdded.contains(grp))
            {
              continue;
      if (jds.getDatasetSequence() != null)
      {
        vamsasSeq.setDsseqid(seqHash(jds.getDatasetSequence()));
      }
      else
      {
-       vamsasSeq.setDsseqid(id); // so we can tell which sequences really are
+       // seqId==dsseqid so we can tell which sequences really are
        // dataset sequences only
+       vamsasSeq.setDsseqid(id);
        dbrefs = jds.getDBRefs();
+       if (parentseq == null)
+       {
+         parentseq = jds;
+       }
      }
      if (dbrefs != null)
      {
        if (jmp.getTo() != null)
        {
          MappingChoice mpc = new MappingChoice();
-         if (recurse
-                 && (parentseq != jmp.getTo() || parentseq
-                         .getDatasetSequence() != jmp.getTo()))
+         // check/create ID for the sequence referenced by getTo()
+         String jmpid = "";
+         SequenceI ps = null;
+         if (parentseq != jmp.getTo()
+                 && parentseq.getDatasetSequence() != jmp.getTo())
          {
-           mpc.setSequence(createVamsasSequence(false, seqHash(jmp.getTo()),
-                   jmp.getTo(), jds));
+           // chaining dbref rather than a handshaking one
+           jmpid = seqHash(ps = jmp.getTo());
          }
          else
          {
-           String jmpid = "";
-           SequenceI ps = null;
-           if (parentseq != jmp.getTo()
-                   && parentseq.getDatasetSequence() != jmp.getTo())
-           {
-             // chaining dbref rather than a handshaking one
-             jmpid = seqHash(ps = jmp.getTo());
-           }
-           else
-           {
-             jmpid = seqHash(ps = parentseq);
-           }
-           mpc.setDseqFor(jmpid);
-           if (!seqRefIds.containsKey(mpc.getDseqFor()))
-           {
-             jalview.bin.Cache.log.debug("creatign new DseqFor ID");
-             seqRefIds.put(mpc.getDseqFor(), ps);
-           }
-           else
-           {
-             jalview.bin.Cache.log.debug("reusing DseqFor ID");
-           }
+           jmpid = seqHash(ps = parentseq);
+         }
+         mpc.setDseqFor(jmpid);
+         if (!seqRefIds.containsKey(mpc.getDseqFor()))
+         {
+           jalview.bin.Cache.log.debug("creatign new DseqFor ID");
+           seqRefIds.put(mpc.getDseqFor(), ps);
          }
+         else
+         {
+           jalview.bin.Cache.log.debug("reusing DseqFor ID");
+         }
          mp.setMappingChoice(mpc);
        }
      }
      }
      if (seqRefIds == null)
      {
-       seqRefIds = new HashMap<String, SequenceI>();
-     }
-     if (frefedSequence == null)
-     {
-       frefedSequence = new Vector<Object[]>();
+       initSeqRefs();
      }
      AlignFrame af = null, _af = null;
+     IdentityHashMap<AlignmentI, AlignmentI> importedDatasets = new IdentityHashMap<AlignmentI, AlignmentI>();
      Map<String, AlignFrame> gatherToThisFrame = new HashMap<String, AlignFrame>();
      final String file = jprovider.getFilename();
      try
            if (true) // !skipViewport(object))
            {
              _af = loadFromObject(object, file, true, jprovider);
-             if (object.getJalviewModelSequence().getViewportCount() > 0)
+             if (_af != null
+                     && object.getJalviewModelSequence().getViewportCount() > 0)
              {
-               af = _af;
-               if (af.viewport.isGatherViewsHere())
+               if (af == null)
+               {
+                 // store a reference to the first view
+                 af = _af;
+               }
+               if (_af.viewport.isGatherViewsHere())
                {
-                 gatherToThisFrame.put(af.viewport.getSequenceSetId(), af);
+                 // if this is a gathered view, keep its reference since
+                 // after gathering views, only this frame will remain
+                 af = _af;
+                 gatherToThisFrame.put(_af.viewport.getSequenceSetId(), _af);
                }
+               // Save dataset to register mappings once all resolved
+               importedDatasets.put(af.viewport.getAlignment().getDataset(),
+                       af.viewport.getAlignment().getDataset());
              }
            }
            entryCount++;
        e.printStackTrace();
      }
  
-     if (Desktop.instance != null)
-     {
-       Desktop.instance.stopLoading();
-     }
      /*
       * Regather multiple views (with the same sequence set id) to the frame (if
       * any) that is flagged as the one to gather to, i.e. convert them to tabbed
      }
  
      restoreSplitFrames();
+     for (AlignmentI ds : importedDatasets.keySet())
+     {
+       if (ds.getCodonFrames() != null)
+       {
+         StructureSelectionManager.getStructureSelectionManager(
+                 Desktop.instance).registerMappings(ds.getCodonFrames());
+       }
+     }
      if (errorMessage != null)
      {
        reportErrors();
      }
+     if (Desktop.instance != null)
+     {
+       Desktop.instance.stopLoading();
+     }
      return af;
    }
  
     * @param pdbId
     * @return
     */
-   String loadPDBFile(jarInputStreamProvider jprovider, String pdbId)
+   String loadPDBFile(jarInputStreamProvider jprovider, String pdbId,
+           String origFile)
    {
      if (alreadyLoadedPDB.containsKey(pdbId))
      {
        return alreadyLoadedPDB.get(pdbId).toString();
      }
  
-     String tempFile = copyJarEntry(jprovider, pdbId, "jalview_pdb");
+     String tempFile = copyJarEntry(jprovider, pdbId, "jalview_pdb",
+             origFile);
      if (tempFile != null)
      {
        alreadyLoadedPDB.put(pdbId, tempFile);
     * @param prefix
     *          a prefix for the temporary file name, must be at least three
     *          characters long
+    * @param origFile
+    *          null or original file - so new file can be given the same suffix
+    *          as the old one
     * @return
     */
    protected String copyJarEntry(jarInputStreamProvider jprovider,
-           String jarEntryName, String prefix)
+           String jarEntryName, String prefix, String origFile)
    {
      BufferedReader in = null;
      PrintWriter out = null;
+     String suffix = ".tmp";
+     if (origFile == null)
+     {
+       origFile = jarEntryName;
+     }
+     int sfpos = origFile.lastIndexOf(".");
+     if (sfpos > -1 && sfpos < (origFile.length() - 3))
+     {
+       suffix = "." + origFile.substring(sfpos + 1);
+     }
      try
      {
        JarInputStream jin = jprovider.getJarInputStream();
        if (entry != null)
        {
          in = new BufferedReader(new InputStreamReader(jin, UTF_8));
-         File outFile = File.createTempFile(prefix, ".tmp");
+         File outFile = File.createTempFile(prefix, suffix);
          outFile.deleteOnExit();
          out = new PrintWriter(new FileOutputStream(outFile));
          String data;
      // LOAD SEQUENCES
  
      List<SequenceI> hiddenSeqs = null;
-     jalview.datamodel.Sequence jseq;
  
      List<SequenceI> tmpseqs = new ArrayList<SequenceI>();
  
      boolean multipleView = false;
+     SequenceI referenceseqForView = null;
      JSeq[] jseqs = object.getJalviewModelSequence().getJSeq();
      int vi = 0; // counter in vamsasSeq array
      for (int i = 0; i < jseqs.length; i++)
      {
        String seqId = jseqs[i].getId();
  
-       if (seqRefIds.get(seqId) != null)
+       SequenceI tmpSeq = seqRefIds.get(seqId);
+       if (tmpSeq != null)
        {
-         tmpseqs.add(seqRefIds.get(seqId));
-         multipleView = true;
+         if (!incompleteSeqs.containsKey(seqId))
+         {
+           // may not need this check, but keep it for at least 2.9,1 release
+           if (tmpSeq.getStart()!=jseqs[i].getStart() || tmpSeq.getEnd()!=jseqs[i].getEnd())
+           { 
+             System.err
+                     .println("Warning JAL-2154 regression: updating start/end for sequence "
+                             + tmpSeq.toString() + " to " + jseqs[i]);
+           }
+         } else {
+           incompleteSeqs.remove(seqId);
+         }
+         if (vamsasSeq.length > vi && vamsasSeq[vi].getId().equals(seqId))
+         {
+           // most likely we are reading a dataset XML document so
+           // update from vamsasSeq section of XML for this sequence
+           tmpSeq.setName(vamsasSeq[vi].getName());
+           tmpSeq.setDescription(vamsasSeq[vi].getDescription());
+           tmpSeq.setSequence(vamsasSeq[vi].getSequence());
+           vi++;
+         }
+         else
+         {
+           // reading multiple views, so vamsasSeq set is a subset of JSeq
+           multipleView = true;
+         }
+         tmpSeq.setStart(jseqs[i].getStart());
+         tmpSeq.setEnd(jseqs[i].getEnd());
+         tmpseqs.add(tmpSeq);
        }
        else
        {
-         jseq = new jalview.datamodel.Sequence(vamsasSeq[vi].getName(),
+         tmpSeq = new jalview.datamodel.Sequence(vamsasSeq[vi].getName(),
                  vamsasSeq[vi].getSequence());
-         jseq.setDescription(vamsasSeq[vi].getDescription());
-         jseq.setStart(jseqs[i].getStart());
-         jseq.setEnd(jseqs[i].getEnd());
-         jseq.setVamsasId(uniqueSetSuffix + seqId);
-         seqRefIds.put(vamsasSeq[vi].getId(), jseq);
-         tmpseqs.add(jseq);
+         tmpSeq.setDescription(vamsasSeq[vi].getDescription());
+         tmpSeq.setStart(jseqs[i].getStart());
+         tmpSeq.setEnd(jseqs[i].getEnd());
+         tmpSeq.setVamsasId(uniqueSetSuffix + seqId);
+         seqRefIds.put(vamsasSeq[vi].getId(), tmpSeq);
+         tmpseqs.add(tmpSeq);
          vi++;
        }
  
+       if (jseqs[i].hasViewreference() && jseqs[i].getViewreference())
+       {
+         referenceseqForView = tmpseqs.get(tmpseqs.size() - 1);
+       }
        if (jseqs[i].getHidden())
        {
          if (hiddenSeqs == null)
            hiddenSeqs = new ArrayList<SequenceI>();
          }
  
-         hiddenSeqs.add(seqRefIds.get(seqId));
+         hiddenSeqs.add(tmpSeq);
        }
      }
  
      // /
      SequenceI[] orderedSeqs = tmpseqs
              .toArray(new SequenceI[tmpseqs.size()]);
  
-     Alignment al = new Alignment(orderedSeqs);
-     // / Add the alignment properties
-     for (int i = 0; i < vamsasSet.getSequenceSetPropertiesCount(); i++)
-     {
-       SequenceSetProperties ssp = vamsasSet.getSequenceSetProperties(i);
-       al.setProperty(ssp.getKey(), ssp.getValue());
-     }
-     // /
-     // SequenceFeatures are added to the DatasetSequence,
-     // so we must create or recover the dataset before loading features
+     AlignmentI al = null;
+     // so we must create or recover the dataset alignment before going further
      // ///////////////////////////////
      if (vamsasSet.getDatasetId() == null || vamsasSet.getDatasetId() == "")
      {
-       // older jalview projects do not have a dataset id.
+       // older jalview projects do not have a dataset - so creat alignment and
+       // dataset
+       al = new Alignment(orderedSeqs);
        al.setDataset(null);
      }
      else
      {
-       // recover dataset - passing on flag indicating if this a 'viewless'
-       // sequence set (a.k.a. a stored dataset for the project)
-       recoverDatasetFor(vamsasSet, al, object.getJalviewModelSequence()
-               .getViewportCount() == 0);
+       boolean isdsal = object.getJalviewModelSequence().getViewportCount() == 0;
+       if (isdsal)
+       {
+         // we are importing a dataset record, so
+         // recover reference to an alignment already materialsed as dataset
+         al = getDatasetFor(vamsasSet.getDatasetId());
+       }
+       if (al == null)
+       {
+         // materialse the alignment
+         al = new Alignment(orderedSeqs);
+       }
+       if (isdsal)
+       {
+         addDatasetRef(vamsasSet.getDatasetId(), al);
+       }
+       // finally, verify all data in vamsasSet is actually present in al
+       // passing on flag indicating if it is actually a stored dataset
+       recoverDatasetFor(vamsasSet, al, isdsal);
+     }
+     if (referenceseqForView != null)
+     {
+       al.setSeqrep(referenceseqForView);
      }
+     // / Add the alignment properties
+     for (int i = 0; i < vamsasSet.getSequenceSetPropertiesCount(); i++)
+     {
+       SequenceSetProperties ssp = vamsasSet.getSequenceSetProperties(i);
+       al.setProperty(ssp.getKey(), ssp.getValue());
+     }
      // ///////////////////////////////
  
      Hashtable pdbloaded = new Hashtable(); // TODO nothing writes to this??
      {
        // load sequence features, database references and any associated PDB
        // structures for the alignment
+       //
+       // prior to 2.10, this part would only be executed the first time a
+       // sequence was encountered, but not afterwards.
+       // now, for 2.10 projects, this is also done if the xml doc includes
+       // dataset sequences not actually present in any particular view.
+       //
        for (int i = 0; i < vamsasSeq.length; i++)
        {
          if (jseqs[i].getFeaturesCount() > 0)
                }
  
              }
-             al.getSequenceAt(i).getDatasetSequence().addSequenceFeature(sf);
+             // adds feature to datasequence's feature set (since Jalview 2.10)
+             al.getSequenceAt(i).addSequenceFeature(sf);
            }
          }
          if (vamsasSeq[i].getDBRefCount() > 0)
          {
-           addDBRefs(al.getSequenceAt(i).getDatasetSequence(), vamsasSeq[i]);
+           // adds dbrefs to datasequence's set (since Jalview 2.10)
+           addDBRefs(
+                   al.getSequenceAt(i).getDatasetSequence() == null ? al.getSequenceAt(i)
+                           : al.getSequenceAt(i).getDatasetSequence(),
+                   vamsasSeq[i]);
          }
          if (jseqs[i].getPdbidsCount() > 0)
          {
              entry.setId(ids[p].getId());
              if (ids[p].getType() != null)
              {
-               if (ids[p].getType().equalsIgnoreCase("PDB"))
+               if (PDBEntry.Type.getType(ids[p].getType()) != null)
                {
-                 entry.setType(PDBEntry.Type.PDB);
+                 entry.setType(PDBEntry.Type.getType(ids[p].getType()));
                }
                else
                {
              {
                if (!pdbloaded.containsKey(ids[p].getFile()))
                {
-                 entry.setFile(loadPDBFile(jprovider, ids[p].getId()));
+                 entry.setFile(loadPDBFile(jprovider, ids[p].getId(),
+                         ids[p].getFile()));
                }
                else
                {
                  entry.setFile(pdbloaded.get(ids[p].getId()).toString());
                }
              }
+             if (ids[p].getPdbentryItem() != null)
+             {
+               entry.setProperty(new Hashtable());
+               for (PdbentryItem item : ids[p].getPdbentryItem())
+               {
+                 for (Property pr : item.getProperty())
+                 {
+                   entry.getProperty().put(pr.getName(), pr.getValue());
+                 }
+               }
+             }
              StructureSelectionManager.getStructureSelectionManager(
                      Desktop.instance).registerPDBEntry(entry);
-             al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
+             // adds PDBEntry to datasequence's set (since Jalview 2.10)
+             if (al.getSequenceAt(i).getDatasetSequence() != null)
+             {
+               al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
+             }
+             else
+             {
+               al.getSequenceAt(i).addPDBId(entry);
+             }
            }
          }
        }
              if (maps[m].getMapping() != null)
              {
                mapping = addMapping(maps[m].getMapping());
-             }
-             if (dnaseq != null && mapping.getTo() != null)
-             {
-               cf.addMap(dnaseq, mapping.getTo(), mapping.getMap());
-             }
-             else
-             {
-               // defer to later
-               frefedSequence.add(new Object[] { maps[m].getDnasq(), cf,
-                   mapping });
+               if (dnaseq != null && mapping.getTo() != null)
+               {
+                 cf.addMap(dnaseq, mapping.getTo(), mapping.getMap());
+               }
+               else
+               {
+                 // defer to later
+                 frefedSequence.add(newAlcodMapRef(maps[m].getDnasq(), cf,
+                         mapping));
+               }
              }
            }
+           al.addCodonFrame(cf);
          }
-         al.addCodonFrame(cf);
        }
      }
  
  
          sg.setOutlineColour(new java.awt.Color(jGroup.getOutlineColour()));
  
 -        sg.textColour = new java.awt.Color(jGroup.getTextCol1());
 -        sg.textColour2 = new java.awt.Color(jGroup.getTextCol2());
 +        sg.textColour = new Colour(jGroup.getTextCol1());
 +        sg.textColour2 = new Colour(jGroup.getTextCol2());
          sg.setShowNonconserved(jGroup.hasShowUnconserved() ? jGroup
                  .isShowUnconserved() : false);
          sg.thresholdTextColour = jGroup.getTextColThreshold();
            String rnaTitle = ss.getTitle();
            String sessionState = ss.getViewerState();
            String tempStateFile = copyJarEntry(jprovider, sessionState,
-                   "varna");
+                   "varna", null);
            RnaModel rna = new RnaModel(rnaTitle, ann, seq, null, gapped);
            appVarna.addModelSession(rna, rnaTitle, tempStateFile);
          }
              // Originally : ids[p].getFile()
              // : TODO: verify external PDB file recovery still works in normal
              // jalview project load
-             jpdb.setFile(loadPDBFile(jprovider, ids[p].getId()));
+             jpdb.setFile(loadPDBFile(jprovider, ids[p].getId(),
+                     ids[p].getFile()));
              jpdb.setId(ids[p].getId());
  
              int x = structureState.getXpos();
              // Probably don't need to do this anymore...
              // Desktop.desktop.getComponentAt(x, y);
              // TODO: NOW: check that this recovers the PDB file correctly.
-             String pdbFile = loadPDBFile(jprovider, ids[p].getId());
+             String pdbFile = loadPDBFile(jprovider, ids[p].getId(),
+                     ids[p].getFile());
              jalview.datamodel.SequenceI seq = seqRefIds.get(jseqs[i]
                      .getId() + "");
              if (sviewid == null)
       */
      String viewerJarEntryName = getViewerJarEntryName(data.getViewId());
      chimeraSessionFile = copyJarEntry(jprovider, viewerJarEntryName,
-             "chimera");
+             "chimera", null);
  
      Set<Entry<File, StructureData>> fileData = data.getFileData()
              .entrySet();
          // filename
          // translation differently.
          StructureData filedat = oldFiles.get(new File(oldfilenam));
+           if (filedat == null)
+           {
+             String reformatedOldFilename = oldfilenam.replaceAll("/",
+                     "\\\\");
+             filedat = oldFiles.get(new File(reformatedOldFilename));
+         }
          newFileLoc.append(Platform.escapeString(filedat.getFilePath()));
          pdbfilenames.add(filedat.getFilePath());
          pdbids.add(filedat.getPdbId());
    }
  
    /**
+    * Answers true if 'version' is equal to or later than 'supported', where each
+    * is formatted as major/minor versions like "2.8.3" or "2.3.4b1" for bugfix
+    * changes. Development and test values for 'version' are leniently treated
+    * i.e. answer true.
     * 
     * @param supported
     *          - minimum version we are comparing against
     * @param version
-    *          - version of data being processsed.
-    * @return true if version is development/null or evaluates to the same or
-    *         later X.Y.Z (where X,Y,Z are like [0-9]+b?[0-9]*)
+    *          - version of data being processsed
+    * @return
     */
    public static boolean isVersionStringLaterThan(String supported,
            String version)
    {
-     if (version == null || version.equalsIgnoreCase("DEVELOPMENT BUILD")
+     if (supported == null || version == null
+             || version.equalsIgnoreCase("DEVELOPMENT BUILD")
              || version.equalsIgnoreCase("Test")
              || version.equalsIgnoreCase("AUTOMATED BUILD"))
      {
      }
      else
      {
-       StringTokenizer currentV = new StringTokenizer(supported, "."), fileV = new StringTokenizer(
-               version, ".");
-       while (currentV.hasMoreTokens() && fileV.hasMoreTokens())
-       {
-         // convert b to decimal to catch bugfix releases within a series
-         String curT = currentV.nextToken().toLowerCase().replace('b', '.');
-         String fileT = fileV.nextToken().toLowerCase().replace('b', '.');
-         try
-         {
-           float supportedVersionToken = Float.parseFloat(curT);
-           float myVersiontoken = Float.parseFloat(fileT);
-           if (supportedVersionToken > myVersiontoken)
-           {
-             // current version is newer than the version that wrote the file
-             return false;
-           }
-           if (supportedVersionToken < myVersiontoken)
-           {
-             // current version is older than the version that wrote the file
-             return true;
-           }
-         } catch (NumberFormatException nfe)
-         {
-           System.err
-                   .println("** WARNING: Version comparison failed for tokens ("
-                           + curT
-                           + ") and ("
-                           + fileT
-                           + ")\n** Current: '"
-                           + supported + "' and Version: '" + version + "'");
-         }
-       }
-       if (currentV.hasMoreElements())
-       {
-         // fileV has no minor version but identical series to current
-         return false;
-       }
+       return StringUtils.compareVersions(version, supported, "b") >= 0;
      }
-     return true;
    }
  
    Vector<JalviewStructureDisplayI> newStructureViewers = null;
    }
  
    AlignFrame loadViewport(String file, JSeq[] JSEQ,
-           List<SequenceI> hiddenSeqs, Alignment al,
+           List<SequenceI> hiddenSeqs, AlignmentI al,
            JalviewModelSequence jms, Viewport view, String uniqueSeqSetId,
            String viewId, List<JvAnnotRow> autoAlan)
    {
      for (int i = 0; i < JSEQ.length; i++)
      {
        af.viewport.setSequenceColour(af.viewport.getAlignment()
 -              .getSequenceAt(i), new java.awt.Color(JSEQ[i].getColour()));
 +              .getSequenceAt(i), new Colour(JSEQ[i].getColour()));
      }
  
+     if (al.hasSeqrep())
+     {
+       af.getViewport().setColourByReferenceSeq(true);
+       af.getViewport().setDisplayReferenceSeq(true);
+     }
      af.viewport.setGatherViewsHere(view.getGatheredViews());
  
      if (view.getSequenceSetId() != null)
      {
        for (int s = 0; s < JSEQ.length; s++)
        {
-         jalview.datamodel.SequenceGroup hidden = new jalview.datamodel.SequenceGroup();
+         SequenceGroup hidden = new SequenceGroup();
+         boolean isRepresentative = false;
          for (int r = 0; r < JSEQ[s].getHiddenSequencesCount(); r++)
          {
-           hidden.addSequence(
-                   al.getSequenceAt(JSEQ[s].getHiddenSequences(r)), false);
+           isRepresentative = true;
+           SequenceI sequenceToHide = al.getSequenceAt(JSEQ[s]
+                   .getHiddenSequences(r));
+           hidden.addSequence(sequenceToHide, false);
+           // remove from hiddenSeqs list so we don't try to hide it twice
+           hiddenSeqs.remove(sequenceToHide);
+         }
+         if (isRepresentative)
+         {
+           SequenceI representativeSequence = al.getSequenceAt(s);
+           hidden.addSequence(representativeSequence, false);
+           af.viewport.hideRepSequences(representativeSequence, hidden);
          }
-         af.viewport.hideRepSequences(al.getSequenceAt(s), hidden);
        }
  
-       // jalview.datamodel.SequenceI[] hseqs = new
-       // jalview.datamodel.SequenceI[hiddenSeqs
-       // .size()];
-       //
-       // for (int s = 0; s < hiddenSeqs.size(); s++)
-       // {
-       // hseqs[s] = (jalview.datamodel.SequenceI) hiddenSeqs.elementAt(s);
-       // }
        SequenceI[] hseqs = hiddenSeqs.toArray(new SequenceI[hiddenSeqs
                .size()]);
        af.viewport.hideSequence(hseqs);
  
      af.viewport.setShowText(view.getShowText());
  
 -    af.viewport.setTextColour(new java.awt.Color(view.getTextCol1()));
 -    af.viewport.setTextColour2(new java.awt.Color(view.getTextCol2()));
 +    af.viewport.setTextColour(new Colour(view.getTextCol1()));
 +    af.viewport.setTextColour2(new Colour(view.getTextCol2()));
      af.viewport.setThresholdTextColour(view.getTextColThreshold());
      af.viewport.setShowUnconserved(view.hasShowUnconserved() ? view
              .isShowUnconserved() : false);
          if (setting.hasMincolour())
          {
            FeatureColourI gc = setting.hasMin() ? new FeatureColour(
 -                  new Color(setting.getMincolour()), new Color(
 -                          setting.getColour()), setting.getMin(),
 -                  setting.getMax()) : new FeatureColour(new Color(
 -                  setting.getMincolour()), new Color(setting.getColour()),
 +                  new Colour(new Color(setting.getMincolour())),
 +                  new Colour(new Color(setting.getColour())),
 +                  setting.getMin(), setting.getMax()) : new FeatureColour(
 +                  new Colour(new Color(setting.getMincolour())),
 +                  new Colour(new Color(setting.getColour())),
                    0, 1);
            if (setting.hasThreshold())
            {
          else
          {
            featureColours.put(setting.getType(), new FeatureColour(
 -                  new Color(setting.getColour())));
 +                  new Colour(new Color(setting.getColour()))));
          }
          renderOrder[fs] = setting.getType();
          if (setting.hasOrder())
        }
      }
      af.setMenusFromViewport(af.viewport);
+     af.setTitle(view.getTitle());
      // TODO: we don't need to do this if the viewport is aready visible.
      /*
       * Add the AlignFrame to the desktop (it may be 'gathered' later), unless it
    }
  
    private ColourSchemeI constructAnnotationColour(
-           AnnotationColours viewAnnColour, AlignFrame af, Alignment al,
+           AnnotationColours viewAnnColour, AlignFrame af, AlignmentI al,
            JalviewModelSequence jms, boolean checkGroupAnnColour)
    {
      boolean propagateAnnColour = false;
      return cs;
    }
  
-   private void reorderAutoannotation(AlignFrame af, Alignment al,
+   private void reorderAutoannotation(AlignFrame af, AlignmentI al,
            List<JvAnnotRow> autoAlan)
    {
      // copy over visualization settings for autocalculated annotation in the
      }
    }
  
-   private void recoverDatasetFor(SequenceSet vamsasSet, Alignment al,
+   private void recoverDatasetFor(SequenceSet vamsasSet, AlignmentI al,
            boolean ignoreUnrefed)
    {
-     jalview.datamodel.Alignment ds = getDatasetFor(vamsasSet.getDatasetId());
+     jalview.datamodel.AlignmentI ds = getDatasetFor(vamsasSet
+             .getDatasetId());
      Vector dseqs = null;
      if (ds == null)
      {
      for (int i = 0, iSize = vamsasSet.getSequenceCount(); i < iSize; i++)
      {
        Sequence vamsasSeq = vamsasSet.getSequence(i);
-       ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs, ignoreUnrefed);
+       ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs, ignoreUnrefed, i);
      }
      // create a new dataset
      if (ds == null)
     *          dataset alignment
     * @param dseqs
     *          vector to add new dataset sequence to
+    * @param ignoreUnrefed
+    *          - when true, don't create new sequences from vamsasSeq if it's id
+    *          doesn't already have an asssociated Jalview sequence.
+    * @param vseqpos
+    *          - used to reorder the sequence in the alignment according to the
+    *          vamsasSeq array ordering, to preserve ordering of dataset
     */
    private void ensureJalviewDatasetSequence(Sequence vamsasSeq,
-           AlignmentI ds, Vector dseqs, boolean ignoreUnrefed)
+           AlignmentI ds, Vector dseqs, boolean ignoreUnrefed, int vseqpos)
    {
      // JBP TODO: Check this is called for AlCodonFrames to support recovery of
      // xRef Codon Maps
      SequenceI sq = seqRefIds.get(vamsasSeq.getId());
+     boolean reorder = false;
      SequenceI dsq = null;
      if (sq != null && sq.getDatasetSequence() != null)
      {
        dsq = sq.getDatasetSequence();
      }
+     else
+     {
+       reorder = true;
+     }
      if (sq == null && ignoreUnrefed)
      {
        return;
          // + (post ? "appended" : ""));
        }
      }
+     else
+     {
+       // sequence refs are identical. We may need to update the existing dataset
+       // alignment with this one, though.
+       if (ds != null && dseqs == null)
+       {
+         int opos = ds.findIndex(dsq);
+         SequenceI tseq = null;
+         if (opos != -1 && vseqpos != opos)
+         {
+           // remove from old position
+           ds.deleteSequence(dsq);
+         }
+         if (vseqpos < ds.getHeight())
+         {
+           if (vseqpos != opos)
+           {
+             // save sequence at destination position
+             tseq = ds.getSequenceAt(vseqpos);
+             ds.replaceSequenceAt(vseqpos, dsq);
+             ds.addSequence(tseq);
+           }
+         }
+         else
+         {
+           ds.addSequence(dsq);
+         }
+       }
+     }
    }
  
    /*
     * TODO use AlignmentI here and in related methods - needs
     * AlignmentI.getDataset() changed to return AlignmentI instead of Alignment
     */
-   Hashtable<String, Alignment> datasetIds = null;
+   Hashtable<String, AlignmentI> datasetIds = null;
  
-   IdentityHashMap<Alignment, String> dataset2Ids = null;
+   IdentityHashMap<AlignmentI, String> dataset2Ids = null;
  
-   private Alignment getDatasetFor(String datasetId)
+   private AlignmentI getDatasetFor(String datasetId)
    {
      if (datasetIds == null)
      {
-       datasetIds = new Hashtable<String, Alignment>();
+       datasetIds = new Hashtable<String, AlignmentI>();
        return null;
      }
      if (datasetIds.containsKey(datasetId))
      return null;
    }
  
-   private void addDatasetRef(String datasetId, Alignment dataset)
+   private void addDatasetRef(String datasetId, AlignmentI dataset)
    {
      if (datasetIds == null)
      {
-       datasetIds = new Hashtable<String, Alignment>();
+       datasetIds = new Hashtable<String, AlignmentI>();
      }
      datasetIds.put(datasetId, dataset);
    }
     * @param dataset
     * @return
     */
-   private String getDatasetIdRef(Alignment dataset)
+   private String getDatasetIdRef(AlignmentI dataset)
    {
      if (dataset.getDataset() != null)
      {
        // make a new datasetId and record it
        if (dataset2Ids == null)
        {
-         dataset2Ids = new IdentityHashMap<Alignment, String>();
+         dataset2Ids = new IdentityHashMap<AlignmentI, String>();
        }
        else
        {
          }
          else
          {
-           frefedSequence.add(new Object[] { dsfor, jmap });
+           frefedSequence.add(newMappingRef(dsfor, jmap));
          }
        }
        else
            djs.setEnd(jmap.getMap().getToHighest());
            djs.setVamsasId(uniqueSetSuffix + sqid);
            jmap.setTo(djs);
+           incompleteSeqs.put(sqid, djs);
            seqRefIds.put(sqid, djs);
  
          }
@@@ -24,7 -24,8 +24,9 @@@ import jalview.datamodel.AlignmentI
  import jalview.datamodel.SearchResults;
  import jalview.datamodel.SequenceGroup;
  import jalview.datamodel.SequenceI;
+ import jalview.renderer.ScaleRenderer;
+ import jalview.renderer.ScaleRenderer.ScaleMark;
 +import jalview.util.ColorUtils;
  
  import java.awt.BasicStroke;
  import java.awt.BorderLayout;
@@@ -123,24 -124,26 +125,26 @@@ public class SeqCanvas extends JCompone
    private void drawNorthScale(Graphics g, int startx, int endx, int ypos)
    {
      updateViewport();
-     int scalestartx = startx - (startx % 10) + 10;
-     g.setColor(Color.black);
-     // NORTH SCALE
-     for (int i = scalestartx; i < endx; i += 10)
+     for (ScaleMark mark : new ScaleRenderer().calculateMarks(av, startx,
+             endx))
      {
-       int value = i;
-       if (av.hasHiddenColumns())
+       int mpos = mark.column; // (i - startx - 1)
+       if (mpos < 0)
        {
-         value = av.getColumnSelection().adjustForHiddenColumns(value);
+         continue;
        }
+       String mstring = mark.text;
  
-       g.drawString(String.valueOf(value), (i - startx - 1) * charWidth,
-               ypos - (charHeight / 2));
-       g.drawLine(((i - startx - 1) * charWidth) + (charWidth / 2),
-               (ypos + 2) - (charHeight / 2), ((i - startx - 1) * charWidth)
-                       + (charWidth / 2), ypos - 2);
+       if (mark.major)
+       {
+         if (mstring != null)
+         {
+           g.drawString(mstring, mpos * charWidth, ypos - (charHeight / 2));
+         }
+         g.drawLine((mpos * charWidth) + (charWidth / 2), (ypos + 2)
+                 - (charHeight / 2), (mpos * charWidth) + (charWidth / 2),
+                 ypos - 2);
+       }
      }
    }
  
                else
                {
                  g.setStroke(new BasicStroke());
 -                g.setColor(group.getOutlineColour());
 +                g.setColor(ColorUtils.getColor(group.getOutlineColour()));
                }
              }
            }
   */
  package jalview.gui;
  
 +import jalview.api.ColorI;
  import jalview.api.FeatureRenderer;
  import jalview.datamodel.SequenceGroup;
  import jalview.datamodel.SequenceI;
 +import jalview.schemes.Colour;
  import jalview.schemes.ColourSchemeI;
 +import jalview.util.ColorUtils;
  
  import java.awt.Color;
  import java.awt.FontMetrics;
  import java.awt.Graphics;
  
- /**
-  * DOCUMENT ME!
-  * 
-  * @author $author$
-  * @version $Revision$
-  */
  public class SequenceRenderer implements jalview.api.SequenceRenderer
  {
    final static int CHAR_TO_UPPER = 'A' - 'a';
@@@ -92,8 -83,9 +86,9 @@@
    }
  
    @Override
 -  public Color getResidueBoxColour(SequenceI seq, int i)
 +  public ColorI getResidueBoxColour(SequenceI seq, int i)
    {
+     // rate limiting step when rendering overview for lots of groups
      allGroups = av.getAlignment().findAllGroups(seq);
  
      if (inCurrentSequenceGroup(i))
        getBoxColour(av.getGlobalColourScheme(), seq, i);
      }
  
 -    return resBoxColour;
 +    return new Colour(resBoxColour);
    }
  
    /**
     * @return
     */
    @Override
 -  public Color getResidueColour(final SequenceI seq, int position,
 +  public ColorI getResidueColour(final SequenceI seq, int position,
            FeatureRenderer fr)
    {
      // TODO replace 8 or so code duplications with calls to this method
      // (refactored as needed)
 -    Color col = getResidueBoxColour(seq, position);
 +    ColorI col = getResidueBoxColour(seq, position);
  
      if (fr != null)
      {
      {
        end = seq.getLength() - 1;
      }
 -    graphics.setColor(av.getTextColour());
 +    graphics.setColor(ColorUtils.getColor(av.getTextColour()));
  
      if (monospacedFont && av.getShowText() && allGroups.length == 0
              && !av.getColourText() && av.getThresholdTextColour() == 0)
        for (int i = start; i <= end; i++)
        {
  
 -        graphics.setColor(av.getTextColour());
 +        graphics.setColor(ColorUtils.getColor(av.getTextColour()));
          getboxColour = false;
          s = seq.getCharAt(i);
  
                if (resBoxColour.getRed() + resBoxColour.getBlue()
                        + resBoxColour.getGreen() < currentSequenceGroup.thresholdTextColour)
                {
 -                graphics.setColor(currentSequenceGroup.textColour2);
 +                graphics.setColor(ColorUtils.getColor(currentSequenceGroup.textColour2));
                }
              }
            }
            else
            {
 -            graphics.setColor(currentSequenceGroup.textColour);
 +            graphics.setColor(ColorUtils.getColor(currentSequenceGroup.textColour));
            }
            if (!isarep && !isgrep
                    && currentSequenceGroup.getShowNonconserved()) // todo
              if (resBoxColour.getRed() + resBoxColour.getBlue()
                      + resBoxColour.getGreen() < av.getThresholdTextColour())
              {
 -              graphics.setColor(av.getTextColour2());
 +              graphics.setColor(ColorUtils.getColor(av.getTextColour2()));
              }
            }
            if (!isarep && av.getShowUnconserved())
            {
-             s = getDisplayChar(srep, i, s, '.', currentSequenceGroup);
+             s = getDisplayChar(srep, i, s, '.', null);
  
            }
  
    {
      // TODO - use currentSequenceGroup rather than alignment
      // currentSequenceGroup.getConsensus()
-     char conschar = (usesrep) ? (currentGroup == null ? av.getAlignment()
+     char conschar = (usesrep) ? (currentGroup == null
+             || position < currentGroup.getStartRes()
+             || position > currentGroup.getEndRes() ? av.getAlignment()
              .getSeqrep().getCharAt(position)
              : (currentGroup.getSeqrep() != null ? currentGroup.getSeqrep()
                      .getCharAt(position) : av.getAlignment().getSeqrep()
                      .getCharAt(position)))
-             : (currentGroup != null && currentGroup.getConsensus() != null) ? currentGroup
+             : (currentGroup != null && currentGroup.getConsensus() != null
+                     && position >= currentGroup.getStartRes()
+                     && position <= currentGroup.getEndRes() && currentGroup
+                     .getConsensus().annotations.length > position) ? currentGroup
                      .getConsensus().annotations[position].displayCharacter
                      .charAt(0)
                      : av.getAlignmentConsensusAnnotation().annotations[position].displayCharacter
@@@ -21,7 -21,6 +21,7 @@@
  package jalview.gui;
  
  import jalview.datamodel.SequenceGroup;
 +import jalview.schemes.Colour;
  import jalview.util.MessageManager;
  
  import java.awt.BorderLayout;
@@@ -73,7 -72,7 +73,7 @@@ public class TextColourChoose
      final JPanel col2 = new JPanel();
      col2.setPreferredSize(new Dimension(40, 20));
      col2.setBorder(BorderFactory.createEtchedBorder());
-     col2.setToolTipText(MessageManager.getString("label.ligth_colour"));
+     col2.setToolTipText(MessageManager.getString("label.light_colour"));
      col2.setBackground(new Color(original2));
      final JPanel bigpanel = new JPanel(new BorderLayout());
      JPanel panel = new JPanel();
@@@ -82,7 -81,7 +82,7 @@@
              new JLabel(
                      "<html>"
                              + MessageManager
-                                     .getString("label.select_dark_light_set_thereshold")
+                                     .getString("label.select_dark_light_set_threshold")
                              + "</html>"), BorderLayout.NORTH);
      panel.add(col1);
      panel.add(slider);
                      ap,
                      bigpanel,
                      MessageManager
-                             .getString("label.adjunst_foreground_text_colour_thereshold"),
+                             .getString("label.adjunst_foreground_text_colour_threshold"),
                      JOptionPane.OK_CANCEL_OPTION,
                      JOptionPane.QUESTION_MESSAGE, null, null, null);
  
      {
        if (sg == null)
        {
 -        ap.av.setTextColour(new Color(original1));
 -        ap.av.setTextColour2(new Color(original2));
 +        ap.av.setTextColour(new Colour(original1));
 +        ap.av.setTextColour2(new Colour(original2));
          ap.av.setThresholdTextColour(originalThreshold);
        }
        else
        {
 -        sg.textColour = new Color(original1);
 -        sg.textColour2 = new Color(original2);
 +        sg.textColour = new Colour(original1);
 +        sg.textColour2 = new Colour(original2);
          sg.thresholdTextColour = originalThreshold;
        }
      }
    {
      if (sg == null)
      {
 -      ap.av.setTextColour(col);
 +      ap.av.setTextColour(new Colour(col));
        if (ap.av.getColourAppliesToAllGroups())
        {
          setGroupTextColour();
      }
      else
      {
 -      sg.textColour = col;
 +      sg.textColour = new Colour(col);
      }
  
      ap.paintAlignment(true);
    {
      if (sg == null)
      {
 -      ap.av.setTextColour2(col);
 +      ap.av.setTextColour2(new Colour(col));
        if (ap.av.getColourAppliesToAllGroups())
        {
          setGroupTextColour();
      }
      else
      {
 -      sg.textColour2 = col;
 +      sg.textColour2 = new Colour(col);
      }
  
      ap.paintAlignment(true);
@@@ -27,13 -27,11 +27,13 @@@ import jalview.datamodel.Sequence
  import jalview.datamodel.SequenceGroup;
  import jalview.datamodel.SequenceI;
  import jalview.datamodel.SequenceNode;
 +import jalview.schemes.Colour;
  import jalview.schemes.ColourSchemeI;
  import jalview.schemes.ColourSchemeProperty;
  import jalview.schemes.ResidueProperties;
  import jalview.schemes.UserColourScheme;
  import jalview.structure.SelectionSource;
 +import jalview.util.ColorUtils;
  import jalview.util.Format;
  import jalview.util.MappingUtils;
  import jalview.util.MessageManager;
@@@ -61,6 -59,7 +61,7 @@@ import java.util.Vector
  import javax.swing.JColorChooser;
  import javax.swing.JPanel;
  import javax.swing.JScrollPane;
+ import javax.swing.SwingUtilities;
  import javax.swing.ToolTipManager;
  
  /**
@@@ -176,13 -175,13 +177,13 @@@ public class TreeCanvas extends JPanel 
      tree.findHeight(tree.getTopNode());
  
      // Now have to calculate longest name based on the leaves
-     Vector leaves = tree.findLeaves(tree.getTopNode(), new Vector());
+     Vector<SequenceNode> leaves = tree.findLeaves(tree.getTopNode());
      boolean has_placeholders = false;
      longestName = "";
  
      for (int i = 0; i < leaves.size(); i++)
      {
-       SequenceNode lf = (SequenceNode) leaves.elementAt(i);
+       SequenceNode lf = leaves.elementAt(i);
  
        if (lf.isPlaceholder())
        {
          }
          else
          {
 -          g.setColor(av.getSequenceColour(seq).darker());
 +          g.setColor(ColorUtils.getColor(av.getSequenceColour(seq))
 +                  .darker());
          }
        }
        else
            for (int a = 0; a < aps.length; a++)
            {
              final SequenceI seq = (SequenceI) node.element();
 -            aps[a].av.setSequenceColour(seq, c);
 +            aps[a].av.setSequenceColour(seq, new Colour(c));
            }
          }
        }
    }
  
    /**
-    * DOCUMENT ME!
+    * Empty method to satisfy the MouseListener interface
     * 
     * @param e
-    *          DOCUMENT ME!
     */
    @Override
    public void mouseReleased(MouseEvent e)
    {
+     /*
+      * isPopupTrigger is set on mouseReleased on Windows
+      */
+     if (e.isPopupTrigger())
+     {
+       chooseSubtreeColour();
+       e.consume(); // prevent mouseClicked happening
+     }
    }
  
    /**
-    * DOCUMENT ME!
+    * Empty method to satisfy the MouseListener interface
     * 
     * @param e
-    *          DOCUMENT ME!
     */
    @Override
    public void mouseEntered(MouseEvent e)
    }
  
    /**
-    * DOCUMENT ME!
+    * Empty method to satisfy the MouseListener interface
     * 
     * @param e
-    *          DOCUMENT ME!
     */
    @Override
    public void mouseExited(MouseEvent e)
    }
  
    /**
-    * DOCUMENT ME!
+    * Handles a mouse click on a tree node (clicks elsewhere are handled in
+    * mousePressed). Click selects the sub-tree, double-click swaps leaf nodes
+    * order, right-click opens a dialogue to choose colour for the sub-tree.
     * 
     * @param e
-    *          DOCUMENT ME!
     */
    @Override
    public void mouseClicked(MouseEvent evt)
    {
-     if (highlightNode != null)
+     if (highlightNode == null)
      {
-       if (evt.isPopupTrigger())
-       {
-         Color col = JColorChooser.showDialog(this,
-                 MessageManager.getString("label.select_subtree_colour"),
-                 highlightNode.color);
-         if (col != null)
-         {
-           setColor(highlightNode, col);
-         }
-       }
-       else if (evt.getClickCount() > 1)
+       return;
+     }
+     if (evt.getClickCount() > 1)
+     {
+       tree.swapNodes(highlightNode);
+       tree.reCount(tree.getTopNode());
+       tree.findHeight(tree.getTopNode());
+     }
+     else
+     {
+       Vector<SequenceNode> leaves = tree.findLeaves(highlightNode);
+       for (int i = 0; i < leaves.size(); i++)
        {
-         tree.swapNodes(highlightNode);
-         tree.reCount(tree.getTopNode());
-         tree.findHeight(tree.getTopNode());
+         SequenceI seq = (SequenceI) leaves.elementAt(i)
+                 .element();
+         treeSelectionChanged(seq);
        }
-       else
-       {
-         Vector leaves = new Vector();
-         tree.findLeaves(highlightNode, leaves);
+       av.sendSelection();
+     }
  
-         for (int i = 0; i < leaves.size(); i++)
-         {
-           SequenceI seq = (SequenceI) ((SequenceNode) leaves.elementAt(i))
-                   .element();
-           treeSelectionChanged(seq);
-         }
-         av.sendSelection();
-       }
+     PaintRefresher.Refresh(tp, av.getSequenceSetId());
+     repaint();
+   }
  
-       PaintRefresher.Refresh(tp, av.getSequenceSetId());
+   /**
+    * Offer the user the option to choose a colour for the highlighted node and
+    * its children; this colour is also applied to the corresponding sequence ids
+    * in the alignment
+    */
+   void chooseSubtreeColour()
+   {
+     Color col = JColorChooser.showDialog(this,
+             MessageManager.getString("label.select_subtree_colour"),
+             highlightNode.color);
+     if (col != null)
+     {
+       setColor(highlightNode, col);
+       PaintRefresher.Refresh(tp, ap.av.getSequenceSetId());
        repaint();
      }
    }
    }
  
    /**
-    * DOCUMENT ME!
+    * Handles a mouse press on a sequence name or the tree background canvas
+    * (click on a node is handled in mouseClicked). The action is to create
+    * groups by partitioning the tree at the mouse position. Colours for the
+    * groups (and sequence names) are generated randomly.
     * 
     * @param e
-    *          DOCUMENT ME!
     */
    @Override
    public void mousePressed(MouseEvent e)
    {
      av.setCurrentTree(tree);
  
+     /*
+      * isPopupTrigger is set for mousePressed (Mac)
+      * or mouseReleased (Windows)
+      */
+     if (e.isPopupTrigger())
+     {
+       if (highlightNode != null)
+       {
+         chooseSubtreeColour();
+       }
+       return;
+     }
+     /*
+      * defer right-click handling on Windows to
+      * mouseClicked; note isRightMouseButton
+      * also matches Cmd-click on Mac which should do
+      * nothing here
+      */
+     if (SwingUtilities.isRightMouseButton(e))
+     {
+       return;
+     }
      int x = e.getX();
      int y = e.getY();
  
      {
        Color col = new Color((int) (Math.random() * 255),
                (int) (Math.random() * 255), (int) (Math.random() * 255));
-       setColor((SequenceNode) tree.getGroups().elementAt(i), col.brighter());
+       setColor(tree.getGroups().elementAt(i), col.brighter());
  
-       Vector l = tree.findLeaves(
-               (SequenceNode) tree.getGroups().elementAt(i), new Vector());
+       Vector<SequenceNode> l = tree.findLeaves(tree
+               .getGroups().elementAt(i));
  
-       Vector sequences = new Vector();
+       Vector<SequenceI> sequences = new Vector<SequenceI>();
  
        for (int j = 0; j < l.size(); j++)
        {
-         SequenceI s1 = (SequenceI) ((SequenceNode) l.elementAt(j))
+         SequenceI s1 = (SequenceI) l.elementAt(j)
                  .element();
  
          if (!sequences.contains(s1))
                  .getCodingComplement();
          if (codingComplement != null)
          {
-           if (codingComplement != null)
+           SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(sg, av,
+                   codingComplement);
+           if (mappedGroup.getSequences().size() > 0)
            {
-             SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(sg,
-                     av, codingComplement);
-             if (mappedGroup.getSequences().size() > 0)
+             codingComplement.getAlignment().addGroup(mappedGroup);
+             for (SequenceI seq : mappedGroup.getSequences())
              {
-               codingComplement.getAlignment().addGroup(mappedGroup);
-               for (SequenceI seq : mappedGroup.getSequences())
-               {
-                 codingComplement.setSequenceColour(seq,
-                         new Colour(col.brighter()));
-               }
 -              codingComplement.setSequenceColour(seq, col.brighter());
++              codingComplement.setSequenceColour(seq,
++                      new Colour(col.brighter()));
              }
            }
          }
        {
          ((AlignViewport) codingComplement).getAlignPanel()
                  .updateAnnotation();
        }
      }
    }
  
    /**
@@@ -30,7 -30,6 +30,7 @@@ import jalview.datamodel.GraphLine
  import jalview.datamodel.HiddenSequences;
  import jalview.datamodel.SequenceGroup;
  import jalview.datamodel.SequenceI;
 +import jalview.schemes.Colour;
  import jalview.schemes.ColourSchemeI;
  import jalview.schemes.ColourSchemeProperty;
  import jalview.schemes.ResidueProperties;
@@@ -1671,13 -1670,11 +1671,13 @@@ public class AnnotationFil
          }
          else if (key.equalsIgnoreCase("textCol1"))
          {
 -          sg.textColour = new UserColourScheme(value).findColour('A');
 +          sg.textColour = new Colour(
 +                  new UserColourScheme(value).findColour('A'));
          }
          else if (key.equalsIgnoreCase("textCol2"))
          {
 -          sg.textColour2 = new UserColourScheme(value).findColour('A');
 +          sg.textColour2 = new Colour(
 +                  new UserColourScheme(value).findColour('A'));
          }
          else if (key.equalsIgnoreCase("textColThreshold"))
          {
     */
    public String printCSVAnnotations(AlignmentAnnotation[] annotations)
    {
+     if (annotations == null)
+     {
+       return "";
+     }
      StringBuffer sp = new StringBuffer();
      for (int i = 0; i < annotations.length; i++)
      {
@@@ -36,7 -36,6 +36,6 @@@ import jalview.io.gff.GffHelperFactory
  import jalview.io.gff.GffHelperI;
  import jalview.schemes.FeatureColour;
  import jalview.schemes.UserColourScheme;
- import jalview.util.Format;
  import jalview.util.MapList;
  import jalview.util.ParseHtmlBodyAndLinks;
  import jalview.util.StringUtils;
@@@ -49,7 -48,6 +48,7 @@@ import java.util.Iterator
  import java.util.List;
  import java.util.Map;
  import java.util.Map.Entry;
 +import java.util.StringTokenizer;
  
  /**
   * Parses and writes features files, which may be in Jalview, GFF2 or GFF3
@@@ -233,12 -231,18 +232,18 @@@ public class FeaturesFile extends Align
            else if (ft.equalsIgnoreCase("endgroup"))
            {
              // We should check whether this is the current group,
-             // but at present theres no way of showing more than 1 group
+             // but at present there's no way of showing more than 1 group
              featureGroup = null;
            }
            else
            {
-             parseFeatureColour(line, ft, gffColumns, colours);
+             String colscheme = gffColumns[1];
+             FeatureColourI colour = FeatureColour
+                     .parseJalviewFeatureColour(colscheme);
+             if (colour != null)
+             {
+               colours.put(ft, colour);
+             }
            }
            continue;
          }
    }
  
    /**
 +   * Process a feature type colour specification
 +   * 
 +   * @param line
 +   *          the current input line (for error messages only)
 +   * @param featureType
 +   *          the first token on the line
 +   * @param gffColumns
 +   *          holds tokens on the line
 +   * @param colours
 +   *          map to which to add derived colour specification
 +   */
 +  protected void parseFeatureColour(String line, String featureType,
 +          String[] gffColumns, Map<String, FeatureColourI> colours)
 +  {
 +    FeatureColourI colour = null;
 +    String colscheme = gffColumns[1];
 +    if (colscheme.indexOf("|") > -1
 +            || colscheme.trim().equalsIgnoreCase("label"))
 +    {
 +      colour = parseGraduatedColourScheme(line, colscheme);
 +    }
 +    else
 +    {
 +      UserColourScheme ucs = new UserColourScheme(colscheme);
 +      colour = new FeatureColour(ucs.findColour('A'));
 +    }
 +    if (colour != null)
 +    {
 +      colours.put(featureType, colour);
 +    }
 +  }
 +
 +  /**
 +   * Parse a Jalview graduated colour descriptor
 +   * 
 +   * @param line
 +   * @param colourDescriptor
 +   * @return
 +   */
 +  protected FeatureColourI parseGraduatedColourScheme(String line,
 +          String colourDescriptor)
 +  {
 +    // Parse '|' separated graduated colourscheme fields:
 +    // [label|][mincolour|maxcolour|[absolute|]minvalue|maxvalue|thresholdtype|thresholdvalue]
 +    // can either provide 'label' only, first is optional, next two
 +    // colors are required (but may be
 +    // left blank), next is optional, nxt two min/max are required.
 +    // first is either 'label'
 +    // first/second and third are both hexadecimal or word equivalent
 +    // colour.
 +    // next two are values parsed as floats.
 +    // fifth is either 'above','below', or 'none'.
 +    // sixth is a float value and only required when fifth is either
 +    // 'above' or 'below'.
 +    StringTokenizer gcol = new StringTokenizer(colourDescriptor, "|", true);
 +    // set defaults
 +    float min = Float.MIN_VALUE, max = Float.MAX_VALUE;
 +    boolean labelCol = false;
 +    // Parse spec line
 +    String mincol = gcol.nextToken();
 +    if (mincol == "|")
 +    {
 +      System.err
 +              .println("Expected either 'label' or a colour specification in the line: "
 +                      + line);
 +      return null;
 +    }
 +    String maxcol = null;
 +    if (mincol.toLowerCase().indexOf("label") == 0)
 +    {
 +      labelCol = true;
 +      mincol = (gcol.hasMoreTokens() ? gcol.nextToken() : null); // skip '|'
 +      mincol = (gcol.hasMoreTokens() ? gcol.nextToken() : null);
 +    }
 +    String abso = null, minval, maxval;
 +    if (mincol != null)
 +    {
 +      // at least four more tokens
 +      if (mincol.equals("|"))
 +      {
 +        mincol = "";
 +      }
 +      else
 +      {
 +        gcol.nextToken(); // skip next '|'
 +      }
 +      // continue parsing rest of line
 +      maxcol = gcol.nextToken();
 +      if (maxcol.equals("|"))
 +      {
 +        maxcol = "";
 +      }
 +      else
 +      {
 +        gcol.nextToken(); // skip next '|'
 +      }
 +      abso = gcol.nextToken();
 +      gcol.nextToken(); // skip next '|'
 +      if (abso.toLowerCase().indexOf("abso") != 0)
 +      {
 +        minval = abso;
 +        abso = null;
 +      }
 +      else
 +      {
 +        minval = gcol.nextToken();
 +        gcol.nextToken(); // skip next '|'
 +      }
 +      maxval = gcol.nextToken();
 +      if (gcol.hasMoreTokens())
 +      {
 +        gcol.nextToken(); // skip next '|'
 +      }
 +      try
 +      {
 +        if (minval.length() > 0)
 +        {
 +          min = Float.valueOf(minval);
 +        }
 +      } catch (Exception e)
 +      {
 +        System.err
 +                .println("Couldn't parse the minimum value for graduated colour for type ("
 +                        + colourDescriptor
 +                        + ") - did you misspell 'auto' for the optional automatic colour switch ?");
 +        e.printStackTrace();
 +      }
 +      try
 +      {
 +        if (maxval.length() > 0)
 +        {
 +          max = Float.valueOf(maxval);
 +        }
 +      } catch (Exception e)
 +      {
 +        System.err
 +                .println("Couldn't parse the maximum value for graduated colour for type ("
 +                        + colourDescriptor + ")");
 +        e.printStackTrace();
 +      }
 +    }
 +    else
 +    {
 +      // add in some dummy min/max colours for the label-only
 +      // colourscheme.
 +      mincol = "FFFFFF";
 +      maxcol = "000000";
 +    }
 +
 +    FeatureColourI colour = null;
 +    try
 +    {
 +      colour = new FeatureColour(
 +              new UserColourScheme(mincol).findColour('A'),
 +              new UserColourScheme(maxcol).findColour('A'), min, max);
 +    } catch (Exception e)
 +    {
 +      System.err.println("Couldn't parse the graduated colour scheme ("
 +              + colourDescriptor + ")");
 +      e.printStackTrace();
 +    }
 +    if (colour != null)
 +    {
 +      colour.setColourByLabel(labelCol);
 +      colour.setAutoScaled(abso == null);
 +      // add in any additional parameters
 +      String ttype = null, tval = null;
 +      boolean hasThreshold = false;
 +      if (gcol.hasMoreTokens())
 +      {
 +        // threshold type and possibly a threshold value
 +        ttype = gcol.nextToken();
 +        if (ttype.toLowerCase().startsWith("below"))
 +        {
 +          colour.setBelowThreshold(true);
 +          hasThreshold = true;
 +        }
 +        else if (ttype.toLowerCase().startsWith("above"))
 +        {
 +          colour.setAboveThreshold(true);
 +          hasThreshold = true;
 +        }
 +        else
 +        {
 +          if (!ttype.toLowerCase().startsWith("no"))
 +          {
 +            System.err.println("Ignoring unrecognised threshold type : "
 +                    + ttype);
 +          }
 +        }
 +      }
 +      if (hasThreshold)
 +      {
 +        try
 +        {
 +          gcol.nextToken();
 +          tval = gcol.nextToken();
 +          colour.setThreshold(new Float(tval).floatValue());
 +        } catch (Exception e)
 +        {
 +          System.err.println("Couldn't parse threshold value as a float: ("
 +                  + tval + ")");
 +          e.printStackTrace();
 +        }
 +      }
 +      // parse the thresh-is-min token ?
 +      if (gcol.hasMoreTokens())
 +      {
 +        System.err
 +                .println("Ignoring additional tokens in parameters in graduated colour specification\n");
 +        while (gcol.hasMoreTokens())
 +        {
 +          System.err.println("|" + gcol.nextToken());
 +        }
 +        System.err.println("\n");
 +      }
 +    }
 +    return colour;
 +  }
 +
 +  /**
     * clear any temporary handles used to speed up ID matching
     */
    protected void resetMatcher()
     * 
     * @param sequences
     *          source of features
 -   * @param visible
 +   * @param featureColours
     *          hash of Colours for each feature type
     * @param visOnly
     *          when true only feature types in 'visible' will be output
     * @return features file contents
     */
    public String printJalviewFormat(SequenceI[] sequences,
 -          Map<String, FeatureColourI> visible, boolean visOnly,
 +          Map<String, FeatureColourI> featureColours, boolean visOnly,
            boolean nonpos)
    {
      StringBuilder out = new StringBuilder(256);
      boolean featuresGen = false;
 -    if (visOnly && !nonpos && (visible == null || visible.size() < 1))
 +    if (visOnly && !nonpos
 +            && (featureColours == null || featureColours.size() < 1))
      {
        // no point continuing.
        return "No Features Visible";
      }
  
 -    if (visible != null && visOnly)
 +    if (featureColours != null && visOnly)
      {
        // write feature colours only if we're given them and we are generating
        // viewed features
        // TODO: decide if feature links should also be written here ?
 -      Iterator<String> en = visible.keySet().iterator();
 +      Iterator<String> en = featureColours.keySet().iterator();
-       String featureType, color;
        while (en.hasNext())
        {
-         featureType = en.next();
-         FeatureColourI fc = featureColours.get(featureType);
-         if (fc.isSimpleColour())
-         {
-           color = Format.getHexString(fc.getColour());
-         }
-         else
-         {
-           color = (fc.isColourByLabel() ? "label|" : "")
-                   + Format.getHexString(fc.getMinColour()) + "|"
-                   + Format.getHexString(fc.getMaxColour())
-                   + (fc.isAutoScaled() ? "|" : "|abso|") + fc.getMin() + "|"
-                   + fc.getMax() + "|";
-             if (fc.isBelowThreshold())
-             {
-               color += "below";
-             }
-             else if (fc.isAboveThreshold())
-             {
-               color += "above";
-             }
-             // add the value
-             color += "|" + fc.getThreshold();
-           }
- //          else
- //          {
- //            color += "none";
- //          }
-         // else
-         // {
-         // // legacy support for integer objects containing colour triplet
-         // values
-         // color = Format.getHexString(new Color(Integer
-         // .parseInt(fc.toString())));
-         // }
-         out.append(featureType);
-         out.append(TAB);
-         out.append(color);
-         out.append(newline);
 -        String featureType = en.next().toString();
 -        FeatureColourI colour = visible.get(featureType);
++        String featureType = en.next();
++        FeatureColourI colour = featureColours.get(featureType);
+         out.append(colour.toJalviewFormat(featureType)).append(newline);
        }
      }
      // Work out which groups are both present and visible
      List<String> groups = new ArrayList<String>();
      int groupIndex = 0;
          {
            isnonpos = features[j].begin == 0 && features[j].end == 0;
            if ((!nonpos && isnonpos)
 -                  || (!isnonpos && visOnly && !visible
 +                  || (!isnonpos && visOnly && !featureColours
                            .containsKey(features[j].type)))
            {
              continue;
          features = sequences[i].getSequenceFeatures();
          if (features != null)
          {
-           for (int j = 0; j < features.length; j++)
+           for (SequenceFeature sequenceFeature : features)
            {
-             isnonpos = features[j].begin == 0 && features[j].end == 0;
+             isnonpos = sequenceFeature.begin == 0 && sequenceFeature.end == 0;
              if ((!nonpos && isnonpos)
 -                    || (!isnonpos && visOnly && !visible
 +                    || (!isnonpos && visOnly && !featureColours
-                             .containsKey(features[j].type)))
+                             .containsKey(sequenceFeature.type)))
              {
                // skip if feature is nonpos and we ignore them or if we only
                // output visible and it isn't non-pos and it's not visible
              }
  
              if (group != null
-                     && (features[j].featureGroup == null || !features[j].featureGroup
+                     && (sequenceFeature.featureGroup == null || !sequenceFeature.featureGroup
                              .equals(group)))
              {
                continue;
              }
  
-             if (group == null && features[j].featureGroup != null)
+             if (group == null && sequenceFeature.featureGroup != null)
              {
                continue;
              }
              // we have features to output
              featuresGen = true;
-             if (features[j].description == null
-                     || features[j].description.equals(""))
+             if (sequenceFeature.description == null
+                     || sequenceFeature.description.equals(""))
              {
-               out.append(features[j].type).append(TAB);
+               out.append(sequenceFeature.type).append(TAB);
              }
              else
              {
-               if (features[j].links != null
-                       && features[j].getDescription().indexOf("<html>") == -1)
+               if (sequenceFeature.links != null
+                       && sequenceFeature.getDescription().indexOf("<html>") == -1)
                {
                  out.append("<html>");
                }
  
-               out.append(features[j].description + " ");
-               if (features[j].links != null)
+               out.append(sequenceFeature.description);
+               if (sequenceFeature.links != null)
                {
-                 for (int l = 0; l < features[j].links.size(); l++)
+                 for (int l = 0; l < sequenceFeature.links.size(); l++)
                  {
-                   String label = features[j].links.elementAt(l).toString();
+                   String label = sequenceFeature.links.elementAt(l);
                    String href = label.substring(label.indexOf("|") + 1);
                    label = label.substring(0, label.indexOf("|"));
  
-                   if (features[j].description.indexOf(href) == -1)
+                   if (sequenceFeature.description.indexOf(href) == -1)
                    {
-                     out.append("<a href=\"" + href + "\">" + label + "</a>");
+                     out.append(" <a href=\"" + href + "\">" + label
+                             + "</a>");
                    }
                  }
  
-                 if (features[j].getDescription().indexOf("</html>") == -1)
+                 if (sequenceFeature.getDescription().indexOf("</html>") == -1)
                  {
                    out.append("</html>");
                  }
              }
              out.append(sequences[i].getName());
              out.append("\t-1\t");
-             out.append(features[j].begin);
+             out.append(sequenceFeature.begin);
              out.append(TAB);
-             out.append(features[j].end);
+             out.append(sequenceFeature.end);
              out.append(TAB);
-             out.append(features[j].type);
-             if (!Float.isNaN(features[j].score))
+             out.append(sequenceFeature.type);
+             if (!Float.isNaN(sequenceFeature.score))
              {
                out.append(TAB);
-               out.append(features[j].score);
+               out.append(sequenceFeature.score);
              }
              out.append(newline);
            }
     * 
     * @param sequences
     *          the sequences whose features are to be output
 -   * @param visible
 +   * @param featureColours
     *          a map whose keys are the type names of visible features
     * @param outputVisibleOnly
     * @param includeNonPositionalFeatures
     * @return
     */
    public String printGffFormat(SequenceI[] sequences,
 -          Map<String, FeatureColourI> visible, boolean outputVisibleOnly,
 +          Map<String, FeatureColourI> featureColours,
 +          boolean outputVisibleOnly,
            boolean includeNonPositionalFeatures)
    {
      StringBuilder out = new StringBuilder(256);
-     out.append(String.format("%s %d\n", GFF_VERSION, gffVersion));
+     int version = gffVersion == 0 ? 2 : gffVersion;
+     out.append(String.format("%s %d\n", GFF_VERSION, version));
      String source;
      boolean isnonpos;
      for (SequenceI seq : sequences)
            // TODO why the test !isnonpos here?
            // what about not visible non-positional features?
            if (!isnonpos && outputVisibleOnly
 -                  && !visible.containsKey(sf.type))
 +                  && !featureColours.containsKey(sf.type))
            {
              /*
               * ignore not visible features if not wanted
@@@ -30,7 -30,6 +30,7 @@@ import jalview.gui.HTMLOptions
  import jalview.gui.IProgressIndicator;
  import jalview.gui.OOMWarning;
  import jalview.math.AlignmentDimension;
 +import jalview.util.ColorUtils;
  import jalview.util.MessageManager;
  
  import java.awt.Color;
@@@ -52,6 -51,13 +52,13 @@@ public class HtmlSvgOutpu
  
    AlignmentPanel ap;
  
+   private IProgressIndicator pIndicator;
+   private long pSessionId;
+   private boolean headless;
    public HtmlSvgOutput(File file, AlignmentPanel ap)
    {
      this.av = ap.av;
  
    public void generateHtmlSvgOutput(File file)
    {
-     IProgressIndicator pIndicator = ap.alignFrame;
-     long pSessionId = System.currentTimeMillis();
+     pIndicator = ap.alignFrame;
+     pSessionId = System.currentTimeMillis();
      try
      {
-       boolean headless = (System.getProperty("java.awt.headless") != null && System
+       headless = (System.getProperty("java.awt.headless") != null && System
                .getProperty("java.awt.headless").equals("true"));
        if (file == null)
        {
-         if (pIndicator != null && !headless)
-         {
-           pIndicator.setProgressBar(MessageManager.formatMessage(
-                   "status.waiting_for_user_to_select_output_file", "HTML"),
-                   pSessionId);
-         }
+         setProgressMessage(MessageManager.formatMessage(
+                 "status.waiting_for_user_to_select_output_file", "HTML"));
          JalviewFileChooser chooser = getHTMLChooser();
          chooser.setFileView(new jalview.io.JalviewFileView());
          chooser.setDialogTitle(ap.alignFrame.getTitle());
            jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
                    .getSelectedFile().getParent());
            file = chooser.getSelectedFile();
+           ap.alignFrame.repaint();
          }
          else
          {
-           if (pIndicator != null && !headless)
-         {
-             pIndicator.setProgressBar(MessageManager.formatMessage(
-                     "status.cancelled_image_export_operation", "HTML"),
-                     pSessionId);
-           }
+           setProgressMessage(MessageManager.formatMessage(
+                   "status.cancelled_image_export_operation", "HTML"));
            return;
          }
        }
-       AlignmentDimension aDimension = ap.getAlignmentDimension();
-       SVGGraphics2D g1 = new SVGGraphics2D(aDimension.getWidth(),
-               aDimension.getHeight());
-       SVGGraphics2D g2 = new SVGGraphics2D(aDimension.getWidth(),
-               aDimension.getHeight());
-       String renderStyle = jalview.bin.Cache.getDefault("HTML_RENDERING",
-               "Prompt each time");
-       // If we need to prompt, and if the GUI is visible then
-       // Prompt for rendering style
-       if (renderStyle.equalsIgnoreCase("Prompt each time")
-               && !(System.getProperty("java.awt.headless") != null && System
-                       .getProperty("java.awt.headless").equals("true")))
+     } catch (Exception e)
+     {
+       pIndicator.setProgressBar(MessageManager.formatMessage(
+               "info.error_creating_file", "HTML"), pSessionId);
+       e.printStackTrace();
+       return;
+     }
+     final File fileX = file;
+     new Thread()
+     {
+       @Override
+       public void run()
        {
-         HTMLOptions svgOption = new HTMLOptions();
-         renderStyle = svgOption.getValue();
-         if (renderStyle == null || svgOption.cancelled)
+         try
          {
-           return;
-         }
-       }
+           setProgressMessage(null);
+           setProgressMessage(MessageManager
+ .formatMessage(
+                   "status.exporting_alignment_as_x_file", "HTML"));
+           AlignmentDimension aDimension = ap.getAlignmentDimension();
+           SVGGraphics2D g1 = new SVGGraphics2D(aDimension.getWidth(),
+                   aDimension.getHeight());
+           SVGGraphics2D g2 = new SVGGraphics2D(aDimension.getWidth(),
+                   aDimension.getHeight());
  
-       if (renderStyle.equalsIgnoreCase("lineart"))
-       {
-         g1.setRenderingHint(SVGHints.KEY_DRAW_STRING_TYPE,
-                 SVGHints.VALUE_DRAW_STRING_TYPE_VECTOR);
-         g2.setRenderingHint(SVGHints.KEY_DRAW_STRING_TYPE,
-                 SVGHints.VALUE_DRAW_STRING_TYPE_VECTOR);
-       }
-       printUnwrapped(aDimension.getWidth(), aDimension.getHeight(), 0, g1,
-               g2);
-       String titleSvgData = g1.getSVGDocument();
-       String alignSvgData = g2.getSVGDocument();
-       String jsonData = null;
-       boolean isEmbbedBioJSON = Boolean.valueOf(jalview.bin.Cache
-               .getDefault("EXPORT_EMBBED_BIOJSON", "true"));
-       if (isEmbbedBioJSON)
-       {
-         AlignExportSettingI exportSettings = new AlignExportSettingI()
-         {
-           @Override
-           public boolean isExportHiddenSequences()
-           {
-             return true;
-           }
+           String renderStyle = jalview.bin.Cache.getDefault(
+                   "HTML_RENDERING", "Prompt each time");
  
-           @Override
-           public boolean isExportHiddenColumns()
+           // If we need to prompt, and if the GUI is visible then
+           // Prompt for rendering style
+           if (renderStyle.equalsIgnoreCase("Prompt each time")
+                   && !(System.getProperty("java.awt.headless") != null && System
+                           .getProperty("java.awt.headless").equals("true")))
            {
-             return true;
-           }
+             HTMLOptions svgOption = new HTMLOptions();
+             renderStyle = svgOption.getValue();
  
-           @Override
-           public boolean isExportAnnotations()
-           {
-             return true;
+             if (renderStyle == null || svgOption.cancelled)
+             {
+               setProgressMessage(MessageManager.formatMessage(
+                       "status.cancelled_image_export_operation", "HTML"));
+               return;
+             }
            }
  
-           @Override
-           public boolean isExportFeatures()
+           if (renderStyle.equalsIgnoreCase("Lineart"))
            {
-             return true;
+             g1.setRenderingHint(SVGHints.KEY_DRAW_STRING_TYPE,
+                     SVGHints.VALUE_DRAW_STRING_TYPE_VECTOR);
+             g2.setRenderingHint(SVGHints.KEY_DRAW_STRING_TYPE,
+                     SVGHints.VALUE_DRAW_STRING_TYPE_VECTOR);
            }
+           printUnwrapped(aDimension.getWidth(), aDimension.getHeight(), 0,
+                   g1, g2);
  
-           @Override
-           public boolean isExportGroups()
+           String titleSvgData = g1.getSVGDocument();
+           String alignSvgData = g2.getSVGDocument();
+           String jsonData = null;
+           boolean isEmbbedBioJSON = Boolean.valueOf(jalview.bin.Cache
+                   .getDefault("EXPORT_EMBBED_BIOJSON", "true"));
+           if (isEmbbedBioJSON)
            {
-             return true;
-           }
+             AlignExportSettingI exportSettings = new AlignExportSettingI()
+             {
+               @Override
+               public boolean isExportHiddenSequences()
+               {
+                 return true;
+               }
+               @Override
+               public boolean isExportHiddenColumns()
+               {
+                 return true;
+               }
+               @Override
+               public boolean isExportAnnotations()
+               {
+                 return true;
+               }
+               @Override
+               public boolean isExportFeatures()
+               {
+                 return true;
+               }
  
-           @Override
-           public boolean isCancelled()
+               @Override
+               public boolean isExportGroups()
+               {
+                 return true;
+               }
+               @Override
+               public boolean isCancelled()
+               {
+                 return false;
+               }
+             };
+             AlignmentExportData exportData = jalview.gui.AlignFrame
+                     .getAlignmentForExport(JSONFile.FILE_DESC, av,
+                             exportSettings);
+             jsonData = new FormatAdapter(ap, exportData.getSettings())
+                     .formatSequences(JSONFile.FILE_DESC,
+                             exportData.getAlignment(),
+                             exportData.getOmitHidden(),
+                             exportData.getStartEndPostions(),
+                             av.getColumnSelection());
+           }
+           String htmlData = getHtml(titleSvgData, alignSvgData, jsonData);
+           FileOutputStream out = new FileOutputStream(fileX);
+           out.write(htmlData.getBytes());
+           out.flush();
+           out.close();
+           if (!(System.getProperty("java.awt.headless") != null && System
+                   .getProperty("java.awt.headless").equals("true")))
            {
-             return false;
+             jalview.util.BrowserLauncher.openURL("file:///" + fileX);
            }
-         };
-         AlignmentExportData exportData = jalview.gui.AlignFrame
-                 .getAlignmentForExport(JSONFile.FILE_DESC, av,
-                         exportSettings);
-         jsonData = new FormatAdapter(ap, exportData.getSettings())
-                 .formatSequences(JSONFile.FILE_DESC,
-                         exportData.getAlignment(),
-                         exportData.getOmitHidden(),
-                         exportData.getStartEndPostions(),
-                         av.getColumnSelection());
-       }
-       String htmlData = getHtml(titleSvgData, alignSvgData, jsonData);
-       FileOutputStream out = new FileOutputStream(file);
-       out.write(htmlData.getBytes());
-       out.flush();
-       out.close();
-       if (!(System.getProperty("java.awt.headless") != null && System
-               .getProperty("java.awt.headless").equals("true")))
-       {
-         jalview.util.BrowserLauncher.openURL("file:///" + file);
-       }
-       if (pIndicator != null && !headless)
-       {
-         pIndicator.setProgressBar(MessageManager.formatMessage(
-                 "status.export_complete", "HTML"), pSessionId);
+         } catch (OutOfMemoryError err)
+         {
+           System.out.println("########################\n"
+                   + "OUT OF MEMORY " + fileX + "\n"
+                   + "########################");
+           new OOMWarning("Creating Image for " + fileX, err);
+         } catch (Exception e)
+         {
+           e.printStackTrace();
+           pIndicator.setProgressBar(MessageManager.formatMessage(
+                   "info.error_creating_file", "HTML"), pSessionId);
+         }
+         setProgressMessage(MessageManager.formatMessage(
+                 "status.export_complete", "HTML"));
        }
-     } catch (OutOfMemoryError err)
+     }.start();
+   }
+   private void setProgressMessage(String message)
+   {
+     if (pIndicator != null && !headless)
      {
-       System.out.println("########################\n" + "OUT OF MEMORY "
-               + file + "\n" + "########################");
-       new OOMWarning("Creating Image for " + file, err);
-     } catch (Exception e)
+       pIndicator.setProgressBar(message, pSessionId);
+     }
+     else
      {
-       e.printStackTrace();
-       pIndicator.setProgressBar(MessageManager.formatMessage(
-               "info.error_creating_file", "HTML"), pSessionId);
+       System.out.println(message);
      }
    }
  
        }
        else
        {
 -        currentColor = av.getSequenceColour(seq);
 +        currentColor = ColorUtils.getColor(av.getSequenceColour(seq));
          currentTextColor = Color.black;
        }
        pg[0].setColor(currentColor);
@@@ -46,7 -46,6 +46,7 @@@ import jalview.json.binding.biojson.v1.
  import jalview.json.binding.biojson.v1.SequenceFeaturesPojo;
  import jalview.json.binding.biojson.v1.SequenceGrpPojo;
  import jalview.json.binding.biojson.v1.SequencePojo;
 +import jalview.schemes.Colour;
  import jalview.schemes.ColourSchemeProperty;
  import jalview.schemes.UserColourScheme;
  import jalview.viewmodel.seqfeatures.FeaturesDisplayed;
@@@ -345,7 -344,7 +345,7 @@@ public class JSONFile extends AlignFil
                    String.valueOf(seq.hashCode()));
  
            String featureColour = (fr == null) ? null : jalview.util.Format
 -                  .getHexString(fr.findFeatureColour(Color.white, seq,
 +                  .getHexString(fr.findFeatureColour(Colour.white, seq,
                            seq.findIndex(sf.getBegin())));
            jsonFeature.setXstart(seq.findIndex(sf.getBegin()) - 1);
            jsonFeature.setXend(seq.findIndex(sf.getEnd()));
    @Override
    public void configureForView(AlignmentViewPanel avpanel)
    {
+     if (avpanel == null)
+     {
+       return;
+     }
      super.configureForView(avpanel);
      AlignViewportI viewport = avpanel.getAlignViewport();
      AlignmentI alignment = viewport.getAlignment();
      fr = avpanel.cloneFeatureRenderer();
  
      // Add non auto calculated annotation to AlignFile
-     for (AlignmentAnnotation annot : annots)
+     if (annots != null)
      {
-       if (annot != null && !annot.autoCalculated)
+       for (AlignmentAnnotation annot : annots)
        {
-         annotations.add(annot);
+         if (annot != null && !annot.autoCalculated)
+         {
+           annotations.add(annot);
+         }
        }
      }
      globalColourScheme = ColourSchemeProperty.getColourName(viewport
@@@ -1,11 -1,11 +1,11 @@@
  package jalview.io;
  
 +import jalview.api.ColorI;
  import jalview.api.FeatureColourI;
 +import jalview.schemes.Colour;
- import jalview.schemes.FeatureColourAdapter;
+ import jalview.schemes.FeatureColour;
  import jalview.schemes.FeatureSettingsAdapter;
  
 -import java.awt.Color;
 -
  public class PDBFeatureSettings extends FeatureSettingsAdapter
  {
  
    {
      if (type.equalsIgnoreCase(FEATURE_INSERTION))
      {
-       return new FeatureColourAdapter()
+       return new FeatureColour()
        {
  
          @Override
 -        public Color getColour()
 +        public ColorI getColour()
          {
 -          return Color.RED;
 +          return Colour.red;
          }
        };
      }
   */
  package jalview.renderer.seqfeatures;
  
+ import jalview.api.AlignViewportI;
 +import jalview.api.ColorI;
  import jalview.datamodel.SequenceFeature;
  import jalview.datamodel.SequenceI;
 +import jalview.schemes.Colour;
  import jalview.viewmodel.seqfeatures.FeatureRendererModel;
  
  import java.awt.AlphaComposite;
@@@ -52,6 -51,18 +53,18 @@@ public class FeatureRenderer extends Fe
  
    boolean av_validCharWidth, av_isShowSeqFeatureHeight;
  
+   private Integer currentColour;
+   /**
+    * Constructor given a viewport
+    * 
+    * @param viewport
+    */
+   public FeatureRenderer(AlignViewportI viewport)
+   {
+     this.av = viewport;
+   }
    protected void updateAvConfig()
    {
      av_charHeight = av.getCharHeight();
    BufferedImage offscreenImage;
  
    @Override
 -  public Color findFeatureColour(Color initialCol, SequenceI seq, int res)
 +  public ColorI findFeatureColour(ColorI initialCol, SequenceI seq, int res)
    {
 -    return new Color(findFeatureColour(initialCol.getRGB(), seq, res));
 +    return new Colour(findFeatureColour(initialCol.getRGB(), seq, res));
 +  }
 +
 +  public ColorI findFeatureColour(Color initialCol, SequenceI seq, int res)
 +  {
 +    return findFeatureColour(new Colour(initialCol), seq, res);
    }
  
    /**
  
    int epos;
  
-   private Integer currentColour;
    public synchronized void drawSequence(Graphics g, final SequenceI seq,
            int start, int end, int y1)
    {
@@@ -1,6 -1,5 +1,6 @@@
  package jalview.schemes;
  
 +import jalview.api.ColorI;
  import jalview.api.FeatureColourI;
  import jalview.datamodel.SequenceFeature;
  import jalview.util.Format;
@@@ -15,11 -14,11 +15,11 @@@ public class FeatureColour implements F
  {
    private static final String BAR = "|";
  
 -  final private Color colour;
 +  final private ColorI colour;
  
 -  final private Color minColour;
 +  final private ColorI minColour;
  
 -  final private Color maxColour;
 +  final private ColorI maxColour;
  
    private boolean graduatedColour;
  
          throw new IllegalArgumentException("Invalid colour descriptor: "
                  + descriptor);
        }
 -      return new FeatureColour(colour);
 +      return new FeatureColour(new Colour(colour));
      }
  
      /*
-      * autoScaled == true: colours range over actual score range; autoScaled ==
-      * false ('abso'): colours range over min/max range
 -     * autoScaled == true: colours range over actual score range
++     * autoScaled == true: colours range over actual score range; 
+      * autoScaled == false ('abso'): colours range over min/max range
       */
-     boolean autoScaled = false;
+     boolean autoScaled = true;
      String tok = null, minval, maxval;
      if (mincol != null)
      {
        }
        tok = gcol.nextToken();
        gcol.nextToken(); // skip next '|'
-       if (tok.toLowerCase().indexOf("abso") != 0)
+       if (tok.toLowerCase().startsWith("abso"))
        {
-         minval = tok;
-         autoScaled = true;
+         minval = gcol.nextToken();
+         gcol.nextToken(); // skip next '|'
+         autoScaled = false;
        }
        else
        {
-         minval = gcol.nextToken();
-         gcol.nextToken(); // skip next '|'
+         minval = tok;
        }
        maxval = gcol.nextToken();
        if (gcol.hasMoreTokens())
      FeatureColour featureColour;
      try
      {
 -      featureColour = new FeatureColour(
 -              new UserColourScheme(mincol).findColour('A'),
 -              new UserColourScheme(maxcol).findColour('A'), min, max);
 +      featureColour = new FeatureColour(new Colour(new UserColourScheme(
 +              mincol).findColour('A')), new Colour(new UserColourScheme(
 +              maxcol).findColour('A')), min, max);
        featureColour.setColourByLabel(labelColour);
        featureColour.setAutoScaled(autoScaled);
        // add in any additional parameters
     */
    public FeatureColour()
    {
 -    this((Color) null);
 +    this((ColorI) null);
    }
  
    /**
     * 
     * @param c
     */
 -  public FeatureColour(Color c)
 +  public FeatureColour(ColorI c)
    {
 -    minColour = Color.WHITE;
 -    maxColour = Color.BLACK;
 +    minColour = new Colour(Color.WHITE);
 +    maxColour = new Colour(Color.BLACK);
      minRed = 0f;
      minGreen = 0f;
      minBlue = 0f;
      colour = c;
    }
  
 +  public FeatureColour(Color c)
 +  {
 +    this(new Colour(c));
 +  }
 +
    /**
     * Constructor given a colour range and a score range
     * 
     * @param min
     * @param max
     */
 -  public FeatureColour(Color low, Color high, float min, float max)
 +  public FeatureColour(ColorI low, ColorI high, float min, float max)
    {
      graduatedColour = true;
      colour = null;
      }
    }
  
 +  public FeatureColour(Color low, Color high, float min, float max)
 +  {
 +    this(new Colour(low), new Colour(high), min, max);
 +  }
    /**
     * Copy constructor
     * 
     */
    public FeatureColour(FeatureColour fc)
    {
 -    graduatedColour = fc.graduatedColour;
      colour = fc.colour;
      minColour = fc.minColour;
      maxColour = fc.maxColour;
      return graduatedColour;
    }
  
--  /**
--   * Sets the 'graduated colour' flag. If true, also sets 'colour by label' to
--   * false.
--   */
-   @Override
-   public void setGraduatedColour(boolean b)
-   {
-     graduatedColour = b;
-     if (b)
-     {
-       setColourByLabel(false);
-     }
-   }
 -  void setGraduatedColour(boolean b)
 -  {
 -    graduatedColour = b;
 -    if (b)
 -    {
 -      setColourByLabel(false);
 -    }
 -  }
 -
    @Override
 -  public Color getColour()
 +  public ColorI getColour()
    {
      return colour;
    }
  
    @Override
 -  public Color getMinColour()
 +  public ColorI getMinColour()
    {
      return minColour;
    }
  
    @Override
 -  public Color getMaxColour()
 +  public ColorI getMaxColour()
    {
      return maxColour;
    }
        setGraduatedColour(false);
      }
    }
++
++  /**
++   * Sets the 'graduated colour' flag. If true, also sets 'colour by label' to
++   * false.
++   */
++  void setGraduatedColour(boolean b)
++  {
++    graduatedColour = b;
++    if (b)
++    {
++      setColourByLabel(false);
++    }
++  }
++
    @Override
    public boolean isBelowThreshold()
    {
     * @return
     */
    @Override
 -  public Color getColor(SequenceFeature feature)
 +  public ColorI getColor(SequenceFeature feature)
    {
      if (isColourByLabel())
      {
 -      return UserColourScheme
 -              .createColourFromName(feature.getDescription());
 +      return new Colour(UserColourScheme.createColourFromName(feature
 +              .getDescription()));
      }
  
      if (!isGraduatedColour())
      {
        scl = 1f;
      }
 -    return new Color(minRed + scl * deltaRed, minGreen + scl * deltaGreen, minBlue + scl * deltaBlue);
 +    return new Colour(new Color(minRed + scl * deltaRed, minGreen + scl
 +            * deltaGreen, minBlue + scl * deltaBlue));
    }
  
    /**
        {
          sb.append(Format.getHexString(getMinColour())).append(BAR);
          sb.append(Format.getHexString(getMaxColour())).append(BAR);
-         if (isAutoScaled())
+         if (!isAutoScaled())
          {
            sb.append("abso").append(BAR);
          }
index cc03dd3,0000000..869a8ca
mode 100644,000000..100644
--- /dev/null
@@@ -1,155 -1,0 +1,150 @@@
 +package jalview.schemes;
 +
 +import jalview.api.ColorI;
 +import jalview.api.FeatureColourI;
 +import jalview.datamodel.SequenceFeature;
 +
 +/**
 + * A convenience class with implementations of FeatureColourI methods. Override
 + * methods as required in subclasses.
 + */
 +public class FeatureColourAdapter implements FeatureColourI
 +{
 +  @Override
 +  public boolean isGraduatedColour()
 +  {
 +    return isColourByLabel() || isAboveThreshold() || isBelowThreshold();
 +  }
 +
 +  @Override
 +  public ColorI getColour()
 +  {
 +    return Colour.black;
 +  }
 +
 +  @Override
 +  public ColorI getMinColour()
 +  {
 +    return Colour.white;
 +  }
 +
 +  @Override
 +  public ColorI getMaxColour()
 +  {
 +    return Colour.black;
 +  }
 +
 +  @Override
 +  public boolean isColourByLabel()
 +  {
 +    return false;
 +  }
 +
 +  @Override
 +  public boolean isBelowThreshold()
 +  {
 +    return false;
 +  }
 +
 +  @Override
 +  public boolean isAboveThreshold()
 +  {
 +    return false;
 +  }
 +
 +  @Override
 +  public boolean isThresholdMinMax()
 +  {
 +    return false;
 +  }
 +
 +  @Override
 +  public float getThreshold()
 +  {
 +    return 0f;
 +  }
 +
 +  @Override
 +  public float getMax()
 +  {
 +    return 0;
 +  }
 +
 +  @Override
 +  public float getMin()
 +  {
 +    return 0;
 +  }
 +
 +  @Override
 +  public boolean hasThreshold()
 +  {
 +    return false;
 +  }
 +
 +  @Override
 +  public ColorI getColor(SequenceFeature feature)
 +  {
 +    return null;
 +  }
 +
 +  @Override
 +  public boolean isColored(SequenceFeature feature)
 +  {
 +    return false;
 +  }
 +
 +  @Override
 +  public void updateBounds(float min, float max)
 +  {
 +  }
 +
 +  @Override
 +  public String toJalviewFormat(String featureType)
 +  {
 +    return null;
 +  }
 +
 +  @Override
 +  public void setThreshold(float f)
 +  {
 +  }
 +
 +  @Override
 +  public boolean isAutoScaled()
 +  {
 +    return false;
 +  }
 +
 +  @Override
 +  public void setAutoScaled(boolean b)
 +  {
 +  }
 +
 +  @Override
 +  public boolean isSimpleColour()
 +  {
 +    return false;
 +  }
 +
 +  @Override
 +  public void setAboveThreshold(boolean b)
 +  {
 +  }
 +
 +  @Override
 +  public void setThresholdMinMax(boolean b)
 +  {
 +  }
 +
 +  @Override
 +  public void setBelowThreshold(boolean b)
 +  {
 +  }
 +
 +  @Override
 +  public void setColourByLabel(boolean b)
 +  {
 +  }
 +
-   @Override
-   public void setGraduatedColour(boolean b)
-   {
-   }
 +}
@@@ -25,7 -25,6 +25,7 @@@ import jalview.analysis.Conservation
  import jalview.api.AlignCalcManagerI;
  import jalview.api.AlignViewportI;
  import jalview.api.AlignmentViewPanel;
 +import jalview.api.ColorI;
  import jalview.api.FeaturesDisplayedI;
  import jalview.api.ViewStyleI;
  import jalview.commands.CommandI;
@@@ -43,7 -42,6 +43,7 @@@ import jalview.datamodel.SequenceCollec
  import jalview.datamodel.SequenceGroup;
  import jalview.datamodel.SequenceI;
  import jalview.schemes.Blosum62ColourScheme;
 +import jalview.schemes.Colour;
  import jalview.schemes.ColourSchemeI;
  import jalview.schemes.PIDColourScheme;
  import jalview.schemes.ResidueProperties;
@@@ -59,6 -57,7 +59,6 @@@ import jalview.workers.ComplementConsen
  import jalview.workers.ConsensusThread;
  import jalview.workers.StrucConsensusThread;
  
 -import java.awt.Color;
  import java.util.ArrayDeque;
  import java.util.ArrayList;
  import java.util.BitSet;
@@@ -845,14 -844,22 +845,22 @@@ public abstract class AlignmentViewpor
              && !al.getCodonFrames().isEmpty())
      {
        /*
-        * fudge - check first mapping is protein-to-nucleotide
+        * fudge - check first for protein-to-nucleotide mappings
         * (we don't want to do this for protein-to-protein)
         */
-       AlignedCodonFrame mapping = al.getCodonFrames().iterator().next();
-       // TODO hold mapping type e.g. dna-to-protein in AlignedCodonFrame?
-       MapList[] mapLists = mapping.getdnaToProt();
-       // mapLists can be empty if project load has not finished resolving seqs
-       if (mapLists.length > 0 && mapLists[0].getFromRatio() == 3)
+       boolean doConsensus = false;
+       for (AlignedCodonFrame mapping : al.getCodonFrames())
+       {
+         // TODO hold mapping type e.g. dna-to-protein in AlignedCodonFrame?
+         MapList[] mapLists = mapping.getdnaToProt();
+         // mapLists can be empty if project load has not finished resolving seqs
+         if (mapLists.length > 0 && mapLists[0].getFromRatio() == 3)
+         {
+           doConsensus = true;
+           break;
+         }
+       }
+       if (doConsensus)
        {
          if (calculator
                  .getRegisteredWorkersOfClass(ComplementConsensusThread.class) == null)
      {
        updateHiddenColumns();
      }
+     isColSelChanged(true);
    }
  
    /**
    }
  
    @Override
+   public boolean hasSelectedColumns()
+   {
+     ColumnSelection columnSelection = getColumnSelection();
+     return columnSelection != null && columnSelection.hasSelectedColumns();
+   }
+   @Override
    public boolean hasHiddenColumns()
    {
      return colSel != null && colSel.hasHiddenColumns();
     */
    public boolean isColSelChanged(boolean b)
    {
-     int hc = (colSel == null || colSel.isEmpty()) ? -1 : colSel
-             .hashCode();
+     int hc = (colSel == null || colSel.isEmpty()) ? -1 : colSel.hashCode();
      if (hc != -1 && hc != colselhash)
      {
        if (b)
  
    protected boolean showConsensus = true;
  
 -  private Map<SequenceI, Color> sequenceColours = new HashMap<SequenceI, Color>();
 +  private Map<SequenceI, ColorI> sequenceColours = new HashMap<SequenceI, ColorI>();
  
    protected SequenceAnnotationOrder sortAnnotationsBy = null;
  
  
      colSel.hideSelectedColumns();
      setSelectionGroup(null);
+     isColSelChanged(true);
    }
  
    public void hideColumns(int start, int end)
      {
        colSel.hideColumns(start, end);
      }
+     isColSelChanged(true);
    }
  
    public void showColumn(int col)
    {
      colSel.revealHiddenColumns(col);
+     isColSelChanged(true);
    }
  
    public void showAllHiddenColumns()
    {
      colSel.revealAllHiddenColumns();
+     isColSelChanged(true);
    }
  
    // common hide/show seq stuff
    }
  
    /**
+    * Hides the specified sequence, or the sequences it represents
+    * 
+    * @param sequence
+    *          the sequence to hide, or keep as representative
+    * @param representGroup
+    *          if true, hide the current selection group except for the
+    *          representative sequence
+    */
+   public void hideSequences(SequenceI sequence, boolean representGroup)
+   {
+     if (selectionGroup == null || selectionGroup.getSize() < 1)
+     {
+       hideSequence(new SequenceI[] { sequence });
+       return;
+     }
+     if (representGroup)
+     {
+       hideRepSequences(sequence, selectionGroup);
+       setSelectionGroup(null);
+       return;
+     }
+     int gsize = selectionGroup.getSize();
+     SequenceI[] hseqs = selectionGroup.getSequences().toArray(
+             new SequenceI[gsize]);
+     hideSequence(hseqs);
+     setSelectionGroup(null);
+     sendSelection();
+   }
+   /**
     * Set visibility for any annotations for the given sequence.
     * 
     * @param sequenceI
    protected void setSequenceAnnotationsVisible(SequenceI sequenceI,
            boolean visible)
    {
-     for (AlignmentAnnotation ann : alignment.getAlignmentAnnotation())
+     AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
+     if (anns != null)
      {
-       if (ann.sequenceRef == sequenceI)
+       for (AlignmentAnnotation ann : anns)
        {
-         ann.visible = visible;
+         if (ann.sequenceRef == sequenceI)
+         {
+           ann.visible = visible;
+         }
        }
      }
    }
    @Override
    public String[] getViewAsString(boolean selectedRegionOnly)
    {
+     return getViewAsString(selectedRegionOnly, true);
+   }
+   @Override
+   public String[] getViewAsString(boolean selectedRegionOnly,
+           boolean exportHiddenSeqs)
+   {
      String[] selection = null;
      SequenceI[] seqs = null;
      int i, iSize;
      }
      else
      {
-       if (hasHiddenRows())
+       if (hasHiddenRows() && exportHiddenSeqs)
        {
-         iSize = alignment.getHiddenSequences().getFullAlignment()
-                 .getHeight();
-         seqs = alignment.getHiddenSequences().getFullAlignment()
-                 .getSequencesArray();
-         end = alignment.getHiddenSequences().getFullAlignment().getWidth();
+         AlignmentI fullAlignment = alignment.getHiddenSequences()
+                 .getFullAlignment();
+         iSize = fullAlignment.getHeight();
+         seqs = fullAlignment.getSequencesArray();
+         end = fullAlignment.getWidth();
        }
        else
        {
                .getCodonFrames();
        if (codonMappings != null && !codonMappings.isEmpty())
        {
-         // fudge: check mappings are not protein-to-protein
-         // TODO: nicer
-         AlignedCodonFrame mapping = codonMappings.iterator().next();
-         MapList[] mapLists = mapping.getdnaToProt();
-         // mapLists can be empty if project load has not finished resolving seqs
-         if (mapLists.length > 0 && mapLists[0].getFromRatio() == 3)
+         boolean doConsensus = false;
+         for (AlignedCodonFrame mapping : codonMappings)
+         {
+           // TODO hold mapping type e.g. dna-to-protein in AlignedCodonFrame?
+           MapList[] mapLists = mapping.getdnaToProt();
+           // mapLists can be empty if project load has not finished resolving
+           // seqs
+           if (mapLists.length > 0 && mapLists[0].getFromRatio() == 3)
+           {
+             doConsensus = true;
+             break;
+           }
+         }
+         if (doConsensus)
          {
            complementConsensus = new AlignmentAnnotation("cDNA Consensus",
                    "PID for cDNA", new Annotation[1], 0f, 100f,
    }
  
    @Override
 -  public Color getSequenceColour(SequenceI seq)
 +  public ColorI getSequenceColour(SequenceI seq)
    {
 -    Color sqc = sequenceColours.get(seq);
 -    return (sqc == null ? Color.white : sqc);
 +    ColorI sqc = sequenceColours.get(seq);
 +    return (sqc == null ? Colour.white : sqc);
    }
  
    @Override
 -  public void setSequenceColour(SequenceI seq, Color col)
 +  public void setSequenceColour(SequenceI seq, ColorI col)
    {
      if (col == null)
      {
     * @see jalview.api.ViewStyleI#getTextColour()
     */
    @Override
 -  public Color getTextColour()
 +  public ColorI getTextColour()
    {
      return viewStyle.getTextColour();
    }
     * @see jalview.api.ViewStyleI#getTextColour2()
     */
    @Override
 -  public Color getTextColour2()
 +  public ColorI getTextColour2()
    {
      return viewStyle.getTextColour2();
    }
     * @see jalview.api.ViewStyleI#setTextColour(java.awt.Color)
     */
    @Override
 -  public void setTextColour(Color textColour)
 +  public void setTextColour(ColorI textColour)
    {
      viewStyle.setTextColour(textColour);
    }
     * @see jalview.api.ViewStyleI#setTextColour2(java.awt.Color)
     */
    @Override
 -  public void setTextColour2(Color textColour2)
 +  public void setTextColour2(ColorI textColour2)
    {
      viewStyle.setTextColour2(textColour2);
    }
       * all gapped visible regions
       */
      int lastSeq = alignment.getHeight() - 1;
+     List<AlignedCodonFrame> seqMappings = null;
      for (int seqNo = getStartSeq(); seqNo < lastSeq; seqNo++, seqOffset++)
      {
        sequence = getAlignment().getSequenceAt(seqNo);
        {
          continue;
        }
-       List<AlignedCodonFrame> seqMappings = MappingUtils
-               .findMappingsForSequence(sequence, mappings);
+       seqMappings = MappingUtils
+               .findMappingsForSequenceAndOthers(sequence, mappings,
+                       getCodingComplement().getAlignment().getSequences());
        if (!seqMappings.isEmpty())
        {
          break;
        }
      }
  
-     if (sequence == null)
+     if (sequence == null || seqMappings == null || seqMappings.isEmpty())
      {
        /*
         * No ungapped mapped sequence in middle column - do nothing
        return 0;
      }
      MappingUtils.addSearchResults(sr, sequence,
-             sequence.findPosition(middleColumn), mappings);
+             sequence.findPosition(middleColumn), seqMappings);
      return seqOffset;
    }
  
      if (sg != null
              && (sgs = sg.getStartRes()) >= 0
              && sg.getStartRes() <= (sge = sg.getEndRes())
-             && (colSel == null || colSel.getSelected() == null || colSel
-                     .getSelected().size() == 0))
+             && !this.hasSelectedColumns())
      {
        if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
        {
        }
      }
    }
+   /**
+    * hold status of current selection group - defined on alignment or not.
+    */
+   private boolean selectionIsDefinedGroup = false;
+   @Override
+   public boolean isSelectionDefinedGroup()
+   {
+     if (selectionGroup == null)
+     {
+       return false;
+     }
+     if (isSelectionGroupChanged(true))
+     {
+       selectionIsDefinedGroup = false;
+       List<SequenceGroup> gps = alignment.getGroups();
+       if (gps == null || gps.size() == 0)
+       {
+         selectionIsDefinedGroup = false;
+       }
+       else
+       {
+         selectionIsDefinedGroup = gps.contains(selectionGroup);
+       }
+     }
+     return selectionGroup.getContext() == alignment
+             || selectionIsDefinedGroup;
+   }
  }
@@@ -27,11 -27,8 +27,10 @@@ import jalview.datamodel.AlignmentI
  import jalview.datamodel.SequenceFeature;
  import jalview.datamodel.SequenceI;
  import jalview.renderer.seqfeatures.FeatureRenderer;
 +import jalview.schemes.Colour;
  import jalview.schemes.FeatureColour;
  import jalview.schemes.UserColourScheme;
 +import jalview.util.ColorUtils;
- import jalview.viewmodel.AlignmentViewport;
  
  import java.awt.Color;
  import java.beans.PropertyChangeListener;
@@@ -66,14 -63,8 +65,14 @@@ public abstract class FeatureRendererMo
    protected PropertyChangeSupport changeSupport = new PropertyChangeSupport(
            this);
  
-   protected AlignmentViewport av;
+   protected AlignViewportI av;
  
 +  /*
 +   * map holds per feature type, {{min, max}, {min, max}} feature score
 +   * values for positional and non-positional features respectively
 +   */
 +  private Map<String, float[][]> minmax = new Hashtable<String, float[][]>();
 +
    @Override
    public AlignViewportI getViewport()
    {
      renderOrder = neworder;
    }
  
 -  protected Map<String, float[][]> minmax = new Hashtable<String, float[][]>();
 -
    public Map<String, float[][]> getMinMax()
    {
      return minmax;
      if (fc == null)
      {
        Color col = UserColourScheme.createColourFromName(featureType);
 -      fc = new FeatureColour(col);
 +      fc = new FeatureColour(new Colour(col));
        featureColours.put(featureType, fc);
      }
      return fc;
    public Color getColour(SequenceFeature feature)
    {
      FeatureColourI fc = getFeatureStyle(feature.getType());
 -    return fc.getColor(feature);
 +    return ColorUtils.getColor(fc.getColor(feature));
    }
  
    protected boolean showFeature(SequenceFeature sequenceFeature)
       * note visible feature ordering and colours before update
       */
      List<String> visibleFeatures = getDisplayedFeatureTypes();
-     Map<String, Object> visibleColours = new HashMap<String, Object>(
+     Map<String, FeatureColourI> visibleColours = new HashMap<String, FeatureColourI>(
              getFeatureColours());
  
      FeaturesDisplayedI av_featuresdisplayed = null;
      return renderOrder != null;
    }
  
 -  /**
 -   * Returns feature types in ordering of rendering, where last means on top
 -   */
    public List<String> getRenderOrder()
    {
      if (renderOrder == null)
     * @return list of groups
     */
    @Override
 -  public List getGroups(boolean visible)
 +  public List<String> getGroups(boolean visible)
    {
      if (featureGroups != null)
      {
      return av.getFeaturesDisplayed();
    }
  
 -  /**
 -   * Returns a (possibly empty) list of visible feature types, in render order
 -   * (last is on top)
 -   */
    @Override
    public List<String> getDisplayedFeatureTypes()
    {
@@@ -32,8 -32,14 +32,14 @@@ public class FeatureRendererSettings im
  {
    String[] renderOrder;
  
+   /*
+    * map of {groupName, isDisplayed}
+    */
    Map<String, Boolean> featureGroups;
  
+   /*
+    * map of {featureType, colourScheme}
+    */
    Map<String, FeatureColourI> featureColours;
  
    float transparency;
@@@ -88,7 -94,8 +94,7 @@@
      {
        String next = en.next();
        FeatureColourI val = featureColours.get(next);
 -      // if (val instanceof GraduatedColor)
 -      if (val.isGraduatedColour() || val.isColourByLabel()) // why this test?
 +      if (!val.isSimpleColour())
        {
          featureColours.put(next, new FeatureColour((FeatureColour) val));
        }
@@@ -26,15 -26,13 +26,15 @@@ import static org.testng.AssertJUnit.as
  import static org.testng.AssertJUnit.assertTrue;
  
  import jalview.analysis.AlignSeq;
 +import jalview.api.ColorI;
  import jalview.datamodel.AlignmentAnnotation;
  import jalview.datamodel.Sequence;
  import jalview.datamodel.SequenceFeature;
  import jalview.datamodel.SequenceI;
 +import jalview.schemes.Colour;
  import jalview.schemes.ColourSchemeI;
  import jalview.schemes.TaylorColourScheme;
- import jalview.structure.StructureViewSettings;
+ import jalview.structure.StructureImportSettings;
  
  import java.awt.Color;
  import java.util.Vector;
@@@ -58,7 -56,7 +58,7 @@@ public class PDBChainTes
    public void setUp()
    {
      System.out.println("setup");
-     StructureViewSettings.setShowSeqFeatures(true);
+     StructureImportSettings.setShowSeqFeatures(true);
      c = new PDBChain("1GAQ", "A");
    }
  
      c.makeBond(a2, a3);
      c.setChainColours(Color.PINK);
      assertEquals(2, c.bonds.size());
 -    assertEquals(Color.PINK, c.bonds.get(0).startCol);
 -    assertEquals(Color.PINK, c.bonds.get(0).endCol);
 -    assertEquals(Color.PINK, c.bonds.get(1).startCol);
 -    assertEquals(Color.PINK, c.bonds.get(1).endCol);
 +    assertEquals(Colour.pink, c.bonds.get(0).startCol);
 +    assertEquals(Colour.pink, c.bonds.get(0).endCol);
 +    assertEquals(Colour.pink, c.bonds.get(1).startCol);
 +    assertEquals(Colour.pink, c.bonds.get(1).endCol);
    }
  
    /**
    @Test(groups = { "Functional" })
    public void testSetChainColours_colourScheme()
    {
 -    Color alaColour = new Color(204, 255, 0);
 -    Color glyColour = new Color(255, 153, 0);
 +    ColorI alaColour = new Colour(204, 255, 0);
 +    ColorI glyColour = new Colour(255, 153, 0);
      a1.resName = "ALA";
      a2.resName = "GLY";
      a3.resName = "XXX"; // no colour defined
      // bond a2 to a3 - no colour found for a3
      // exception handling defaults to gray
      b = c.bonds.get(2);
 -    assertEquals(Color.gray, b.startCol);
 -    assertEquals(Color.gray, b.endCol);
 +    assertEquals(Colour.gray, b.startCol);
 +    assertEquals(Colour.gray, b.endCol);
    }
  
    @Test(groups = { "Functional" })
    public void testGetChargeColour()
    {
 -    assertEquals(Color.red, PDBChain.getChargeColour("ASP"));
 -    assertEquals(Color.red, PDBChain.getChargeColour("GLU"));
 -    assertEquals(Color.blue, PDBChain.getChargeColour("LYS"));
 -    assertEquals(Color.blue, PDBChain.getChargeColour("ARG"));
 -    assertEquals(Color.yellow, PDBChain.getChargeColour("CYS"));
 -    assertEquals(Color.lightGray, PDBChain.getChargeColour("ALA"));
 -    assertEquals(Color.lightGray, PDBChain.getChargeColour(null));
 +    assertEquals(Colour.red, PDBChain.getChargeColour("ASP"));
 +    assertEquals(Colour.red, PDBChain.getChargeColour("GLU"));
 +    assertEquals(Colour.blue, PDBChain.getChargeColour("LYS"));
 +    assertEquals(Colour.blue, PDBChain.getChargeColour("ARG"));
 +    assertEquals(Colour.yellow, PDBChain.getChargeColour("CYS"));
 +    assertEquals(Colour.lightGray, PDBChain.getChargeColour("ALA"));
 +    assertEquals(Colour.lightGray, PDBChain.getChargeColour(null));
    }
  
    /**
      assertEquals(3, c.bonds.size());
      // bond a1 to a2
      Bond b = c.bonds.get(0);
 -    assertEquals(Color.red, b.startCol);
 -    assertEquals(Color.blue, b.endCol);
 +    assertEquals(Colour.red, b.startCol);
 +    assertEquals(Colour.blue, b.endCol);
      // bond a2 to a3
      b = c.bonds.get(1);
 -    assertEquals(Color.blue, b.startCol);
 -    assertEquals(Color.yellow, b.endCol);
 +    assertEquals(Colour.blue, b.startCol);
 +    assertEquals(Colour.yellow, b.endCol);
      // bond a3 to a4
      b = c.bonds.get(2);
 -    assertEquals(Color.yellow, b.startCol);
 -    assertEquals(Color.lightGray, b.endCol);
 +    assertEquals(Colour.yellow, b.startCol);
 +    assertEquals(Colour.lightGray, b.endCol);
    }
  
    /**
@@@ -11,9 -11,9 +11,9 @@@ import jalview.datamodel.SequenceFeatur
  import jalview.datamodel.SequenceI;
  import jalview.io.gff.SequenceOntologyFactory;
  import jalview.io.gff.SequenceOntologyLite;
++import jalview.schemes.Colour;
  import jalview.util.MapList;
  
--import java.awt.Color;
  import java.util.List;
  
  import org.testng.annotations.AfterClass;
@@@ -22,13 -22,13 +22,13 @@@ import org.testng.annotations.Test
  
  public class EnsemblGeneTest
  {
-   @BeforeClass
+   @BeforeClass(alwaysRun = true)
    public void setUp()
    {
      SequenceOntologyFactory.setInstance(new SequenceOntologyLite());
    }
  
-   @AfterClass
+   @AfterClass(alwaysRun = true)
    public void tearDown()
    {
      SequenceOntologyFactory.setInstance(null);
      assertTrue(fc.isFeatureDisplayed("sequence_variant"));
      assertTrue(fc.isFeatureDisplayed("feature_variant")); // subtype
      assertFalse(fc.isFeatureDisplayed("transcript"));
--    assertEquals(Color.RED, fc.getFeatureColour("sequence_variant")
++    assertEquals(Colour.red, fc.getFeatureColour("sequence_variant")
              .getColour());
--    assertEquals(Color.RED, fc.getFeatureColour("feature_variant")
++    assertEquals(Colour.red, fc.getFeatureColour("feature_variant")
              .getColour());
      assertTrue(fc.getFeatureColour("exon").isColourByLabel());
      assertTrue(fc.getFeatureColour("coding_exon").isColourByLabel());
@@@ -27,14 -27,15 +27,15 @@@ import static org.testng.AssertJUnit.as
  import static org.testng.AssertJUnit.assertTrue;
  
  import jalview.api.FeatureColourI;
+ import jalview.api.FeatureRenderer;
  import jalview.datamodel.Alignment;
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.SequenceDummy;
  import jalview.datamodel.SequenceFeature;
  import jalview.datamodel.SequenceI;
  import jalview.gui.AlignFrame;
 +import jalview.schemes.Colour;
  
 -import java.awt.Color;
  import java.io.File;
  import java.io.IOException;
  import java.util.Map;
@@@ -66,61 -67,71 +67,71 @@@ public class FeaturesFileTes
       */
      colours = af.getFeatureRenderer().getFeatureColours();
      assertEquals("26 feature group colours not found", 26, colours.size());
 -    assertEquals(colours.get("Cath").getColour(), new Color(0x93b1d1));
 -    assertEquals(colours.get("ASX-MOTIF").getColour(), new Color(0x6addbb));
 +    assertEquals(colours.get("Cath").getColour(), new Colour(0x93b1d1));
 +    assertEquals(colours.get("ASX-MOTIF").getColour(), new Colour(0x6addbb));
  
      /*
       * verify (some) features on sequences
       */
      SequenceFeature[] sfs = al.getSequenceAt(0).getDatasetSequence()
              .getSequenceFeatures(); // FER_CAPAA
-     assertEquals(7, sfs.length);
+     assertEquals(8, sfs.length);
      SequenceFeature sf = sfs[0];
+     assertEquals("Pfam family%LINK%", sf.description);
+     assertEquals(0, sf.begin);
+     assertEquals(0, sf.end);
+     assertEquals("uniprot", sf.featureGroup);
+     assertEquals("Pfam", sf.type);
+     assertEquals(1, sf.links.size());
+     assertEquals("Pfam family|http://pfam.xfam.org/family/PF00111",
+             sf.links.get(0));
+     sf = sfs[1];
      assertEquals("Iron-sulfur (2Fe-2S)", sf.description);
      assertEquals(39, sf.begin);
      assertEquals(39, sf.end);
      assertEquals("uniprot", sf.featureGroup);
      assertEquals("METAL", sf.type);
-     sf = sfs[1];
+     sf = sfs[2];
      assertEquals("Iron-sulfur (2Fe-2S)", sf.description);
      assertEquals(44, sf.begin);
      assertEquals(44, sf.end);
      assertEquals("uniprot", sf.featureGroup);
      assertEquals("METAL", sf.type);
-     sf = sfs[2];
+     sf = sfs[3];
      assertEquals("Iron-sulfur (2Fe-2S)", sf.description);
      assertEquals(47, sf.begin);
      assertEquals(47, sf.end);
      assertEquals("uniprot", sf.featureGroup);
      assertEquals("METAL", sf.type);
-     sf = sfs[3];
+     sf = sfs[4];
      assertEquals("Iron-sulfur (2Fe-2S)", sf.description);
      assertEquals(77, sf.begin);
      assertEquals(77, sf.end);
      assertEquals("uniprot", sf.featureGroup);
      assertEquals("METAL", sf.type);
-     sf = sfs[4];
+     sf = sfs[5];
      assertEquals("Fer2 Status: True Positive Pfam 8_8%LINK%",
              sf.description);
-     assertEquals("Pfam 8_8|http://pfam.sanger.ac.uk/family/PF00111",
-             sf.links.get(0).toString());
+     assertEquals("Pfam 8_8|http://pfam.xfam.org/family/PF00111",
+             sf.links.get(0));
      assertEquals(8, sf.begin);
      assertEquals(83, sf.end);
      assertEquals("uniprot", sf.featureGroup);
      assertEquals("Pfam", sf.type);
-     sf = sfs[5];
+     sf = sfs[6];
      assertEquals("Ferredoxin_fold Status: True Positive ", sf.description);
      assertEquals(3, sf.begin);
      assertEquals(93, sf.end);
      assertEquals("uniprot", sf.featureGroup);
      assertEquals("Cath", sf.type);
-     sf = sfs[6];
+     sf = sfs[7];
      assertEquals(
              "High confidence server. Only hits with scores over 0.8 are reported. PHOSPHORYLATION (T) 89_8%LINK%",
              sf.description);
      assertEquals(
              "PHOSPHORYLATION (T) 89_8|http://www.cbs.dtu.dk/cgi-bin/proview/webface-link?seqid=P83527&amp;service=NetPhos-2.0",
-             sf.links.get(0).toString());
+             sf.links.get(0));
      assertEquals(89, sf.begin);
      assertEquals(89, sf.end);
      assertEquals("netphos", sf.featureGroup);
      // verify colours read or synthesized
      colours = af.getFeatureRenderer().getFeatureColours();
      assertEquals("1 feature group colours not found", 1, colours.size());
 -    assertEquals(colours.get("METAL").getColour(), new Color(0xcc9900));
 +    assertEquals(colours.get("METAL").getColour(), new Colour(0xcc9900));
  
      // verify feature on FER_CAPAA
      SequenceFeature[] sfs = al.getSequenceAt(0).getDatasetSequence()
    }
  
    /**
 +   * Test various ways of describing a feature colour scheme
 +   * 
 +   * @throws Exception
 +   */
 +  @Test(groups = { "Functional" })
 +  public void testParseGraduatedColourScheme() throws Exception
 +  {
 +    FeaturesFile ff = new FeaturesFile();
 +
 +    // colour by label:
 +    FeatureColourI gc = ff.parseGraduatedColourScheme(
 +            "BETA-TURN-IR\t9a6a94", "label");
 +    assertTrue(gc.isColourByLabel());
 +    assertEquals(Colour.white, gc.getMinColour());
 +    assertEquals(Colour.black, gc.getMaxColour());
 +    assertTrue(gc.isAutoScaled());
 +
 +    // using colour name, rgb, etc:
 +    String spec = "blue|255,0,255|absolute|20.0|95.0|below|66.0";
 +    gc = ff.parseGraduatedColourScheme("BETA-TURN-IR\t" + spec, spec);
 +    assertFalse(gc.isColourByLabel());
 +    assertEquals(Colour.blue, gc.getMinColour());
 +    assertEquals(new Colour(255, 0, 255), gc.getMaxColour());
 +    assertFalse(gc.isAutoScaled());
 +    // assertFalse(gc.getTolow());
 +    assertEquals(20.0f, gc.getMin(), 0.001f);
 +    assertEquals(95.0f, gc.getMax(), 0.001f);
 +    assertTrue(gc.isBelowThreshold());
 +    assertEquals(66.0f, gc.getThreshold(), 0.001f);
 +
 +    // inverse gradient high to low:
 +    spec = "blue|255,0,255|95.0|20.0|below|66.0";
 +    gc = ff.parseGraduatedColourScheme("BETA-TURN-IR\t" + spec, spec);
 +    assertTrue(gc.isAutoScaled());
 +    // assertTrue(gc..getTolow());
 +  }
 +
 +  /**
     * Test parsing a features file with GFF formatted content only
     * 
     * @throws Exception
              parseResult);
      checkDatasetfromSimpleGff3(dataset);
    }
+   @Test(groups = { "Functional" })
+   public void testPrintJalviewFormat() throws Exception
+   {
+     File f = new File("examples/uniref50.fa");
+     AlignmentI al = readAlignmentFile(f);
+     AlignFrame af = new AlignFrame(al, 500, 500);
+     Map<String, FeatureColourI> colours = af.getFeatureRenderer()
+             .getFeatureColours();
+     String features = "METAL\tcc9900\n"
+             + "GAMMA-TURN\tred|0,255,255|20.0|95.0|below|66.0\n"
+             + "Pfam\tred\n"
+             + "STARTGROUP\tuniprot\n"
+             + "Iron\tFER_CAPAA\t-1\t39\t39\tMETAL\n"
+             + "Turn\tFER_CAPAA\t-1\t36\t38\tGAMMA-TURN\n"
+             + "<html>Pfam domain<a href=\"http://pfam.xfam.org/family/PF00111\">Pfam_3_4</a></html>\tFER_CAPAA\t-1\t20\t20\tPfam\n"
+             + "ENDGROUP\tuniprot\n";
+     FeaturesFile featuresFile = new FeaturesFile(features,
+             FormatAdapter.PASTE);
+     featuresFile.parse(al.getDataset(), colours, false);
+     /*
+      * first with no features displayed
+      */
+     FeatureRenderer fr = af.alignPanel.getFeatureRenderer();
+     Map<String, FeatureColourI> visible = fr
+             .getDisplayedFeatureCols();
+     String exported = featuresFile.printJalviewFormat(
+             al.getSequencesArray(), visible);
+     String expected = "No Features Visible";
+     assertEquals(expected, exported);
+     /*
+      * set METAL (in uniprot group) and GAMMA-TURN visible, but not Pfam
+      */
+     fr.setVisible("METAL");
+     fr.setVisible("GAMMA-TURN");
+     visible = fr.getDisplayedFeatureCols();
+     exported = featuresFile.printJalviewFormat(al.getSequencesArray(),
+             visible);
+     expected = "METAL\tcc9900\n"
+             + "GAMMA-TURN\tff0000|00ffff|20.0|95.0|below|66.0\n"
+             + "\nSTARTGROUP\tuniprot\n"
+             + "Iron\tFER_CAPAA\t-1\t39\t39\tMETAL\t0.0\n"
+             + "Turn\tFER_CAPAA\t-1\t36\t38\tGAMMA-TURN\t0.0\n"
+             + "ENDGROUP\tuniprot\n";
+     assertEquals(expected, exported);
+     /*
+      * now set Pfam visible
+      */
+     fr.setVisible("Pfam");
+     visible = fr.getDisplayedFeatureCols();
+     exported = featuresFile.printJalviewFormat(al.getSequencesArray(),
+             visible);
+     /*
+      * note the order of feature types is uncontrolled - derives from
+      * FeaturesDisplayed.featuresDisplayed which is a HashSet
+      */
+     expected = "METAL\tcc9900\n"
+             + "Pfam\tff0000\n"
+             + "GAMMA-TURN\tff0000|00ffff|20.0|95.0|below|66.0\n"
+             + "\nSTARTGROUP\tuniprot\n"
+             + "Iron\tFER_CAPAA\t-1\t39\t39\tMETAL\t0.0\n"
+             + "Turn\tFER_CAPAA\t-1\t36\t38\tGAMMA-TURN\t0.0\n"
+             + "<html>Pfam domain<a href=\"http://pfam.xfam.org/family/PF00111\">Pfam_3_4</a></html>\tFER_CAPAA\t-1\t20\t20\tPfam\t0.0\n"
+             + "ENDGROUP\tuniprot\n";
+     assertEquals(expected, exported);
+   }
  }
@@@ -5,7 -5,6 +5,7 @@@ import static org.testng.AssertJUnit.as
  import static org.testng.AssertJUnit.assertTrue;
  import static org.testng.AssertJUnit.fail;
  
 +import jalview.api.ColorI;
  import jalview.datamodel.SequenceFeature;
  import jalview.util.Format;
  
@@@ -16,6 -15,44 +16,6 @@@ import org.testng.annotations.Test
  public class FeatureColourTest
  {
    @Test(groups = { "Functional" })
 -  public void testCopyConstructor()
 -  {
 -    /*
 -     * plain colour
 -     */
 -    FeatureColour fc = new FeatureColour(Color.RED);
 -    FeatureColour fc1 = new FeatureColour(fc);
 -    assertTrue(fc1.getColour().equals(Color.RED));
 -    assertFalse(fc1.isGraduatedColour());
 -    assertFalse(fc1.isColourByLabel());
 -
 -    /*
 -     * min-max colour
 -     */
 -    fc = new FeatureColour(Color.gray, Color.black, 10f, 20f);
 -    fc.setAboveThreshold(true);
 -    fc.setThreshold(12f);
 -    fc1 = new FeatureColour(fc);
 -    assertTrue(fc1.isGraduatedColour());
 -    assertFalse(fc1.isColourByLabel());
 -    assertTrue(fc1.isAboveThreshold());
 -    assertEquals(12f, fc1.getThreshold());
 -    assertEquals(Color.gray, fc1.getMinColour());
 -    assertEquals(Color.black, fc1.getMaxColour());
 -    assertEquals(10f, fc1.getMin());
 -    assertEquals(20f, fc1.getMax());
 -
 -    /*
 -     * colour by label
 -     */
 -    fc = new FeatureColour();
 -    fc.setColourByLabel(true);
 -    fc1 = new FeatureColour(fc);
 -    assertTrue(fc1.isColourByLabel());
 -    assertFalse(fc1.isGraduatedColour());
 -  }
 -
 -  @Test(groups = { "Functional" })
    public void testIsColored_simpleColour()
    {
      FeatureColour fc = new FeatureColour(Color.RED);
    public void testIsColored_aboveThreshold()
    {
      // graduated colour range from score 20 to 100
 -    FeatureColour fc = new FeatureColour(Color.WHITE, Color.BLACK, 20f,
 -            100f);
 +    FeatureColour fc = new FeatureColour(new Colour(Color.WHITE),
 +            new Colour(Color.BLACK), 20f, 100f);
  
      // score 0 is adjusted to bottom of range
      SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 0f,
              null);
      assertTrue(fc.isColored(sf));
 -    assertEquals(Color.WHITE, fc.getColor(sf));
 +    assertEquals(fc.getColor(sf), Colour.white);
  
      // score 120 is adjusted to top of range
      sf.setScore(120f);
 -    assertEquals(Color.BLACK, fc.getColor(sf));
 +    assertEquals(fc.getColor(sf), Colour.black);
  
      // value below threshold is still rendered
      // setting threshold has no effect yet...
      fc.setThreshold(60f);
      sf.setScore(36f);
      assertTrue(fc.isColored(sf));
 -    assertEquals(new Color(204, 204, 204), fc.getColor(sf));
 +    assertEquals(fc.getColor(sf), new Colour(204, 204, 204));
  
      // now apply threshold:
      fc.setAboveThreshold(true);
      assertFalse(fc.isColored(sf));
      // colour is still returned though ?!?
 -    assertEquals(new Color(204, 204, 204), fc.getColor(sf));
 +    assertEquals(fc.getColor(sf), new Colour(204, 204, 204));
  
      sf.setScore(84); // above threshold now
      assertTrue(fc.isColored(sf));
 -    assertEquals(new Color(51, 51, 51), fc.getColor(sf));
 +    assertEquals(fc.getColor(sf), new Colour(51, 51, 51));
    }
  
    @Test(groups = { "Functional" })
    public void testGetColor_simpleColour()
    {
 -    FeatureColour fc = new FeatureColour(Color.RED);
 -    assertEquals(Color.RED, fc.getColor(new SequenceFeature()));
 +    FeatureColour fc = new FeatureColour(Colour.red);
 +    assertEquals(fc.getColor(new SequenceFeature()), Colour.red);
    }
  
    @Test(groups = { "Functional" })
      fc.setColourByLabel(true);
      SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 1f,
              null);
 -    Color expected = UserColourScheme.createColourFromName("desc");
 -    assertEquals(expected, fc.getColor(sf));
 +    ColorI expected = new Colour(
 +            UserColourScheme.createColourFromName("desc"));
 +    assertEquals(fc.getColor(sf), expected);
 +    // assertEquals(expected, fc.getColor(sf));
    }
  
    @Test(groups = { "Functional" })
      float red = 128 / 255f + 3 / 4f * (255 - 128) / 255f;
      float green = 128 / 255f + 3 / 4f * (0 - 128) / 255f;
      float blue = 128 / 255f + 3 / 4f * (0 - 128) / 255f;
 -    Color expected = new Color(red, green, blue);
 -    assertEquals(expected, fc.getColor(sf));
 +    Colour expected = new Colour(red, green, blue);
 +    assertEquals(fc.getColor(sf), expected);
 +    // assertEquals(expected, fc.getColor(sf));
    }
  
    @Test(groups = { "Functional" })
              null);
      fc.setThreshold(100f); // ignore for now
      assertTrue(fc.isColored(sf));
 -    assertEquals(new Color(204, 204, 204), fc.getColor(sf));
 +    assertEquals(fc.getColor(sf), new Colour(204, 204, 204));
 +    // assertEquals(new Color(204, 204, 204), fc.getColor(sf));
  
      fc.setAboveThreshold(true); // feature lies below threshold
      assertFalse(fc.isColored(sf));
 -    assertEquals(new Color(204, 204, 204), fc.getColor(sf));
 +    assertEquals(fc.getColor(sf), new Colour(204, 204, 204));
 +    // assertEquals(new Color(204, 204, 204), fc.getColor(sf));
    }
  
    /**
       */
      fc = new FeatureColour(Color.GREEN, Color.RED, 12f, 25f);
      String greenHex = Format.getHexString(Color.GREEN);
-     String expected = String.format("domain\t%s|%s|12.0|25.0|none",
+     String expected = String.format("domain\t%s|%s|abso|12.0|25.0|none",
              greenHex, redHex);
      assertEquals(expected, fc.toJalviewFormat("domain"));
  
       * colour ranges over the actual score ranges (not min/max)
       */
      fc.setAutoScaled(true);
-     expected = String.format("domain\t%s|%s|abso|12.0|25.0|none", greenHex,
+     expected = String.format("domain\t%s|%s|12.0|25.0|none", greenHex,
              redHex);
      assertEquals(expected, fc.toJalviewFormat("domain"));
  
       */
      fc.setThreshold(12.5f);
      fc.setBelowThreshold(true);
-     expected = String.format("domain\t%s|%s|abso|12.0|25.0|below|12.5",
+     expected = String.format("domain\t%s|%s|12.0|25.0|below|12.5",
              greenHex, redHex);
      assertEquals(expected, fc.toJalviewFormat("domain"));
  
       */
      fc.setThreshold(12.5f);
      fc.setAboveThreshold(true);
+     fc.setAutoScaled(false);
      expected = String.format("domain\t%s|%s|abso|12.0|25.0|above|12.5",
              greenHex, redHex);
      assertEquals(expected, fc.toJalviewFormat("domain"));
       */
      FeatureColour fc = FeatureColour.parseJalviewFeatureColour("red");
      assertTrue(fc.isSimpleColour());
 -    assertEquals(Color.RED, fc.getColour());
 +    assertEquals(fc.getColour(), Colour.red);
  
      /*
       * simple colour by hex code
      fc = FeatureColour.parseJalviewFeatureColour(Format
              .getHexString(Color.RED));
      assertTrue(fc.isSimpleColour());
 -    assertEquals(Color.RED, fc.getColour());
 +    assertEquals(fc.getColour(), Colour.red);
 +    // assertEquals(Color.RED, fc.getColour());
  
      /*
       * simple colour by rgb triplet
       */
      fc = FeatureColour.parseJalviewFeatureColour("255,0,0");
      assertTrue(fc.isSimpleColour());
 -    assertEquals(Color.RED, fc.getColour());
 +    assertEquals(fc.getColour(), Colour.red);
 +    // assertEquals(Color.RED, fc.getColour());
  
      /*
       * malformed colour
      fc = FeatureColour.parseJalviewFeatureColour("red|green|10.0|20.0");
      assertTrue(fc.isGraduatedColour());
      assertFalse(fc.hasThreshold());
 -    assertEquals(Color.RED, fc.getMinColour());
 -    assertEquals(Color.GREEN, fc.getMaxColour());
 +    assertEquals(fc.getMinColour(), Colour.red);
 +    // assertEquals(Color.RED, fc.getMinColour());
 +    assertEquals(fc.getMaxColour(), Colour.green);
 +    // assertEquals(Color.GREEN, fc.getMaxColour());
      assertEquals(10f, fc.getMin());
      assertEquals(20f, fc.getMax());
      assertTrue(fc.isAutoScaled());
      assertTrue(fc.hasThreshold());
      assertTrue(fc.isAboveThreshold());
      assertEquals(15f, fc.getThreshold());
 -    assertEquals(Color.RED, fc.getMinColour());
 -    assertEquals(Color.GREEN, fc.getMaxColour());
 +    assertEquals(fc.getMinColour(), Colour.red);
 +    // assertEquals(Color.RED, fc.getMinColour());
 +    assertEquals(fc.getMaxColour(), Colour.green);
 +    // assertEquals(Color.GREEN, fc.getMaxColour());
      assertEquals(10f, fc.getMin());
      assertEquals(20f, fc.getMax());
      assertTrue(fc.isAutoScaled());
      assertTrue(fc.hasThreshold());
      assertTrue(fc.isBelowThreshold());
      assertEquals(15f, fc.getThreshold());
 -    assertEquals(Color.RED, fc.getMinColour());
 -    assertEquals(Color.GREEN, fc.getMaxColour());
 +    assertEquals(fc.getMinColour(), Colour.red);
 +    // assertEquals(Color.RED, fc.getMinColour());
 +    assertEquals(fc.getMaxColour(), Colour.green);
 +    // assertEquals(Color.GREEN, fc.getMaxColour());
      assertEquals(10f, fc.getMin());
      assertEquals(20f, fc.getMax());
 -
 -    descriptor = String
 -            .format("blue|255,0,255|absolute|20.0|95.0|below|66.0");
 -    fc = FeatureColour.parseJalviewFeatureColour(descriptor);
 -    assertTrue(fc.isGraduatedColour());
    }
  }