JAL-2163 set title for the alignframe from restored view state
[jalview.git] / src / jalview / gui / Jalview2XML.java
index e4ec30b..0f15505 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.gui;
 
+import jalview.api.FeatureColourI;
+import jalview.api.ViewStyleI;
 import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
 import jalview.datamodel.AlignedCodonFrame;
@@ -70,7 +72,7 @@ import jalview.schemabinding.version2.Viewport;
 import jalview.schemes.AnnotationColourGradient;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ColourSchemeProperty;
-import jalview.schemes.GraduatedColor;
+import jalview.schemes.FeatureColour;
 import jalview.schemes.ResidueColourScheme;
 import jalview.schemes.ResidueProperties;
 import jalview.schemes.UserColourScheme;
@@ -78,6 +80,7 @@ import jalview.structure.StructureSelectionManager;
 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;
@@ -89,6 +92,7 @@ import jalview.ws.params.ArgumentI;
 import jalview.ws.params.AutoCalcSetting;
 import jalview.ws.params.WsParamSetI;
 
+import java.awt.Color;
 import java.awt.Rectangle;
 import java.io.BufferedReader;
 import java.io.DataInputStream;
@@ -104,6 +108,7 @@ import java.lang.reflect.InvocationTargetException;
 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;
@@ -115,7 +120,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;
@@ -162,7 +166,9 @@ public class Jalview2XML
    */
   Map<String, SequenceI> seqRefIds = null;
 
-  Vector frefedSequence = null;
+  Map<String, SequenceI> incompleteSeqs = null;
+
+  List<SeqFref> frefedSequence = null;
 
   boolean raiseGUI = true; // whether errors are raised in dialog boxes or not
 
@@ -217,6 +223,10 @@ public class Jalview2XML
       {
         seqsToIds.clear();
       }
+      if (incompleteSeqs != null)
+      {
+        incompleteSeqs.clear();
+      }
       // seqRefIds = null;
       // seqsToIds = null;
     }
@@ -239,6 +249,14 @@ public class Jalview2XML
     {
       seqRefIds = new HashMap<String, SequenceI>();
     }
+    if (incompleteSeqs == null)
+    {
+      incompleteSeqs = new HashMap<String, SequenceI>();
+    }
+    if (frefedSequence == null)
+    {
+      frefedSequence = new ArrayList<SeqFref>();
+    }
   }
 
   public Jalview2XML()
@@ -250,78 +268,160 @@ public class 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;
+
+    public SeqFref(String _sref)
     {
-      int r = 0, rSize = frefedSequence.size();
-      while (r < rSize)
+      sref = _sref;
+    }
+
+    public String getSref()
+    {
+      return sref;
+    }
+
+    public SequenceI getSrefSeq()
+    {
+      return seqRefIds.get(sref);
+    }
+
+    public boolean isResolvable()
+    {
+      return seqRefIds.get(sref) != null;
+    }
+
+    public SequenceI getSrefDatasetSeq()
+    {
+      SequenceI sq = seqRefIds.get(sref);
+      if (sq != null)
       {
-        Object[] ref = (Object[]) 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();
+  }
+
+  /**
+   * 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)
+    {
+      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)
+    {
+      AlignedCodonFrame cf = _cf;
+
+      public jalview.datamodel.Mapping mp = _jmap;
+
+      @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.");
+      }
     }
   }
 
@@ -393,7 +493,20 @@ public class Jalview2XML
     {
       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>();
 
     /*
@@ -413,9 +526,9 @@ public class Jalview2XML
       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
@@ -518,30 +631,20 @@ public class Jalview2XML
   {
     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();
@@ -632,11 +735,15 @@ public class Jalview2XML
     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();
@@ -653,6 +760,7 @@ public class Jalview2XML
       {
         // switch jal and the dataset
         jal = jal.getDataset();
+        rjal = jal;
       }
     }
     if (jal.getProperties() != null)
@@ -672,12 +780,10 @@ public class Jalview2XML
     Set<String> calcIdSet = new HashSet<String>();
 
     // SAVE SEQUENCES
-    for (int i = 0; i < jal.getHeight(); i++)
+    for (final SequenceI jds : rjal.getSequences())
     {
-      final SequenceI jds = jal.getSequenceAt(i);
       final SequenceI jdatasq = jds.getDatasetSequence() == null ? jds
-              : jds
-              .getDatasetSequence();
+              : jds.getDatasetSequence();
       String id = seqHash(jds);
 
       if (seqRefIds.get(id) != null)
@@ -714,30 +820,37 @@ public class Jalview2XML
         // 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();
+        jalview.datamodel.SequenceFeature[] sf = jds.getSequenceFeatures();
         int index = 0;
         while (index < sf.length)
         {
@@ -762,10 +875,11 @@ public class Jalview2XML
           if (sf[index].otherDetails != null)
           {
             String key;
-            Enumeration keys = sf[index].otherDetails.keys();
-            while (keys.hasMoreElements())
+            Iterator<String> keys = sf[index].otherDetails.keySet()
+                    .iterator();
+            while (keys.hasNext())
             {
-              key = keys.nextElement().toString();
+              key = keys.next();
               OtherData keyValue = new OtherData();
               keyValue.setKey(key);
               keyValue.setValue(sf[index].otherDetails.get(key).toString());
@@ -778,9 +892,9 @@ public class Jalview2XML
         }
       }
 
-      if (jdatasq.getPDBId() != null)
+      if (jdatasq.getAllPDBEntries() != null)
       {
-        Enumeration en = jdatasq.getPDBId().elements();
+        Enumeration en = jdatasq.getAllPDBEntries().elements();
         while (en.hasMoreElements())
         {
           Pdbids pdb = new Pdbids();
@@ -880,14 +994,14 @@ public class Jalview2XML
     // SAVE MAPPINGS
     if (jal.getCodonFrames() != null)
     {
-      Set<AlignedCodonFrame> jac = jal.getCodonFrames();
+      List<AlignedCodonFrame> jac = jal.getCodonFrames();
       for (AlignedCodonFrame acf : jac)
       {
         AlcodonFrame alc = new AlcodonFrame();
-        vamsasSet.addAlcodonFrame(alc);
         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++)
@@ -897,39 +1011,44 @@ public class Jalview2XML
             alcmap.setMapping(createVamsasMapping(pmaps[m], dnas[m], null,
                     false));
             alc.addAlcodMap(alcmap);
+            hasMap = true;
+          }
+          if (hasMap)
+          {
+            vamsasSet.addAlcodonFrame(alc);
           }
         }
-
-//      {
-//        AlcodonFrame alc = new AlcodonFrame();
-//        vamsasSet.addAlcodonFrame(alc);
-//        for (int p = 0; p < acf.aaWidth; p++)
-//        {
-//          Alcodon cmap = new Alcodon();
-//          if (acf.codons[p] != null)
-//          {
-//            // Null codons indicate a gapped column in the translated peptide
-//            // alignment.
-//            cmap.setPos1(acf.codons[p][0]);
-//            cmap.setPos2(acf.codons[p][1]);
-//            cmap.setPos3(acf.codons[p][2]);
-//          }
-//          alc.addAlcodon(cmap);
-//        }
-//        if (acf.getProtMappings() != null
-//                && acf.getProtMappings().length > 0)
-//        {
-//          SequenceI[] dnas = acf.getdnaSeqs();
-//          jalview.datamodel.Mapping[] pmaps = acf.getProtMappings();
-//          for (int m = 0; m < pmaps.length; m++)
-//          {
-//            AlcodMap alcmap = new AlcodMap();
-//            alcmap.setDnasq(seqHash(dnas[m]));
-//            alcmap.setMapping(createVamsasMapping(pmaps[m], dnas[m], null,
-//                    false));
-//            alc.addAlcodMap(alcmap);
-//          }
-//        }
+        // TODO: delete this ? dead code from 2.8.3->2.9 ?
+        // {
+        // AlcodonFrame alc = new AlcodonFrame();
+        // vamsasSet.addAlcodonFrame(alc);
+        // for (int p = 0; p < acf.aaWidth; p++)
+        // {
+        // Alcodon cmap = new Alcodon();
+        // if (acf.codons[p] != null)
+        // {
+        // // Null codons indicate a gapped column in the translated peptide
+        // // alignment.
+        // cmap.setPos1(acf.codons[p][0]);
+        // cmap.setPos2(acf.codons[p][1]);
+        // cmap.setPos3(acf.codons[p][2]);
+        // }
+        // alc.addAlcodon(cmap);
+        // }
+        // if (acf.getProtMappings() != null
+        // && acf.getProtMappings().length > 0)
+        // {
+        // SequenceI[] dnas = acf.getdnaSeqs();
+        // jalview.datamodel.Mapping[] pmaps = acf.getProtMappings();
+        // for (int m = 0; m < pmaps.length; m++)
+        // {
+        // AlcodMap alcmap = new AlcodMap();
+        // alcmap.setDnasq(seqHash(dnas[m]));
+        // alcmap.setMapping(createVamsasMapping(pmaps[m], dnas[m], null,
+        // false));
+        // alc.addAlcodMap(alcmap);
+        // }
+        // }
       }
     }
 
@@ -1032,13 +1151,11 @@ public class Jalview2XML
 
             if (sg.cs instanceof jalview.schemes.UserColourScheme)
             {
-              jGroup.setColour(setUserColourScheme(sg.cs, userColours,
-                      jms));
+              jGroup.setColour(setUserColourScheme(sg.cs, userColours, jms));
             }
             else
             {
-              jGroup
-                      .setColour(ColourSchemeProperty.getColourName(sg.cs));
+              jGroup.setColour(ColourSchemeProperty.getColourName(sg.cs));
             }
           }
           else if (sg.cs instanceof jalview.schemes.AnnotationColourGradient)
@@ -1050,8 +1167,7 @@ public class Jalview2XML
           }
           else if (sg.cs instanceof jalview.schemes.UserColourScheme)
           {
-            jGroup
-                    .setColour(setUserColourScheme(sg.cs, userColours, jms));
+            jGroup.setColour(setUserColourScheme(sg.cs, userColours, jms));
           }
           else
           {
@@ -1096,15 +1212,26 @@ public class Jalview2XML
       view.setViewName(av.viewName);
       view.setGatheredViews(av.isGatherViewsHere());
 
-      Rectangle position = ap.av.getExplodedGeometry();
-      if (position == null)
+      Rectangle size = ap.av.getExplodedGeometry();
+      Rectangle position = size;
+      if (size == null)
       {
-        position = ap.alignFrame.getBounds();
+        size = ap.alignFrame.getBounds();
+        if (av.getCodingComplement() != null)
+        {
+          position = ((SplitFrame) ap.alignFrame.getSplitViewContainer())
+                  .getBounds();
+        }
+        else
+        {
+          position = size;
+        }
       }
       view.setXpos(position.x);
       view.setYpos(position.y);
-      view.setWidth(position.width);
-      view.setHeight(position.height);
+
+      view.setWidth(size.width);
+      view.setHeight(size.height);
 
       view.setStartRes(av.startRes);
       view.setStartSeq(av.startSeq);
@@ -1154,6 +1281,7 @@ public class Jalview2XML
       view.setFontName(av.font.getName());
       view.setFontSize(av.font.getSize());
       view.setFontStyle(av.font.getStyle());
+      view.setScaleProteinAsCdna(av.getViewStyle().isScaleProteinAsCdna());
       view.setRenderGaps(av.isRenderGaps());
       view.setShowAnnotation(av.isShowAnnotation());
       view.setShowBoxes(av.getShowBoxes());
@@ -1185,82 +1313,55 @@ public class Jalview2XML
                 .getFeatureRenderer().getRenderOrder()
                 .toArray(new String[0]);
 
-        Vector settingsAdded = new Vector();
-        Object gstyle = null;
-        GraduatedColor gcol = null;
+        Vector<String> settingsAdded = new Vector<String>();
         if (renderOrder != null)
         {
-          for (int ro = 0; ro < renderOrder.length; ro++)
+          for (String featureType : renderOrder)
           {
-            gstyle = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
-                    .getFeatureStyle(renderOrder[ro]);
+            FeatureColourI fcol = ap.getSeqPanel().seqCanvas
+                    .getFeatureRenderer()
+                    .getFeatureStyle(featureType);
             Setting setting = new Setting();
-            setting.setType(renderOrder[ro]);
-            if (gstyle instanceof GraduatedColor)
+            setting.setType(featureType);
+            if (!fcol.isSimpleColour())
             {
-              gcol = (GraduatedColor) gstyle;
-              setting.setColour(gcol.getMaxColor().getRGB());
-              setting.setMincolour(gcol.getMinColor().getRGB());
-              setting.setMin(gcol.getMin());
-              setting.setMax(gcol.getMax());
-              setting.setColourByLabel(gcol.isColourByLabel());
-              setting.setAutoScale(gcol.isAutoScale());
-              setting.setThreshold(gcol.getThresh());
-              setting.setThreshstate(gcol.getThreshType());
+              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(fcol.isAboveThreshold() ? 1
+                      : (fcol.isBelowThreshold() ? 0 : -1));
             }
             else
             {
-              setting.setColour(ap.getSeqPanel().seqCanvas
-                      .getFeatureRenderer()
-                      .getColour(renderOrder[ro]).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);
           }
         }
 
-        // Make sure we save none displayed feature settings
-        Iterator en = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
-                .getFeatureColours().keySet().iterator();
-        while (en.hasNext())
-        {
-          String key = en.next().toString();
-          if (settingsAdded.contains(key))
-          {
-            continue;
-          }
-
-          Setting setting = new Setting();
-          setting.setType(key);
-          setting.setColour(ap.getSeqPanel().seqCanvas.getFeatureRenderer()
-                  .getColour(key).getRGB());
-
-          setting.setDisplay(false);
-          float rorder = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
-                  .getOrder(key);
-          if (rorder > -1)
-          {
-            setting.setOrder(rorder);
-          }
-          fs.addSetting(setting);
-          settingsAdded.addElement(key);
-        }
         // is groups actually supposed to be a map here ?
-        en = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
+        Iterator<String> en = ap.getSeqPanel().seqCanvas
+                .getFeatureRenderer()
                 .getFeatureGroups().iterator();
-        Vector groupsAdded = new Vector();
+        Vector<String> groupsAdded = new Vector<String>();
         while (en.hasNext())
         {
-          String grp = en.next().toString();
+          String grp = en.next();
           if (groupsAdded.contains(grp))
           {
             continue;
@@ -1274,7 +1375,6 @@ public class Jalview2XML
           groupsAdded.addElement(grp);
         }
         jms.setFeatureSettings(fs);
-
       }
 
       if (av.hasHiddenColumns())
@@ -1424,8 +1524,7 @@ public class Jalview2XML
               {
 
                 String varnaStateFile = varna.getStateInfo(model.rna);
-                jarEntryName = RNA_PREFIX + viewId + "_"
-                      + nextCounter();
+                jarEntryName = RNA_PREFIX + viewId + "_" + nextCounter();
                 copyFileToJar(jout, varnaStateFile, jarEntryName);
                 rnaSessions.put(model, jarEntryName);
               }
@@ -1608,8 +1707,8 @@ public class Jalview2XML
 
   private void storeAlignmentAnnotation(AlignmentAnnotation[] aa,
           IdentityHashMap<SequenceGroup, String> groupRefs,
-          AlignmentViewport av,
-          Set<String> calcIdSet, boolean storeDS, SequenceSet vamsasSet)
+          AlignmentViewport av, Set<String> calcIdSet, boolean storeDS,
+          SequenceSet vamsasSet)
   {
 
     for (int i = 0; i < aa.length; i++)
@@ -1639,9 +1738,11 @@ public class Jalview2XML
         if (groupIdr == null)
         {
           // make a locally unique String
-          groupRefs.put(annotation.groupRef,
+          groupRefs.put(
+                  annotation.groupRef,
                   groupIdr = ("" + System.currentTimeMillis()
-                          + annotation.groupRef.getName() + groupRefs.size()));
+                          + annotation.groupRef.getName() + groupRefs
+                          .size()));
         }
         an.setGroupRef(groupIdr.toString());
       }
@@ -1856,8 +1957,8 @@ public class Jalview2XML
       }
     }
     throw new Error(MessageManager.formatMessage(
-            "error.unsupported_version_calcIdparam", new Object[]
-            { calcIdParam.toString() }));
+            "error.unsupported_version_calcIdparam",
+            new Object[] { calcIdParam.toString() }));
   }
 
   /**
@@ -1939,16 +2040,16 @@ public class Jalview2XML
     if (jds.getDatasetSequence() != null)
     {
       vamsasSeq.setDsseqid(seqHash(jds.getDatasetSequence()));
-      if (jds.getDatasetSequence().getDBRef() != null)
+      if (jds.getDatasetSequence().getDBRefs() != null)
       {
-        dbrefs = jds.getDatasetSequence().getDBRef();
+        dbrefs = jds.getDatasetSequence().getDBRefs();
       }
     }
     else
     {
       vamsasSeq.setDsseqid(id); // so we can tell which sequences really are
       // dataset sequences only
-      dbrefs = jds.getDBRef();
+      dbrefs = jds.getDBRefs();
     }
     if (dbrefs != null)
     {
@@ -2169,6 +2270,7 @@ public class Jalview2XML
       {
         SwingUtilities.invokeAndWait(new Runnable()
         {
+          @Override
           public void run()
           {
             setLoadingFinishedForNewStructureViewers();
@@ -2239,13 +2341,8 @@ public class Jalview2XML
     }
     if (seqRefIds == null)
     {
-      seqRefIds = new HashMap<String, SequenceI>();
-    }
-    if (frefedSequence == null)
-    {
-      frefedSequence = new Vector();
+      initSeqRefs();
     }
-
     AlignFrame af = null, _af = null;
     Map<String, AlignFrame> gatherToThisFrame = new HashMap<String, AlignFrame>();
     final String file = jprovider.getFilename();
@@ -2419,7 +2516,8 @@ public class Jalview2XML
             .entrySet())
     {
       AlignFrame af = candidate.getValue();
-      if (!addedToSplitFrames.contains(af)) {
+      if (!addedToSplitFrames.contains(af))
+      {
         Viewport view = candidate.getKey();
         Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(),
                 view.getHeight());
@@ -2454,6 +2552,11 @@ public class Jalview2XML
     int width = (int) dnaFrame.getBounds().getWidth();
     int height = (int) (dnaFrame.getBounds().getHeight()
             + proteinFrame.getBounds().getHeight() + 50);
+
+    /*
+     * SplitFrame location is saved to both enclosed frames
+     */
+    splitFrame.setLocation(dnaFrame.getX(), dnaFrame.getY());
     Desktop.addInternalFrame(splitFrame, title, width, height);
 
     /*
@@ -2658,36 +2761,56 @@ public class Jalview2XML
     // 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));
+        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());
+          }
+        } else {
+          incompleteSeqs.remove(seqId);
+        }
+        tmpSeq.setStart(jseqs[i].getStart());
+        tmpSeq.setEnd(jseqs[i].getEnd());
+        tmpseqs.add(tmpSeq);
         multipleView = true;
       }
       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)
@@ -2695,9 +2818,8 @@ public class Jalview2XML
           hiddenSeqs = new ArrayList<SequenceI>();
         }
 
-        hiddenSeqs.add(seqRefIds.get(seqId));
+        hiddenSeqs.add(tmpSeq);
       }
-
     }
 
     // /
@@ -2706,8 +2828,12 @@ public class Jalview2XML
     SequenceI[] orderedSeqs = tmpseqs
             .toArray(new SequenceI[tmpseqs.size()]);
 
-    Alignment al = new Alignment(orderedSeqs);
+    AlignmentI al = new Alignment(orderedSeqs);
 
+    if (referenceseqForView != null)
+    {
+      al.setSeqrep(referenceseqForView);
+    }
     // / Add the alignment properties
     for (int i = 0; i < vamsasSet.getSequenceSetPropertiesCount(); i++)
     {
@@ -2833,19 +2959,19 @@ public class Jalview2XML
             {
               mapping = addMapping(maps[m].getMapping());
             }
-            if (dnaseq != null)
+            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 });
+              frefedSequence.add(newAlcodMapRef(maps[m].getDnasq(), cf,
+                      mapping));
             }
           }
+          al.addCodonFrame(cf);
         }
-        al.addCodonFrame(cf);
       }
     }
 
@@ -2892,8 +3018,7 @@ public class Jalview2XML
 
         // set visiblity for other annotation in this view
         String annotationId = annotation.getId();
-        if (annotationId != null
-                && annotationIds.containsKey(annotationId))
+        if (annotationId != null && annotationIds.containsKey(annotationId))
         {
           AlignmentAnnotation jda = annotationIds.get(annotationId);
           // in principle Visible should always be true for annotation displayed
@@ -3127,8 +3252,7 @@ public class Jalview2XML
 
         SequenceGroup sg = new SequenceGroup(seqs, jGroup.getName(), cs,
                 jGroup.getDisplayBoxes(), jGroup.getDisplayText(),
-                jGroup.getColourText(), jGroup.getStart(),
-                jGroup.getEnd());
+                jGroup.getColourText(), jGroup.getStart(), jGroup.getEnd());
 
         sg.setOutlineColour(new java.awt.Color(jGroup.getOutlineColour()));
 
@@ -3167,8 +3291,8 @@ public class Jalview2XML
         if (jGroup.getId() != null && groupAnnotRefs.size() > 0)
         {
           // re-instate unique group/annotation row reference
-          List<AlignmentAnnotation> jaal = groupAnnotRefs
-                  .get(jGroup.getId());
+          List<AlignmentAnnotation> jaal = groupAnnotRefs.get(jGroup
+                  .getId());
           if (jaal != null)
           {
             for (AlignmentAnnotation jaa : jaal)
@@ -3196,8 +3320,8 @@ public class Jalview2XML
         if (addAnnotSchemeGroup)
         {
           // reconstruct the annotation colourscheme
-          sg.cs = constructAnnotationColour(
-                  jGroup.getAnnotationColours(), null, al, jms, false);
+          sg.cs = constructAnnotationColour(jGroup.getAnnotationColours(),
+                  null, al, jms, false);
         }
       }
     }
@@ -3253,8 +3377,8 @@ public class Jalview2XML
      * indicate that annotation colours are applied across all groups (pre
      * Jalview 2.8.1 behaviour)
      */
-    boolean doGroupAnnColour = isVersionStringLaterThan("2.8.1",
-            object.getVersion());
+    boolean doGroupAnnColour = Jalview2XML.isVersionStringLaterThan(
+            "2.8.1", object.getVersion());
 
     AlignmentPanel ap = null;
     boolean isnewview = true;
@@ -3329,8 +3453,8 @@ public class Jalview2XML
       for (int i = 0; i < jseq.getRnaViewerCount(); i++)
       {
         RnaViewer viewer = jseq.getRnaViewer(i);
-        AppVarna appVarna = findOrCreateVarnaViewer(viewer, uniqueSetSuffix,
-                ap);
+        AppVarna appVarna = findOrCreateVarnaViewer(viewer,
+                uniqueSetSuffix, ap);
 
         for (int j = 0; j < viewer.getSecondaryStructureCount(); j++)
         {
@@ -3392,8 +3516,8 @@ public class Jalview2XML
      * viewer not found - make it
      */
     RnaViewerModel model = new RnaViewerModel(postLoadId,
-            viewer.getTitle(), viewer.getXpos(),
-            viewer.getYpos(), viewer.getWidth(), viewer.getHeight(),
+            viewer.getTitle(), viewer.getXpos(), viewer.getYpos(),
+            viewer.getWidth(), viewer.getHeight(),
             viewer.getDividerLocation());
     AppVarna varna = new AppVarna(model, ap);
 
@@ -3678,12 +3802,12 @@ public class Jalview2XML
    * @param af
    * @param jprovider
    */
-  protected void createChimeraViewer(Entry<String, StructureViewerModel> viewerData,
-          AlignFrame af,
+  protected void createChimeraViewer(
+          Entry<String, StructureViewerModel> viewerData, AlignFrame af,
           jarInputStreamProvider jprovider)
   {
     StructureViewerModel data = viewerData.getValue();
-    String chimeraSessionFile =  data.getStateData();
+    String chimeraSessionFile = data.getStateData();
 
     /*
      * Copy Chimera session from jar entry "viewer_"+viewId to a temporary file
@@ -3720,8 +3844,8 @@ public class Jalview2XML
     String newViewId = viewerData.getKey();
 
     ChimeraViewFrame cvf = new ChimeraViewFrame(chimeraSessionFile,
-            af.alignPanel, pdbArray,
-            seqsArray, colourByChimera, colourBySequence, newViewId);
+            af.alignPanel, pdbArray, seqsArray, colourByChimera,
+            colourBySequence, newViewId);
     cvf.setSize(data.getWidth(), data.getHeight());
     cvf.setLocation(data.getX(), data.getY());
   }
@@ -3810,16 +3934,23 @@ public class Jalview2XML
       newFileLoc.append(";");
     }
 
-    if (newFileLoc.length() > 0)
+    if (newFileLoc.length() == 0)
     {
-      int histbug = newFileLoc.indexOf("history = ");
+      return;
+    }
+    int histbug = newFileLoc.indexOf("history = ");
+    if (histbug > -1)
+    {
+      /*
+       * change "history = [true|false];" to "history = [1|0];"
+       */
       histbug += 10;
       int diff = histbug == -1 ? -1 : newFileLoc.indexOf(";", histbug);
       String val = (diff == -1) ? null : newFileLoc
               .substring(histbug, diff);
       if (val != null && val.length() >= 4)
       {
-        if (val.contains("e"))
+        if (val.contains("e")) // eh? what can it be?
         {
           if (val.trim().equals("true"))
           {
@@ -3832,54 +3963,55 @@ public class Jalview2XML
           newFileLoc.replace(histbug, diff, val);
         }
       }
+    }
 
-      final String[] pdbf = pdbfilenames.toArray(new String[pdbfilenames
-              .size()]);
-      final String[] id = pdbids.toArray(new String[pdbids.size()]);
-      final SequenceI[][] sq = seqmaps
-              .toArray(new SequenceI[seqmaps.size()][]);
-      final String fileloc = newFileLoc.toString();
-      final String sviewid = viewerData.getKey();
-      final AlignFrame alf = af;
-      final Rectangle rect = new Rectangle(svattrib.getX(),
-              svattrib.getY(), svattrib.getWidth(), svattrib.getHeight());
-      try
+    final String[] pdbf = pdbfilenames.toArray(new String[pdbfilenames
+            .size()]);
+    final String[] id = pdbids.toArray(new String[pdbids.size()]);
+    final SequenceI[][] sq = seqmaps
+            .toArray(new SequenceI[seqmaps.size()][]);
+    final String fileloc = newFileLoc.toString();
+    final String sviewid = viewerData.getKey();
+    final AlignFrame alf = af;
+    final Rectangle rect = new Rectangle(svattrib.getX(), svattrib.getY(),
+            svattrib.getWidth(), svattrib.getHeight());
+    try
+    {
+      javax.swing.SwingUtilities.invokeAndWait(new Runnable()
       {
-        javax.swing.SwingUtilities.invokeAndWait(new Runnable()
+        @Override
+        public void run()
         {
-          @Override
-          public void run()
+          JalviewStructureDisplayI sview = null;
+          try
           {
-            JalviewStructureDisplayI sview = null;
-            try
-            {
-              sview = new StructureViewer(alf.alignPanel
-                      .getStructureSelectionManager()).createView(
-                      StructureViewer.ViewerType.JMOL, pdbf, id, sq,
-                      alf.alignPanel, svattrib, fileloc, rect, sviewid);
-              addNewStructureViewer(sview);
-            } catch (OutOfMemoryError ex)
+            sview = new StructureViewer(alf.alignPanel
+                    .getStructureSelectionManager()).createView(
+                    StructureViewer.ViewerType.JMOL, pdbf, id, sq,
+                    alf.alignPanel, svattrib, fileloc, rect, sviewid);
+            addNewStructureViewer(sview);
+          } catch (OutOfMemoryError ex)
+          {
+            new OOMWarning("restoring structure view for PDB id " + id,
+                    (OutOfMemoryError) ex.getCause());
+            if (sview != null && sview.isVisible())
             {
-              new OOMWarning("restoring structure view for PDB id " + id,
-                      (OutOfMemoryError) ex.getCause());
-              if (sview != null && sview.isVisible())
-              {
-                sview.closeViewer(false);
-                sview.setVisible(false);
-                sview.dispose();
-              }
+              sview.closeViewer(false);
+              sview.setVisible(false);
+              sview.dispose();
             }
           }
-        });
-      } catch (InvocationTargetException ex)
-      {
-        warn("Unexpected error when opening Jmol view.", ex);
+        }
+      });
+    } catch (InvocationTargetException ex)
+    {
+      warn("Unexpected error when opening Jmol view.", ex);
 
-      } catch (InterruptedException e)
-      {
-        // e.printStackTrace();
-      }
+    } catch (InterruptedException e)
+    {
+      // e.printStackTrace();
     }
+
   }
 
   /**
@@ -4024,18 +4156,22 @@ public class Jalview2XML
   }
 
   /**
+   * 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
    */
-  protected boolean isVersionStringLaterThan(String supported,
+  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"))
     {
@@ -4046,38 +4182,8 @@ public class Jalview2XML
     }
     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
-        {
-          if (Float.valueOf(curT) > Float.valueOf(fileT))
-          {
-            // current version is newer than the version that wrote the file
-            return false;
-          }
-        } 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;
@@ -4105,7 +4211,7 @@ public class Jalview2XML
   }
 
   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)
   {
@@ -4121,6 +4227,12 @@ public class Jalview2XML
               .getSequenceAt(i), new java.awt.Color(JSEQ[i].getColour()));
     }
 
+    if (al.hasSeqrep())
+    {
+      af.getViewport().setColourByReferenceSeq(true);
+      af.getViewport().setDisplayReferenceSeq(true);
+    }
+
     af.viewport.setGatherViewsHere(view.getGatheredViews());
 
     if (view.getSequenceSetId() != null)
@@ -4147,25 +4259,25 @@ public class Jalview2XML
     {
       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);
@@ -4191,6 +4303,9 @@ public class Jalview2XML
     af.viewport.setFont(
             new java.awt.Font(view.getFontName(), view.getFontStyle(), view
                     .getFontSize()), true);
+    ViewStyleI vs = af.viewport.getViewStyle();
+    vs.setScaleProteinAsCdna(view.isScaleProteinAsCdna());
+    af.viewport.setViewStyle(vs);
     // TODO: allow custom charWidth/Heights to be restored by updating them
     // after setting font - which means set above to false
     af.viewport.setRenderGaps(view.getRenderGaps());
@@ -4321,25 +4436,33 @@ public class Jalview2XML
       af.viewport.setFeaturesDisplayed(fdi = new FeaturesDisplayed());
       String[] renderOrder = new String[jms.getFeatureSettings()
               .getSettingCount()];
-      Hashtable featureGroups = new Hashtable();
-      Hashtable featureColours = new Hashtable();
-      Hashtable featureOrder = new Hashtable();
+      Map<String, FeatureColourI> featureColours = new Hashtable<String, FeatureColourI>();
+      Map<String, Float> featureOrder = new Hashtable<String, Float>();
 
       for (int fs = 0; fs < jms.getFeatureSettings().getSettingCount(); fs++)
       {
         Setting setting = jms.getFeatureSettings().getSetting(fs);
         if (setting.hasMincolour())
         {
-          GraduatedColor gc = setting.hasMin() ? new GraduatedColor(
-                  new java.awt.Color(setting.getMincolour()),
-                  new java.awt.Color(setting.getColour()),
-                  setting.getMin(), setting.getMax()) : new GraduatedColor(
-                  new java.awt.Color(setting.getMincolour()),
-                  new java.awt.Color(setting.getColour()), 0, 1);
+          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()),
+                  0, 1);
           if (setting.hasThreshold())
           {
-            gc.setThresh(setting.getThreshold());
-            gc.setThreshType(setting.getThreshstate());
+            gc.setThreshold(setting.getThreshold());
+            int threshstate = setting.getThreshstate();
+            // -1 = None, 0 = Below, 1 = Above threshold
+            if (threshstate == 0)
+            {
+              gc.setBelowThreshold(true);
+            }
+            else if (threshstate == 1)
+            {
+              gc.setAboveThreshold(true);
+            }
           }
           gc.setAutoScaled(true); // default
           if (setting.hasAutoScale())
@@ -4355,8 +4478,8 @@ public class Jalview2XML
         }
         else
         {
-          featureColours.put(setting.getType(),
-                  new java.awt.Color(setting.getColour()));
+          featureColours.put(setting.getType(), new FeatureColour(
+                  new Color(setting.getColour())));
         }
         renderOrder[fs] = setting.getType();
         if (setting.hasOrder())
@@ -4373,7 +4496,7 @@ public class Jalview2XML
           fdi.setVisible(setting.getType());
         }
       }
-      Hashtable fgtable = new Hashtable();
+      Map<String, Boolean> fgtable = new Hashtable<String, Boolean>();
       for (int gs = 0; gs < jms.getFeatureSettings().getGroupCount(); gs++)
       {
         Group grp = jms.getFeatureSettings().getGroup(gs);
@@ -4416,7 +4539,7 @@ public class Jalview2XML
       }
     }
     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
@@ -4441,7 +4564,7 @@ public class Jalview2XML
   }
 
   private ColourSchemeI constructAnnotationColour(
-          AnnotationColours viewAnnColour, AlignFrame af, Alignment al,
+          AnnotationColours viewAnnColour, AlignFrame af, AlignmentI al,
           JalviewModelSequence jms, boolean checkGroupAnnColour)
   {
     boolean propagateAnnColour = false;
@@ -4565,7 +4688,7 @@ public class Jalview2XML
     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
@@ -4575,8 +4698,8 @@ public class Jalview2XML
       /**
        * Kludge for magic autoannotation names (see JAL-811)
        */
-      String[] magicNames = new String[]
-      { "Consensus", "Quality", "Conservation" };
+      String[] magicNames = new String[] { "Consensus", "Quality",
+          "Conservation" };
       JvAnnotRow nullAnnot = new JvAnnotRow(-1, null);
       Hashtable<String, JvAnnotRow> visan = new Hashtable<String, JvAnnotRow>();
       for (String nm : magicNames)
@@ -4720,10 +4843,11 @@ public class Jalview2XML
     }
   }
 
-  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)
     {
@@ -4873,15 +4997,15 @@ public class Jalview2XML
    * 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))
@@ -4891,11 +5015,11 @@ public class Jalview2XML
     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);
   }
@@ -4906,7 +5030,7 @@ public class Jalview2XML
    * @param dataset
    * @return
    */
-  private String getDatasetIdRef(Alignment dataset)
+  private String getDatasetIdRef(AlignmentI dataset)
   {
     if (dataset.getDataset() != null)
     {
@@ -4918,7 +5042,7 @@ public class Jalview2XML
       // make a new datasetId and record it
       if (dataset2Ids == null)
       {
-        dataset2Ids = new IdentityHashMap<Alignment, String>();
+        dataset2Ids = new IdentityHashMap<AlignmentI, String>();
       }
       else
       {
@@ -4986,8 +5110,7 @@ public class Jalview2XML
         }
         else
         {
-          frefedSequence.add(new Object[]
-          { dsfor, jmap });
+          frefedSequence.add(newMappingRef(dsfor, jmap));
         }
       }
       else
@@ -5025,6 +5148,7 @@ public class Jalview2XML
           djs.setEnd(jmap.getMap().getToHighest());
           djs.setVamsasId(uniqueSetSuffix + sqid);
           jmap.setTo(djs);
+          incompleteSeqs.put(sqid, djs);
           seqRefIds.put(sqid, djs);
 
         }
@@ -5321,7 +5445,7 @@ public class Jalview2XML
         }
       }
     }
-  
+
     return result;
   }