JAL-1620 version bump and release notes
[jalview.git] / src / jalview / gui / Jalview2XML.java
old mode 100755 (executable)
new mode 100644 (file)
index 51e26b8..f3854fe
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Development Version 2.4.1)
- * Copyright (C) 2009 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2b1)
+ * Copyright (C) 2014 The Jalview Authors
  * 
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
+ * This file is part of Jalview.
  * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
  * 
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
 package jalview.gui;
 
-import java.awt.Rectangle;
-import java.io.*;
-import java.net.*;
-import java.util.*;
-import java.util.jar.*;
-
-import javax.swing.*;
-
-import org.exolab.castor.xml.*;
-
-import uk.ac.vamsas.objects.utils.MapList;
+import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
 import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceI;
-import jalview.schemabinding.version2.*;
-import jalview.schemes.*;
+import jalview.schemabinding.version2.AlcodMap;
+import jalview.schemabinding.version2.Alcodon;
+import jalview.schemabinding.version2.AlcodonFrame;
+import jalview.schemabinding.version2.Annotation;
+import jalview.schemabinding.version2.AnnotationColours;
+import jalview.schemabinding.version2.AnnotationElement;
+import jalview.schemabinding.version2.CalcIdParam;
+import jalview.schemabinding.version2.DBRef;
+import jalview.schemabinding.version2.Features;
+import jalview.schemabinding.version2.Group;
+import jalview.schemabinding.version2.HiddenColumns;
+import jalview.schemabinding.version2.JGroup;
+import jalview.schemabinding.version2.JSeq;
+import jalview.schemabinding.version2.JalviewModel;
+import jalview.schemabinding.version2.JalviewModelSequence;
+import jalview.schemabinding.version2.MapListFrom;
+import jalview.schemabinding.version2.MapListTo;
+import jalview.schemabinding.version2.Mapping;
+import jalview.schemabinding.version2.MappingChoice;
+import jalview.schemabinding.version2.OtherData;
+import jalview.schemabinding.version2.PdbentryItem;
+import jalview.schemabinding.version2.Pdbids;
+import jalview.schemabinding.version2.Property;
+import jalview.schemabinding.version2.Sequence;
+import jalview.schemabinding.version2.SequenceSet;
+import jalview.schemabinding.version2.SequenceSetProperties;
+import jalview.schemabinding.version2.Setting;
+import jalview.schemabinding.version2.StructureState;
+import jalview.schemabinding.version2.ThresholdLine;
+import jalview.schemabinding.version2.Tree;
+import jalview.schemabinding.version2.UserColours;
+import jalview.schemabinding.version2.Viewport;
+import jalview.schemes.AnnotationColourGradient;
+import jalview.schemes.ColourSchemeI;
+import jalview.schemes.ColourSchemeProperty;
+import jalview.schemes.GraduatedColor;
+import jalview.schemes.ResidueColourScheme;
+import jalview.schemes.ResidueProperties;
 import jalview.structure.StructureSelectionManager;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
 import jalview.util.jarInputStreamProvider;
+import jalview.viewmodel.AlignmentViewport;
+import jalview.ws.jws2.Jws2Discoverer;
+import jalview.ws.jws2.dm.AAConSettings;
+import jalview.ws.jws2.jabaws2.Jws2Instance;
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.AutoCalcSetting;
+import jalview.ws.params.WsParamSetI;
+
+import java.awt.Rectangle;
+import java.io.BufferedReader;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.List;
+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;
+import java.util.jar.JarOutputStream;
+
+import javax.swing.JInternalFrame;
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+
+import org.exolab.castor.xml.Unmarshaller;
 
 /**
  * Write out the current jalview desktop state as a Jalview XML stream.
@@ -46,7 +119,7 @@ import jalview.util.jarInputStreamProvider;
  * will be :)
  * 
  * @author $author$
- * @version $Revision$
+ * @version $Revision: 1.134 $
  */
 public class Jalview2XML
 {
@@ -270,6 +343,8 @@ public class Jalview2XML
       return;
     }
 
+    Hashtable<String, AlignFrame> dsses = new Hashtable<String, AlignFrame>();
+
     try
     {
 
@@ -324,6 +399,7 @@ public class Jalview2XML
           }
 
           int ap, apSize = af.alignPanels.size();
+
           for (ap = 0; ap < apSize; ap++)
           {
             AlignmentPanel apanel = (AlignmentPanel) af.alignPanels
@@ -335,9 +411,21 @@ public class Jalview2XML
             }
 
             SaveState(apanel, fileName, jout);
+
+            String dssid = getDatasetIdRef(af.getViewport().getAlignment()
+                    .getDataset());
+            if (!dsses.containsKey(dssid))
+            {
+              dsses.put(dssid, af);
+            }
+
           }
         }
       }
+
+      writeDatasetFor(dsses, "" + jout.hashCode() + " " + uniqueSetSuffix,
+              jout);
+
       try
       {
         jout.flush();
@@ -367,6 +455,7 @@ public class Jalview2XML
       int ap, apSize = af.alignPanels.size();
       FileOutputStream fos = new FileOutputStream(jarFile);
       JarOutputStream jout = new JarOutputStream(fos);
+      Hashtable<String, AlignFrame> dsses = new Hashtable<String, AlignFrame>();
       for (ap = 0; ap < apSize; ap++)
       {
         AlignmentPanel apanel = (AlignmentPanel) af.alignPanels
@@ -377,8 +466,14 @@ public class Jalview2XML
           jfileName = jfileName + ".xml";
         }
         SaveState(apanel, jfileName, jout);
+        String dssid = getDatasetIdRef(af.getViewport().getAlignment()
+                .getDataset());
+        if (!dsses.containsKey(dssid))
+        {
+          dsses.put(dssid, af);
+        }
       }
-
+      writeDatasetFor(dsses, fileName, jout);
       try
       {
         jout.flush();
@@ -396,6 +491,22 @@ public class Jalview2XML
     }
   }
 
+  private void writeDatasetFor(Hashtable<String, AlignFrame> dsses,
+          String fileName, JarOutputStream jout)
+  {
+
+    for (String dssids : dsses.keySet())
+    {
+      AlignFrame _af = dsses.get(dssids);
+      String jfileName = fileName + " Dataset for " + _af.getTitle();
+      if (!jfileName.endsWith(".xml"))
+      {
+        jfileName = jfileName + ".xml";
+      }
+      SaveState(_af.alignPanel, jfileName, true, jout);
+    }
+  }
+
   /**
    * create a JalviewModel from an algnment view and marshall it to a
    * JarOutputStream
@@ -412,8 +523,30 @@ public class Jalview2XML
   public JalviewModel SaveState(AlignmentPanel ap, String fileName,
           JarOutputStream jout)
   {
-    initSeqRefs();
+    return SaveState(ap, fileName, false, jout);
+  }
 
+  /**
+   * create a JalviewModel from an algnment view and marshall it to a
+   * JarOutputStream
+   * 
+   * @param ap
+   *          panel to create jalview model for
+   * @param fileName
+   *          name of alignment panel written to output stream
+   * @param storeDS
+   *          when true, only write the dataset for the alignment, not the data
+   *          associated with the view.
+   * @param jout
+   *          jar output stream
+   * @param out
+   *          jar entry name
+   */
+  public JalviewModel SaveState(AlignmentPanel ap, String fileName,
+          boolean storeDS, JarOutputStream jout)
+  {
+    initSeqRefs();
+    Vector jmolViewIds = new Vector(); //
     Vector userColours = new Vector();
 
     AlignViewport av = ap.av;
@@ -422,11 +555,12 @@ public class Jalview2XML
     object.setVamsasModel(new jalview.schemabinding.version2.VamsasModel());
 
     object.setCreationDate(new java.util.Date(System.currentTimeMillis()));
-    object.setVersion(jalview.bin.Cache.getProperty("VERSION"));
+    object.setVersion(jalview.bin.Cache.getDefault("VERSION",
+            "Development Build"));
 
-    jalview.datamodel.AlignmentI jal = av.alignment;
+    jalview.datamodel.AlignmentI jal = av.getAlignment();
 
-    if (av.hasHiddenRows)
+    if (av.hasHiddenRows())
     {
       jal = jal.getHiddenSequences().getFullAlignment();
     }
@@ -441,6 +575,11 @@ public class Jalview2XML
     {
       // dataset id is the dataset's hashcode
       vamsasSet.setDatasetId(getDatasetIdRef(jal.getDataset()));
+      if (storeDS)
+      {
+        // switch jal and the dataset
+        jal = jal.getDataset();
+      }
     }
     if (jal.getProperties() != null)
     {
@@ -456,13 +595,16 @@ public class Jalview2XML
     }
 
     JSeq jseq;
+    Set<String> calcIdSet = new HashSet<String>();
 
     // SAVE SEQUENCES
     String id = "";
-    jalview.datamodel.SequenceI jds;
+    jalview.datamodel.SequenceI jds, jdatasq;
     for (int i = 0; i < jal.getHeight(); i++)
     {
       jds = jal.getSequenceAt(i);
+      jdatasq = jds.getDatasetSequence() == null ? jds : jds
+              .getDatasetSequence();
       id = seqHash(jds);
 
       if (seqRefIds.get(id) != null)
@@ -494,30 +636,34 @@ public class Jalview2XML
       jseq.setColour(av.getSequenceColour(jds).getRGB());
 
       jseq.setId(id); // jseq id should be a string not a number
-
-      if (av.hasHiddenRows)
+      if (!storeDS)
       {
-        jseq.setHidden(av.alignment.getHiddenSequences().isHidden(jds));
-
-        if (av.hiddenRepSequences != null
-                && av.hiddenRepSequences.containsKey(jal.getSequenceAt(i)))
+        // Store any sequences this sequence represents
+        if (av.hasHiddenRows())
         {
-          jalview.datamodel.SequenceI[] reps = ((jalview.datamodel.SequenceGroup) av.hiddenRepSequences
-                  .get(jal.getSequenceAt(i))).getSequencesInOrder(jal);
+          jseq.setHidden(av.getAlignment().getHiddenSequences()
+                  .isHidden(jds));
 
-          for (int h = 0; h < reps.length; h++)
+          if (av.isHiddenRepSequence(jal.getSequenceAt(i)))
           {
-            if (reps[h] != jal.getSequenceAt(i))
+            jalview.datamodel.SequenceI[] reps = av
+                    .getRepresentedSequences(jal.getSequenceAt(i))
+                    .getSequencesInOrder(jal);
+
+            for (int h = 0; h < reps.length; h++)
             {
-              jseq.addHiddenSequences(jal.findIndex(reps[h]));
+              if (reps[h] != jal.getSequenceAt(i))
+              {
+                jseq.addHiddenSequences(jal.findIndex(reps[h]));
+              }
             }
           }
         }
       }
 
-      if (jds.getDatasetSequence().getSequenceFeatures() != null)
+      if (jdatasq.getSequenceFeatures() != null)
       {
-        jalview.datamodel.SequenceFeature[] sf = jds.getDatasetSequence()
+        jalview.datamodel.SequenceFeature[] sf = jdatasq
                 .getSequenceFeatures();
         int index = 0;
         while (index < sf.length)
@@ -559,9 +705,9 @@ public class Jalview2XML
         }
       }
 
-      if (jds.getDatasetSequence().getPDBId() != null)
+      if (jdatasq.getPDBId() != null)
       {
-        Enumeration en = jds.getDatasetSequence().getPDBId().elements();
+        Enumeration en = jdatasq.getPDBId().elements();
         while (en.hasMoreElements())
         {
           Pdbids pdb = new Pdbids();
@@ -570,7 +716,10 @@ public class Jalview2XML
 
           pdb.setId(entry.getId());
           pdb.setType(entry.getType());
-
+          //
+          // store any JMol views associated with this seqeunce
+          // this section copes with duplicate entries in the project, so a
+          // dataset only view *should* be coped with sensibly
           AppJmol jmol;
           // This must have been loaded, is it still visible?
           JInternalFrame[] frames = Desktop.desktop.getAllFrames();
@@ -580,32 +729,66 @@ public class Jalview2XML
             if (frames[f] instanceof AppJmol)
             {
               jmol = (AppJmol) frames[f];
-              if (!jmol.pdbentry.getId().equals(entry.getId())
-                      && !(entry.getId().length() > 4 && entry.getId()
-                              .toLowerCase().startsWith(
-                                      jmol.pdbentry.getId().toLowerCase())))
-                continue;
-              matchedFile = jmol.pdbentry.getFile(); // record the file so we
-                                                     // can get at it if the ID
-                                                     // match is ambiguous (e.g.
-                                                     // 1QIP==1qipA)
-              StructureState state = new StructureState();
-              state.setVisible(true);
-              state.setXpos(jmol.getX());
-              state.setYpos(jmol.getY());
-              state.setWidth(jmol.getWidth());
-              state.setHeight(jmol.getHeight());
-              state.setViewId(jmol.getViewId());
-              String statestring = jmol.viewer.getStateInfo();
-              if (state != null)
-              {
-                state.setContent(statestring.replaceAll("\n", ""));
-              }
-              for (int s = 0; s < jmol.sequence.length; s++)
+              for (int peid = 0; peid < jmol.jmb.pdbentry.length; peid++)
               {
-                if (jal.findIndex(jmol.sequence[s]) > -1)
+                if (!jmol.jmb.pdbentry[peid].getId().equals(entry.getId())
+                        && !(entry.getId().length() > 4 && entry
+                                .getId()
+                                .toLowerCase()
+                                .startsWith(
+                                        jmol.jmb.pdbentry[peid].getId()
+                                                .toLowerCase())))
+                {
+                  continue;
+                }
+                if (matchedFile == null)
+                {
+                  matchedFile = jmol.jmb.pdbentry[peid].getFile();
+                }
+                else if (!matchedFile.equals(jmol.jmb.pdbentry[peid]
+                        .getFile()))
+                {
+                  Cache.log
+                          .warn("Probably lost some PDB-Sequence mappings for this structure file (which apparently has same PDB Entry code): "
+                                  + jmol.jmb.pdbentry[peid].getFile());
+                  ; // record the
+                }
+                // file so we
+                // can get at it if the ID
+                // match is ambiguous (e.g.
+                // 1QIP==1qipA)
+                String statestring = jmol.jmb.viewer.getStateInfo();
+
+                for (int smap = 0; smap < jmol.jmb.sequence[peid].length; smap++)
                 {
-                  pdb.addStructureState(state);
+                  // if (jal.findIndex(jmol.jmb.sequence[peid][smap]) > -1)
+                  if (jds == jmol.jmb.sequence[peid][smap])
+                  {
+                    StructureState state = new StructureState();
+                    state.setVisible(true);
+                    state.setXpos(jmol.getX());
+                    state.setYpos(jmol.getY());
+                    state.setWidth(jmol.getWidth());
+                    state.setHeight(jmol.getHeight());
+                    state.setViewId(jmol.getViewId());
+                    state.setAlignwithAlignPanel(jmol.isUsedforaligment(ap));
+                    state.setColourwithAlignPanel(jmol
+                            .isUsedforcolourby(ap));
+                    state.setColourByJmol(jmol.isColouredByJmol());
+                    if (!jmolViewIds.contains(state.getViewId()))
+                    {
+                      // Make sure we only store a Jmol state once in each XML
+                      // document.
+                      jmolViewIds.addElement(state.getViewId());
+                      state.setContent(statestring.replaceAll("\n", ""));
+                    }
+                    else
+                    {
+                      state.setContent("# duplicate state");
+                    }
+                    pdb.addStructureState(state);
+                  }
+
                 }
               }
             }
@@ -674,9 +857,9 @@ public class Jalview2XML
       jms.addJSeq(jseq);
     }
 
-    if (av.hasHiddenRows)
+    if (!storeDS && av.hasHiddenRows())
     {
-      jal = av.alignment;
+      jal = av.getAlignment();
     }
     // SAVE MAPPINGS
     if (jal.getCodonFrames() != null && jal.getCodonFrames().length > 0)
@@ -718,7 +901,7 @@ public class Jalview2XML
 
     // SAVE TREES
     // /////////////////////////////////
-    if (av.currentTree != null)
+    if (!storeDS && av.currentTree != null)
     {
       // FIND ANY ASSOCIATED TREES
       // NOT IMPLEMENTED FOR HEADLESS STATE AT PRESENT
@@ -732,7 +915,7 @@ public class Jalview2XML
           {
             TreePanel tp = (TreePanel) frames[t];
 
-            if (tp.treeCanvas.av.alignment == jal)
+            if (tp.treeCanvas.av.getAlignment() == jal)
             {
               Tree tree = new Tree();
               tree.setTitle(tp.getTitle());
@@ -760,144 +943,50 @@ public class Jalview2XML
         }
       }
     }
-
     // SAVE ANNOTATIONS
     /**
      * store forward refs from an annotationRow to any groups
      */
     IdentityHashMap groupRefs = new IdentityHashMap();
-    if (jal.getAlignmentAnnotation() != null)
+    if (storeDS)
     {
-      jalview.datamodel.AlignmentAnnotation[] aa = jal
-              .getAlignmentAnnotation();
-
-      for (int i = 0; i < aa.length; i++)
+      for (SequenceI sq : jal.getSequences())
       {
-        Annotation an = new Annotation();
-
-        if (aa[i].annotationId != null)
-        {
-          annotationIds.put(aa[i].annotationId, aa[i]);
-        }
-
-        an.setId(aa[i].annotationId);
-
-        
-        an.setVisible(aa[i].visible);
-
-        an.setDescription(aa[i].description);
-
-        if (aa[i].sequenceRef != null)
-        {
-          // TODO later annotation sequenceRef should be the XML ID of the
-          // sequence rather than its display name
-          an.setSequenceRef(aa[i].sequenceRef.getName());
-        }
-        if (aa[i].groupRef!=null)
-        {
-          Object groupIdr = groupRefs.get(aa[i].groupRef);
-          if (groupIdr==null)
-          {
-            // make a locally unique String
-            groupRefs.put(aa[i].groupRef, groupIdr = (""+System.currentTimeMillis()+aa[i].groupRef.getName()+groupRefs.size()));
-          }
-          an.setGroupRef(groupIdr.toString());
-        }
-        if (aa[i] == av.quality || aa[i] == av.conservation
-                || aa[i] == av.consensus || aa[i].autoCalculated)
-        {
-          // new way of indicating autocalculated annotation - 
-          an.setAutoCalculated(aa[i].autoCalculated);
-          // write a stub for this annotation - indicate presence of autocalc rows
-          an.setLabel(aa[i].label);
-          an.setGraph(true);
-          vamsasSet.addAnnotation(an);
-          continue;
-        }
-
-        if (aa[i].graph > 0)
-        {
-          an.setGraph(true);
-          an.setGraphType(aa[i].graph);
-          an.setGraphGroup(aa[i].graphGroup);
-          if (aa[i].getThreshold() != null)
-          {
-            ThresholdLine line = new ThresholdLine();
-            line.setLabel(aa[i].getThreshold().label);
-            line.setValue(aa[i].getThreshold().value);
-            line.setColour(aa[i].getThreshold().colour.getRGB());
-            an.setThresholdLine(line);
-          }
-        }
-        else
-        {
-          an.setGraph(false);
-        }
-
-        an.setLabel(aa[i].label);
-        if (aa[i].hasScore())
-        {
-          an.setScore(aa[i].getScore());
-        }
-        AnnotationElement ae;
-        if (aa[i].annotations != null)
+        // Store annotation on dataset sequences only
+        jalview.datamodel.AlignmentAnnotation[] aa = sq.getAnnotation();
+        if (aa != null && aa.length > 0)
         {
-          an.setScoreOnly(false);
-          for (int a = 0; a < aa[i].annotations.length; a++)
-          {
-            if ((aa[i] == null) || (aa[i].annotations[a] == null))
-            {
-              continue;
-            }
-
-            ae = new AnnotationElement();
-            if (aa[i].annotations[a].description != null)
-              ae.setDescription(aa[i].annotations[a].description);
-            if (aa[i].annotations[a].displayCharacter != null)
-              ae.setDisplayCharacter(aa[i].annotations[a].displayCharacter);
-
-            if (!Float.isNaN(aa[i].annotations[a].value))
-              ae.setValue(aa[i].annotations[a].value);
-
-            ae.setPosition(a);
-            if (aa[i].annotations[a].secondaryStructure != ' '
-                    && aa[i].annotations[a].secondaryStructure != '\0')
-              ae
-                      .setSecondaryStructure(aa[i].annotations[a].secondaryStructure
-                              + "");
-
-            if (aa[i].annotations[a].colour != null
-                    && aa[i].annotations[a].colour != java.awt.Color.black)
-            {
-              ae.setColour(aa[i].annotations[a].colour.getRGB());
-            }
-
-            an.addAnnotationElement(ae);
-          }
+          storeAlignmentAnnotation(aa, groupRefs, av, calcIdSet, storeDS,
+                  vamsasSet);
         }
-        else
-        {
-          an.setScoreOnly(true);
-        }
-        vamsasSet.addAnnotation(an);
+      }
+    }
+    else
+    {
+      if (jal.getAlignmentAnnotation() != null)
+      {
+        // Store the annotation shown on the alignment.
+        jalview.datamodel.AlignmentAnnotation[] aa = jal
+                .getAlignmentAnnotation();
+        storeAlignmentAnnotation(aa, groupRefs, av, calcIdSet, storeDS,
+                vamsasSet);
       }
     }
     // SAVE GROUPS
     if (jal.getGroups() != null)
     {
       JGroup[] groups = new JGroup[jal.getGroups().size()];
-
-      for (int i = 0; i < groups.length; i++)
+      int i = -1;
+      for (jalview.datamodel.SequenceGroup sg : jal.getGroups())
       {
-        groups[i] = new JGroup();
+        groups[++i] = new JGroup();
 
-        jalview.datamodel.SequenceGroup sg = (jalview.datamodel.SequenceGroup) jal
-                .getGroups().elementAt(i);
         groups[i].setStart(sg.getStartRes());
         groups[i].setEnd(sg.getEndRes());
-        groups[i].setName(sg.getName()); 
-        if (groupRefs.containsKey(sg)) {
-          // group has references so set it's ID field 
+        groups[i].setName(sg.getName());
+        if (groupRefs.containsKey(sg))
+        {
+          // group has references so set it's ID field
           groups[i].setId(groupRefs.get(sg).toString());
         }
         if (sg.cs != null)
@@ -919,10 +1008,10 @@ public class Jalview2XML
           }
           else if (sg.cs instanceof jalview.schemes.AnnotationColourGradient)
           {
-            groups[i]
-                    .setColour(ColourSchemeProperty
-                            .getColourName(((jalview.schemes.AnnotationColourGradient) sg.cs)
-                                    .getBaseColour()));
+            groups[i].setColour("AnnotationColourGradient");
+            groups[i].setAnnotationColours(constructAnnotationColours(
+                    (jalview.schemes.AnnotationColourGradient) sg.cs,
+                    userColours, jms));
           }
           else if (sg.cs instanceof jalview.schemes.UserColourScheme)
           {
@@ -944,10 +1033,11 @@ public class Jalview2XML
         groups[i].setTextCol1(sg.textColour.getRGB());
         groups[i].setTextCol2(sg.textColour2.getRGB());
         groups[i].setTextColThreshold(sg.thresholdTextColour);
-        groups[i].setShowUnconserved(sg.getShowunconserved());
+        groups[i].setShowUnconserved(sg.getShowNonconserved());
         groups[i].setIgnoreGapsinConsensus(sg.getIgnoreGapsConsensus());
         groups[i].setShowConsensusHistogram(sg.isShowConsensusHistogram());
-        groups[i].setShowConsensusProfile(sg.isIncludeAllConsSymbols());
+        groups[i].setShowSequenceLogo(sg.isShowSequenceLogo());
+        groups[i].setNormaliseSequenceLogo(sg.isNormaliseSequenceLogo());
         for (int s = 0; s < sg.getSize(); s++)
         {
           jalview.datamodel.Sequence seq = (jalview.datamodel.Sequence) sg
@@ -958,228 +1048,238 @@ public class Jalview2XML
 
       jms.setJGroup(groups);
     }
-
-    // /////////SAVE VIEWPORT
-    Viewport view = new Viewport();
-    view.setTitle(ap.alignFrame.getTitle());
-    view.setSequenceSetId(makeHashCode(av.getSequenceSetId(), av
-            .getSequenceSetId()));
-    view.setId(av.getViewId());
-    view.setViewName(av.viewName);
-    view.setGatheredViews(av.gatherViewsHere);
-
-    if (ap.av.explodedPosition != null)
-    {
-      view.setXpos(av.explodedPosition.x);
-      view.setYpos(av.explodedPosition.y);
-      view.setWidth(av.explodedPosition.width);
-      view.setHeight(av.explodedPosition.height);
-    }
-    else
-    {
-      view.setXpos(ap.alignFrame.getBounds().x);
-      view.setYpos(ap.alignFrame.getBounds().y);
-      view.setWidth(ap.alignFrame.getBounds().width);
-      view.setHeight(ap.alignFrame.getBounds().height);
-    }
-
-    view.setStartRes(av.startRes);
-    view.setStartSeq(av.startSeq);
-
-    if (av.getGlobalColourScheme() instanceof jalview.schemes.UserColourScheme)
-    {
-      view.setBgColour(SetUserColourScheme(av.getGlobalColourScheme(),
-              userColours, jms));
-    }
-    else if (av.getGlobalColourScheme() instanceof jalview.schemes.AnnotationColourGradient)
+    if (!storeDS)
     {
-      jalview.schemes.AnnotationColourGradient acg = (jalview.schemes.AnnotationColourGradient) av
-              .getGlobalColourScheme();
-
-      AnnotationColours ac = new AnnotationColours();
-      ac.setAboveThreshold(acg.getAboveThreshold());
-      ac.setThreshold(acg.getAnnotationThreshold());
-      ac.setAnnotation(acg.getAnnotation());
-      if (acg.getBaseColour() instanceof jalview.schemes.UserColourScheme)
+      // /////////SAVE VIEWPORT
+      Viewport view = new Viewport();
+      view.setTitle(ap.alignFrame.getTitle());
+      view.setSequenceSetId(makeHashCode(av.getSequenceSetId(),
+              av.getSequenceSetId()));
+      view.setId(av.getViewId());
+      view.setViewName(av.viewName);
+      view.setGatheredViews(av.gatherViewsHere);
+
+      if (ap.av.explodedPosition != null)
       {
-        ac.setColourScheme(SetUserColourScheme(acg.getBaseColour(),
-                userColours, jms));
+        view.setXpos(av.explodedPosition.x);
+        view.setYpos(av.explodedPosition.y);
+        view.setWidth(av.explodedPosition.width);
+        view.setHeight(av.explodedPosition.height);
       }
       else
       {
-        ac.setColourScheme(ColourSchemeProperty.getColourName(acg
-                .getBaseColour()));
+        view.setXpos(ap.alignFrame.getBounds().x);
+        view.setYpos(ap.alignFrame.getBounds().y);
+        view.setWidth(ap.alignFrame.getBounds().width);
+        view.setHeight(ap.alignFrame.getBounds().height);
       }
 
-      ac.setMaxColour(acg.getMaxColour().getRGB());
-      ac.setMinColour(acg.getMinColour().getRGB());
-      view.setAnnotationColours(ac);
-      view.setBgColour("AnnotationColourGradient");
-    }
-    else
-    {
-      view.setBgColour(ColourSchemeProperty.getColourName(av
-              .getGlobalColourScheme()));
-    }
-
-    ColourSchemeI cs = av.getGlobalColourScheme();
+      view.setStartRes(av.startRes);
+      view.setStartSeq(av.startSeq);
 
-    if (cs != null)
-    {
-      if (cs.conservationApplied())
+      if (av.getGlobalColourScheme() instanceof jalview.schemes.UserColourScheme)
       {
-        view.setConsThreshold(cs.getConservationInc());
-        if (cs instanceof jalview.schemes.UserColourScheme)
-        {
-          view.setBgColour(SetUserColourScheme(cs, userColours, jms));
-        }
+        view.setBgColour(SetUserColourScheme(av.getGlobalColourScheme(),
+                userColours, jms));
       }
+      else if (av.getGlobalColourScheme() instanceof jalview.schemes.AnnotationColourGradient)
+      {
+        AnnotationColours ac = constructAnnotationColours(
+                (jalview.schemes.AnnotationColourGradient) av
+                        .getGlobalColourScheme(),
+                userColours, jms);
 
-      if (cs instanceof ResidueColourScheme)
+        view.setAnnotationColours(ac);
+        view.setBgColour("AnnotationColourGradient");
+      }
+      else
       {
-        view.setPidThreshold(cs.getThreshold());
+        view.setBgColour(ColourSchemeProperty.getColourName(av
+                .getGlobalColourScheme()));
       }
-    }
 
-    view.setConservationSelected(av.getConservationSelected());
-    view.setPidSelected(av.getAbovePIDThreshold());
-    view.setFontName(av.font.getName());
-    view.setFontSize(av.font.getSize());
-    view.setFontStyle(av.font.getStyle());
-    view.setRenderGaps(av.renderGaps);
-    view.setShowAnnotation(av.getShowAnnotation());
-    view.setShowBoxes(av.getShowBoxes());
-    view.setShowColourText(av.getColourText());
-    view.setShowFullId(av.getShowJVSuffix());
-    view.setRightAlignIds(av.rightAlignIds);
-    view.setShowSequenceFeatures(av.showSequenceFeatures);
-    view.setShowText(av.getShowText());
-    view.setShowUnconserved(av.getShowUnconserved());
-    view.setWrapAlignment(av.getWrapAlignment());
-    view.setTextCol1(av.textColour.getRGB());
-    view.setTextCol2(av.textColour2.getRGB());
-    view.setTextColThreshold(av.thresholdTextColour);
-    view.setShowConsensusHistogram(av.isShowConsensusHistogram());
-    view.setShowConsensusProfile(av.isShowConsensusProfile());
-    view.setShowGroupConsensus(av.isShowGroupConsensus());
-    view.setShowGroupConservation(av.isShowGroupConservation());
-    view.setShowNPfeatureTooltip(av.isShowNpFeats());
-    view.setShowDbRefTooltip(av.isShowDbRefs());
-    view.setFollowHighlight(av.followHighlight);
-    view.setFollowSelection(av.followSelection);
-    view.setIgnoreGapsinConsensus(av.getIgnoreGapsConsensus());
-    if (av.featuresDisplayed != null)
-    {
-      jalview.schemabinding.version2.FeatureSettings fs = new jalview.schemabinding.version2.FeatureSettings();
-
-      String[] renderOrder = ap.seqPanel.seqCanvas.getFeatureRenderer().renderOrder;
+      ColourSchemeI cs = av.getGlobalColourScheme();
 
-      Vector settingsAdded = new Vector();
-      Object gstyle=null;
-      GraduatedColor gcol = null; 
-      for (int ro = 0; ro < renderOrder.length; ro++)
+      if (cs != null)
       {
-        gstyle = ap.seqPanel.seqCanvas.getFeatureRenderer().getFeatureStyle(renderOrder[ro]);
-        Setting setting = new Setting();
-        setting.setType(renderOrder[ro]);
-        if (gstyle instanceof GraduatedColor) {
-          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());
-        } else {
-          setting.setColour(ap.seqPanel.seqCanvas.getFeatureRenderer()      .getColour(renderOrder[ro]).getRGB());
+        if (cs.conservationApplied())
+        {
+          view.setConsThreshold(cs.getConservationInc());
+          if (cs instanceof jalview.schemes.UserColourScheme)
+          {
+            view.setBgColour(SetUserColourScheme(cs, userColours, jms));
+          }
         }
-        
-        setting.setDisplay(av.featuresDisplayed
-                .containsKey(renderOrder[ro]));
-        float rorder = ap.seqPanel.seqCanvas.getFeatureRenderer().getOrder(
-                renderOrder[ro]);
-        if (rorder > -1)
+
+        if (cs instanceof ResidueColourScheme)
         {
-          setting.setOrder(rorder);
+          view.setPidThreshold(cs.getThreshold());
         }
-        fs.addSetting(setting);
-        settingsAdded.addElement(renderOrder[ro]);
       }
 
-      // Make sure we save none displayed feature settings
-      Enumeration en = ap.seqPanel.seqCanvas.getFeatureRenderer().featureColours
-              .keys();
-      while (en.hasMoreElements())
+      view.setConservationSelected(av.getConservationSelected());
+      view.setPidSelected(av.getAbovePIDThreshold());
+      view.setFontName(av.font.getName());
+      view.setFontSize(av.font.getSize());
+      view.setFontStyle(av.font.getStyle());
+      view.setRenderGaps(av.renderGaps);
+      view.setShowAnnotation(av.getShowAnnotation());
+      view.setShowBoxes(av.getShowBoxes());
+      view.setShowColourText(av.getColourText());
+      view.setShowFullId(av.getShowJVSuffix());
+      view.setRightAlignIds(av.rightAlignIds);
+      view.setShowSequenceFeatures(av.showSequenceFeatures);
+      view.setShowText(av.getShowText());
+      view.setShowUnconserved(av.getShowUnconserved());
+      view.setWrapAlignment(av.getWrapAlignment());
+      view.setTextCol1(av.textColour.getRGB());
+      view.setTextCol2(av.textColour2.getRGB());
+      view.setTextColThreshold(av.thresholdTextColour);
+      view.setShowConsensusHistogram(av.isShowConsensusHistogram());
+      view.setShowSequenceLogo(av.isShowSequenceLogo());
+      view.setNormaliseSequenceLogo(av.isNormaliseSequenceLogo());
+      view.setShowGroupConsensus(av.isShowGroupConsensus());
+      view.setShowGroupConservation(av.isShowGroupConservation());
+      view.setShowNPfeatureTooltip(av.isShowNpFeats());
+      view.setShowDbRefTooltip(av.isShowDbRefs());
+      view.setFollowHighlight(av.followHighlight);
+      view.setFollowSelection(av.followSelection);
+      view.setIgnoreGapsinConsensus(av.getIgnoreGapsConsensus());
+      if (av.featuresDisplayed != null)
       {
-        String key = en.nextElement().toString();
-        if (settingsAdded.contains(key))
+        jalview.schemabinding.version2.FeatureSettings fs = new jalview.schemabinding.version2.FeatureSettings();
+
+        String[] renderOrder = ap.seqPanel.seqCanvas.getFeatureRenderer().renderOrder;
+
+        Vector settingsAdded = new Vector();
+        Object gstyle = null;
+        GraduatedColor gcol = null;
+        if (renderOrder != null)
         {
-          continue;
-        }
+          for (int ro = 0; ro < renderOrder.length; ro++)
+          {
+            gstyle = ap.seqPanel.seqCanvas.getFeatureRenderer()
+                    .getFeatureStyle(renderOrder[ro]);
+            Setting setting = new Setting();
+            setting.setType(renderOrder[ro]);
+            if (gstyle instanceof GraduatedColor)
+            {
+              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());
+            }
+            else
+            {
+              setting.setColour(ap.seqPanel.seqCanvas.getFeatureRenderer()
+                      .getColour(renderOrder[ro]).getRGB());
+            }
 
-        Setting setting = new Setting();
-        setting.setType(key);
-        setting.setColour(ap.seqPanel.seqCanvas.getFeatureRenderer()
-                .getColour(key).getRGB());
+            setting.setDisplay(av.featuresDisplayed
+                    .containsKey(renderOrder[ro]));
+            float rorder = ap.seqPanel.seqCanvas.getFeatureRenderer()
+                    .getOrder(renderOrder[ro]);
+            if (rorder > -1)
+            {
+              setting.setOrder(rorder);
+            }
+            fs.addSetting(setting);
+            settingsAdded.addElement(renderOrder[ro]);
+          }
+        }
 
-        setting.setDisplay(false);
-        float rorder = ap.seqPanel.seqCanvas.getFeatureRenderer().getOrder(
-                key);
-        if (rorder > -1)
+        // Make sure we save none displayed feature settings
+        Iterator en = ap.seqPanel.seqCanvas.getFeatureRenderer().featureColours
+                .keySet().iterator();
+        while (en.hasNext())
         {
-          setting.setOrder(rorder);
+          String key = en.next().toString();
+          if (settingsAdded.contains(key))
+          {
+            continue;
+          }
+
+          Setting setting = new Setting();
+          setting.setType(key);
+          setting.setColour(ap.seqPanel.seqCanvas.getFeatureRenderer()
+                  .getColour(key).getRGB());
+
+          setting.setDisplay(false);
+          float rorder = ap.seqPanel.seqCanvas.getFeatureRenderer()
+                  .getOrder(key);
+          if (rorder > -1)
+          {
+            setting.setOrder(rorder);
+          }
+          fs.addSetting(setting);
+          settingsAdded.addElement(key);
         }
-        fs.addSetting(setting);
-        settingsAdded.addElement(key);
-      }
-      en = ap.seqPanel.seqCanvas.getFeatureRenderer().featureGroups.keys();
-      Vector groupsAdded = new Vector();
-      while (en.hasMoreElements())
-      {
-        String grp = en.nextElement().toString();
-        if (groupsAdded.contains(grp))
+        en = ap.seqPanel.seqCanvas.getFeatureRenderer().featureGroups
+                .keySet().iterator();
+        Vector groupsAdded = new Vector();
+        while (en.hasNext())
         {
-          continue;
+          String grp = en.next().toString();
+          if (groupsAdded.contains(grp))
+          {
+            continue;
+          }
+          Group g = new Group();
+          g.setName(grp);
+          g.setDisplay(((Boolean) ap.seqPanel.seqCanvas
+                  .getFeatureRenderer().featureGroups.get(grp))
+                  .booleanValue());
+          fs.addGroup(g);
+          groupsAdded.addElement(grp);
         }
-        Group g = new Group();
-        g.setName(grp);
-        g
-                .setDisplay(((Boolean) ap.seqPanel.seqCanvas
-                        .getFeatureRenderer().featureGroups.get(grp))
-                        .booleanValue());
-        fs.addGroup(g);
-        groupsAdded.addElement(grp);
-      }
-      jms.setFeatureSettings(fs);
+        jms.setFeatureSettings(fs);
 
-    }
+      }
 
-    if (av.hasHiddenColumns)
-    {
-      if (av.getColumnSelection() == null
-              || av.getColumnSelection().getHiddenColumns() == null)
+      if (av.hasHiddenColumns())
       {
-        warn("REPORT BUG: avoided null columnselection bug (DMAM reported). Please contact Jim about this.");
+        if (av.getColumnSelection() == null
+                || av.getColumnSelection().getHiddenColumns() == null)
+        {
+          warn("REPORT BUG: avoided null columnselection bug (DMAM reported). Please contact Jim about this.");
+        }
+        else
+        {
+          for (int c = 0; c < av.getColumnSelection().getHiddenColumns()
+                  .size(); c++)
+          {
+            int[] region = (int[]) av.getColumnSelection()
+                    .getHiddenColumns().elementAt(c);
+            HiddenColumns hc = new HiddenColumns();
+            hc.setStart(region[0]);
+            hc.setEnd(region[1]);
+            view.addHiddenColumns(hc);
+          }
+        }
       }
-      else
+      if (calcIdSet.size() > 0)
       {
-        for (int c = 0; c < av.getColumnSelection().getHiddenColumns()
-                .size(); c++)
+        for (String calcId : calcIdSet)
         {
-          int[] region = (int[]) av.getColumnSelection().getHiddenColumns()
-                  .elementAt(c);
-          HiddenColumns hc = new HiddenColumns();
-          hc.setStart(region[0]);
-          hc.setEnd(region[1]);
-          view.addHiddenColumns(hc);
+          if (calcId.trim().length() > 0)
+          {
+            CalcIdParam cidp = createCalcIdParam(calcId, av);
+            // Some calcIds have no parameters.
+            if (cidp != null)
+            {
+              view.addCalcIdParam(cidp);
+            }
+          }
         }
       }
-    }
-
-    jms.addViewport(view);
 
+      jms.addViewport(view);
+    }
     object.setJalviewModelSequence(jms);
     object.getVamsasModel().addSequenceSet(vamsasSet);
 
@@ -1208,51 +1308,327 @@ public class Jalview2XML
     return object;
   }
 
-  /**
-   * External mapping between jalview objects and objects yielding a valid and
-   * unique object ID string. This is null for normal Jalview project IO, but
-   * non-null when a jalview project is being read or written as part of a
-   * vamsas session.
-   */
-  IdentityHashMap jv2vobj = null;
+  private AnnotationColours constructAnnotationColours(
+          AnnotationColourGradient acg, Vector userColours,
+          JalviewModelSequence jms)
+  {
+    AnnotationColours ac = new AnnotationColours();
+    ac.setAboveThreshold(acg.getAboveThreshold());
+    ac.setThreshold(acg.getAnnotationThreshold());
+    ac.setAnnotation(acg.getAnnotation());
+    if (acg.getBaseColour() instanceof jalview.schemes.UserColourScheme)
+    {
+      ac.setColourScheme(SetUserColourScheme(acg.getBaseColour(),
+              userColours, jms));
+    }
+    else
+    {
+      ac.setColourScheme(ColourSchemeProperty.getColourName(acg
+              .getBaseColour()));
+    }
 
-  /**
-   * Construct a unique ID for jvobj using either existing bindings or if none
-   * exist, the result of the hashcode call for the object.
-   * 
-   * @param jvobj
-   *          jalview data object
-   * @return unique ID for referring to jvobj
-   */
-  private String makeHashCode(Object jvobj, String altCode)
+    ac.setMaxColour(acg.getMaxColour().getRGB());
+    ac.setMinColour(acg.getMinColour().getRGB());
+    ac.setPerSequence(acg.isSeqAssociated());
+    ac.setPredefinedColours(acg.isPredefinedColours());
+    return ac;
+  }
+
+  private void storeAlignmentAnnotation(AlignmentAnnotation[] aa,
+          IdentityHashMap groupRefs, AlignmentViewport av,
+          Set<String> calcIdSet, boolean storeDS, SequenceSet vamsasSet)
   {
-    if (jv2vobj != null)
+
+    for (int i = 0; i < aa.length; i++)
     {
-      Object id = jv2vobj.get(jvobj);
-      if (id != null)
+      Annotation an = new Annotation();
+
+      if (aa[i].annotationId != null)
       {
-        return id.toString();
+        annotationIds.put(aa[i].annotationId, aa[i]);
       }
-      // check string ID mappings
-      if (jvids2vobj != null && jvobj instanceof String)
+
+      an.setId(aa[i].annotationId);
+
+      an.setVisible(aa[i].visible);
+
+      an.setDescription(aa[i].description);
+
+      if (aa[i].sequenceRef != null)
       {
-        id = jvids2vobj.get(jvobj);
+        // TODO later annotation sequenceRef should be the XML ID of the
+        // sequence rather than its display name
+        an.setSequenceRef(aa[i].sequenceRef.getName());
       }
-      if (id != null)
+      if (aa[i].groupRef != null)
       {
-        return id.toString();
+        Object groupIdr = groupRefs.get(aa[i].groupRef);
+        if (groupIdr == null)
+        {
+          // make a locally unique String
+          groupRefs.put(aa[i].groupRef,
+                  groupIdr = ("" + System.currentTimeMillis()
+                          + aa[i].groupRef.getName() + groupRefs.size()));
+        }
+        an.setGroupRef(groupIdr.toString());
       }
-      // give up and warn that something has gone wrong
-      warn("Cannot find ID for object in external mapping : " + jvobj);
-    }
-    return altCode;
-  }
 
-  /**
-   * return local jalview object mapped to ID, if it exists
-   * 
-   * @param idcode
-   *          (may be null)
+      // store all visualization attributes for annotation
+      an.setGraphHeight(aa[i].graphHeight);
+      an.setCentreColLabels(aa[i].centreColLabels);
+      an.setScaleColLabels(aa[i].scaleColLabel);
+      an.setShowAllColLabels(aa[i].showAllColLabels);
+      an.setBelowAlignment(aa[i].belowAlignment);
+
+      if (aa[i].graph > 0)
+      {
+        an.setGraph(true);
+        an.setGraphType(aa[i].graph);
+        an.setGraphGroup(aa[i].graphGroup);
+        if (aa[i].getThreshold() != null)
+        {
+          ThresholdLine line = new ThresholdLine();
+          line.setLabel(aa[i].getThreshold().label);
+          line.setValue(aa[i].getThreshold().value);
+          line.setColour(aa[i].getThreshold().colour.getRGB());
+          an.setThresholdLine(line);
+        }
+      }
+      else
+      {
+        an.setGraph(false);
+      }
+
+      an.setLabel(aa[i].label);
+
+      if (aa[i] == av.getAlignmentQualityAnnot()
+              || aa[i] == av.getAlignmentConservationAnnotation()
+              || aa[i] == av.getAlignmentConsensusAnnotation()
+              || aa[i].autoCalculated)
+      {
+        // new way of indicating autocalculated annotation -
+        an.setAutoCalculated(aa[i].autoCalculated);
+      }
+      if (aa[i].hasScore())
+      {
+        an.setScore(aa[i].getScore());
+      }
+
+      if (aa[i].getCalcId() != null)
+      {
+        calcIdSet.add(aa[i].getCalcId());
+        an.setCalcId(aa[i].getCalcId());
+      }
+      if (aa[i].hasProperties())
+      {
+        for (String pr : aa[i].getProperties())
+        {
+          Property prop = new Property();
+          prop.setName(pr);
+          prop.setValue(aa[i].getProperty(pr));
+          an.addProperty(prop);
+        }
+      }
+      AnnotationElement ae;
+      if (aa[i].annotations != null)
+      {
+        an.setScoreOnly(false);
+        for (int a = 0; a < aa[i].annotations.length; a++)
+        {
+          if ((aa[i] == null) || (aa[i].annotations[a] == null))
+          {
+            continue;
+          }
+
+          ae = new AnnotationElement();
+          if (aa[i].annotations[a].description != null)
+          {
+            ae.setDescription(aa[i].annotations[a].description);
+          }
+          if (aa[i].annotations[a].displayCharacter != null)
+          {
+            ae.setDisplayCharacter(aa[i].annotations[a].displayCharacter);
+          }
+
+          if (!Float.isNaN(aa[i].annotations[a].value))
+          {
+            ae.setValue(aa[i].annotations[a].value);
+          }
+
+          ae.setPosition(a);
+          if (aa[i].annotations[a].secondaryStructure > ' ')
+          {
+            ae.setSecondaryStructure(aa[i].annotations[a].secondaryStructure
+                    + "");
+          }
+
+          if (aa[i].annotations[a].colour != null
+                  && aa[i].annotations[a].colour != java.awt.Color.black)
+          {
+            ae.setColour(aa[i].annotations[a].colour.getRGB());
+          }
+
+          an.addAnnotationElement(ae);
+          if (aa[i].autoCalculated)
+          {
+            // only write one non-null entry into the annotation row -
+            // sufficient to get the visualization attributes necessary to
+            // display data
+            continue;
+          }
+        }
+      }
+      else
+      {
+        an.setScoreOnly(true);
+      }
+      if (!storeDS || (storeDS && !aa[i].autoCalculated))
+      {
+        // skip autocalculated annotation - these are only provided for
+        // alignments
+        vamsasSet.addAnnotation(an);
+      }
+    }
+
+  }
+
+  private CalcIdParam createCalcIdParam(String calcId, AlignViewport av)
+  {
+    AutoCalcSetting settings = av.getCalcIdSettingsFor(calcId);
+    if (settings != null)
+    {
+      CalcIdParam vCalcIdParam = new CalcIdParam();
+      vCalcIdParam.setCalcId(calcId);
+      vCalcIdParam.addServiceURL(settings.getServiceURI());
+      // generic URI allowing a third party to resolve another instance of the
+      // service used for this calculation
+      for (String urls : settings.getServiceURLs())
+      {
+        vCalcIdParam.addServiceURL(urls);
+      }
+      vCalcIdParam.setVersion("1.0");
+      if (settings.getPreset() != null)
+      {
+        WsParamSetI setting = settings.getPreset();
+        vCalcIdParam.setName(setting.getName());
+        vCalcIdParam.setDescription(setting.getDescription());
+      }
+      else
+      {
+        vCalcIdParam.setName("");
+        vCalcIdParam.setDescription("Last used parameters");
+      }
+      // need to be able to recover 1) settings 2) user-defined presets or
+      // recreate settings from preset 3) predefined settings provided by
+      // service - or settings that can be transferred (or discarded)
+      vCalcIdParam.setParameters(settings.getWsParamFile().replace("\n",
+              "|\\n|"));
+      vCalcIdParam.setAutoUpdate(settings.isAutoUpdate());
+      // todo - decide if updateImmediately is needed for any projects.
+
+      return vCalcIdParam;
+    }
+    return null;
+  }
+
+  private boolean recoverCalcIdParam(CalcIdParam calcIdParam,
+          AlignViewport av)
+  {
+    if (calcIdParam.getVersion().equals("1.0"))
+    {
+      Jws2Instance service = Jws2Discoverer.getDiscoverer()
+              .getPreferredServiceFor(calcIdParam.getServiceURL());
+      if (service != null)
+      {
+        WsParamSetI parmSet = null;
+        try
+        {
+          parmSet = service.getParamStore().parseServiceParameterFile(
+                  calcIdParam.getName(), calcIdParam.getDescription(),
+                  calcIdParam.getServiceURL(),
+                  calcIdParam.getParameters().replace("|\\n|", "\n"));
+        } catch (IOException x)
+        {
+          warn("Couldn't parse parameter data for "
+                  + calcIdParam.getCalcId(), x);
+          return false;
+        }
+        List<ArgumentI> argList = null;
+        if (calcIdParam.getName().length() > 0)
+        {
+          parmSet = service.getParamStore()
+                  .getPreset(calcIdParam.getName());
+          if (parmSet != null)
+          {
+            // TODO : check we have a good match with settings in AACon -
+            // otherwise we'll need to create a new preset
+          }
+        }
+        else
+        {
+          argList = parmSet.getArguments();
+          parmSet = null;
+        }
+        AAConSettings settings = new AAConSettings(
+                calcIdParam.isAutoUpdate(), service, parmSet, argList);
+        av.setCalcIdSettingsFor(calcIdParam.getCalcId(), settings,
+                calcIdParam.isNeedsUpdate());
+        return true;
+      }
+      else
+      {
+        warn("Cannot resolve a service for the parameters used in this project. Try configuring a JABAWS server.");
+        return false;
+      }
+    }
+    throw new Error(MessageManager.formatMessage("error.unsupported_version_calcIdparam", new String[]{calcIdParam.toString()}));
+  }
+
+  /**
+   * External mapping between jalview objects and objects yielding a valid and
+   * unique object ID string. This is null for normal Jalview project IO, but
+   * non-null when a jalview project is being read or written as part of a
+   * vamsas session.
+   */
+  IdentityHashMap jv2vobj = null;
+
+  /**
+   * Construct a unique ID for jvobj using either existing bindings or if none
+   * exist, the result of the hashcode call for the object.
+   * 
+   * @param jvobj
+   *          jalview data object
+   * @return unique ID for referring to jvobj
+   */
+  private String makeHashCode(Object jvobj, String altCode)
+  {
+    if (jv2vobj != null)
+    {
+      Object id = jv2vobj.get(jvobj);
+      if (id != null)
+      {
+        return id.toString();
+      }
+      // check string ID mappings
+      if (jvids2vobj != null && jvobj instanceof String)
+      {
+        id = jvids2vobj.get(jvobj);
+      }
+      if (id != null)
+      {
+        return id.toString();
+      }
+      // give up and warn that something has gone wrong
+      warn("Cannot find ID for object in external mapping : " + jvobj);
+    }
+    return altCode;
+  }
+
+  /**
+   * return local jalview object mapped to ID, if it exists
+   * 
+   * @param idcode
+   *          (may be null)
    * @return null or object bound to idcode
    */
   private Object retrieveExistingObj(String idcode)
@@ -1391,14 +1767,15 @@ public class Jalview2XML
   {
     String id = null;
     jalview.schemes.UserColourScheme ucs = (jalview.schemes.UserColourScheme) cs;
-    boolean newucs=false;
+    boolean newucs = false;
     if (!userColours.contains(ucs))
     {
       userColours.add(ucs);
-      newucs=true;
+      newucs = true;
     }
     id = "ucs" + userColours.indexOf(ucs);
-    if (newucs) {
+    if (newucs)
+    {
       // actually create the scheme's entry in the XML model
       java.awt.Color[] colours = ucs.getColours();
       jalview.schemabinding.version2.UserColours uc = new jalview.schemabinding.version2.UserColours();
@@ -1496,6 +1873,8 @@ public class Jalview2XML
 
     try
     {
+      // create list to store references for any new Jmol viewers created
+      newStructureViewers = new Vector<JalviewStructureDisplayI>();
       // UNMARSHALLER SEEMS TO CLOSE JARINPUTSTREAM, MOST ANNOYING
       // Workaround is to make sure caller implements the JarInputStreamProvider
       // interface
@@ -1503,10 +1882,26 @@ public class Jalview2XML
 
       jarInputStreamProvider jprovider = createjarInputStreamProvider(file);
       af = LoadJalviewAlign(jprovider);
+
     } catch (MalformedURLException e)
     {
       errorMessage = "Invalid URL format for '" + file + "'";
       reportErrors();
+    } finally
+    {
+      try
+      {
+        SwingUtilities.invokeAndWait(new Runnable()
+        {
+          public void run()
+          {
+            setLoadingFinishedForNewStructureViewers();
+          };
+        });
+      } catch (Exception x)
+      {
+
+      }
     }
     return af;
   }
@@ -1529,6 +1924,7 @@ public class Jalview2XML
     return new jarInputStreamProvider()
     {
 
+      @Override
       public JarInputStream getJarInputStream() throws IOException
       {
         if (_url != null)
@@ -1541,6 +1937,7 @@ public class Jalview2XML
         }
       }
 
+      @Override
       public String getFilename()
       {
         return file;
@@ -1577,7 +1974,7 @@ public class Jalview2XML
       frefedSequence = new Vector();
     }
 
-    jalview.gui.AlignFrame af = null;
+    jalview.gui.AlignFrame af = null, _af = null;
     Hashtable gatherToThisFrame = new Hashtable();
     final String file = jprovider.getFilename();
     try
@@ -1604,10 +2001,14 @@ public class Jalview2XML
           object = (JalviewModel) unmar.unmarshal(in);
           if (true) // !skipViewport(object))
           {
-            af = LoadFromObject(object, file, true, jprovider);
-            if (af.viewport.gatherViewsHere)
+            _af = LoadFromObject(object, file, true, jprovider);
+            if (object.getJalviewModelSequence().getViewportCount() > 0)
             {
-              gatherToThisFrame.put(af.viewport.getSequenceSetId(), af);
+              af = _af;
+              if (af.viewport.gatherViewsHere)
+              {
+                gatherToThisFrame.put(af.viewport.getSequenceSetId(), af);
+              }
             }
           }
           entryCount++;
@@ -1705,6 +2106,7 @@ public class Jalview2XML
       {
         javax.swing.SwingUtilities.invokeLater(new Runnable()
         {
+          @Override
           public void run()
           {
             JOptionPane.showInternalMessageDialog(Desktop.desktop,
@@ -1722,22 +2124,26 @@ public class Jalview2XML
     errorMessage = null;
   }
 
-  Hashtable alreadyLoadedPDB;
+  Hashtable<String, String> alreadyLoadedPDB;
 
   /**
    * when set, local views will be updated from view stored in JalviewXML
    * Currently (28th Sep 2008) things will go horribly wrong in vamsas document
    * sync if this is set to true.
    */
-  private boolean updateLocalViews = false;
+  private final boolean updateLocalViews = false;
 
   String loadPDBFile(jarInputStreamProvider jprovider, String pdbId)
   {
     if (alreadyLoadedPDB == null)
+    {
       alreadyLoadedPDB = new Hashtable();
+    }
 
     if (alreadyLoadedPDB.containsKey(pdbId))
+    {
       return alreadyLoadedPDB.get(pdbId).toString();
+    }
 
     try
     {
@@ -1773,9 +2179,9 @@ public class Jalview2XML
         }
         ;
         out.close();
-
-        alreadyLoadedPDB.put(pdbId, outFile.getAbsolutePath());
-        return outFile.getAbsolutePath();
+        String t = outFile.getAbsolutePath();
+        alreadyLoadedPDB.put(pdbId, t);
+        return t;
       }
       else
       {
@@ -1789,6 +2195,25 @@ public class Jalview2XML
     return null;
   }
 
+  private class JvAnnotRow
+  {
+    public JvAnnotRow(int i, AlignmentAnnotation jaa)
+    {
+      order = i;
+      template = jaa;
+    }
+
+    /**
+     * persisted version of annotation row from which to take vis properties
+     */
+    public jalview.datamodel.AlignmentAnnotation template;
+
+    /**
+     * original position of the annotation row in the alignment
+     */
+    public int order;
+  }
+
   /**
    * Load alignment frame from jalview XML DOM object
    * 
@@ -1810,7 +2235,9 @@ public class Jalview2XML
 
     JalviewModelSequence jms = object.getJalviewModelSequence();
 
-    Viewport view = jms.getViewport(0);
+    Viewport view = (jms.getViewportCount() > 0) ? jms.getViewport(0)
+            : null;
+
     // ////////////////////////////////
     // LOAD SEQUENCES
 
@@ -1829,7 +2256,7 @@ public class Jalview2XML
 
       if (seqRefIds.get(seqId) != null)
       {
-        tmpseqs.add((jalview.datamodel.Sequence) seqRefIds.get(seqId));
+        tmpseqs.add(seqRefIds.get(seqId));
         multipleView = true;
       }
       else
@@ -1852,8 +2279,7 @@ public class Jalview2XML
           hiddenSeqs = new Vector();
         }
 
-        hiddenSeqs.addElement((jalview.datamodel.Sequence) seqRefIds
-                .get(seqId));
+        hiddenSeqs.addElement(seqRefIds.get(seqId));
       }
 
     }
@@ -1887,7 +2313,10 @@ public class Jalview2XML
     }
     else
     {
-      recoverDatasetFor(vamsasSet, al);
+      // 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);
     }
     // ///////////////////////////////
 
@@ -1949,7 +2378,9 @@ public class Jalview2XML
                 entry.setFile(pdbloaded.get(ids[p].getId()).toString());
               }
             }
-
+            StructureSelectionManager.getStructureSelectionManager(
+                    Desktop.instance)
+                    .registerPDBEntry(entry);
             al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
           }
         }
@@ -2021,11 +2452,11 @@ public class Jalview2XML
 
     // ////////////////////////////////
     // LOAD ANNOTATIONS
-    boolean hideQuality = true, hideConservation = true, hideConsensus = true;
+    ArrayList<JvAnnotRow> autoAlan = new ArrayList<JvAnnotRow>();
     /**
      * store any annotations which forward reference a group's ID
      */
-    Hashtable groupAnnotRefs = new Hashtable();
+    Hashtable<String, ArrayList<jalview.datamodel.AlignmentAnnotation>> groupAnnotRefs = new Hashtable<String, ArrayList<jalview.datamodel.AlignmentAnnotation>>();
 
     if (vamsasSet.getAnnotationCount() > 0)
     {
@@ -2033,22 +2464,29 @@ public class Jalview2XML
 
       for (int i = 0; i < an.length; i++)
       {
-        // set visibility for automatic annotation for this view
-        if (an[i].getLabel().equals("Quality"))
+        /**
+         * test if annotation is automatically calculated for this view only
+         */
+        boolean autoForView = false;
+        if (an[i].getLabel().equals("Quality")
+                || an[i].getLabel().equals("Conservation")
+                || an[i].getLabel().equals("Consensus"))
         {
-          hideQuality = false;
-          continue;
+          // Kludge for pre 2.5 projects which lacked the autocalculated flag
+          autoForView = true;
+          if (!an[i].hasAutoCalculated())
+          {
+            an[i].setAutoCalculated(true);
+          }
         }
-        else if (an[i].getLabel().equals("Conservation"))
+        if (autoForView
+                || (an[i].hasAutoCalculated() && an[i].isAutoCalculated()))
         {
-          hideConservation = false;
-          continue;
+          // remove ID - we don't recover annotation from other views for
+          // view-specific annotation
+          an[i].setId(null);
         }
-        else if (an[i].getLabel().equals("Consensus"))
-        {
-          hideConsensus = false;
-          continue;
-        }        
+
         // set visiblity for other annotation in this view
         if (an[i].getId() != null
                 && annotationIds.containsKey(an[i].getId()))
@@ -2058,7 +2496,9 @@ public class Jalview2XML
           // in principle Visible should always be true for annotation displayed
           // in multiple views
           if (an[i].hasVisible())
+          {
             jda.visible = an[i].getVisible();
+          }
 
           al.addAnnotation(jda);
 
@@ -2067,22 +2507,27 @@ public class Jalview2XML
         // Construct new annotation from model.
         AnnotationElement[] ae = an[i].getAnnotationElement();
         jalview.datamodel.Annotation[] anot = null;
-
+        java.awt.Color firstColour = null;
+        int anpos;
         if (!an[i].getScoreOnly())
         {
           anot = new jalview.datamodel.Annotation[al.getWidth()];
-
           for (int aa = 0; aa < ae.length && aa < anot.length; aa++)
           {
-            if (ae[aa].getPosition() >= anot.length)
+            anpos = ae[aa].getPosition();
+
+            if (anpos >= anot.length)
+            {
               continue;
+            }
 
-            anot[ae[aa].getPosition()] = new jalview.datamodel.Annotation(
+            anot[anpos] = new jalview.datamodel.Annotation(
 
-            ae[aa].getDisplayCharacter(), ae[aa].getDescription(), (ae[aa]
-                    .getSecondaryStructure() == null || ae[aa]
-                    .getSecondaryStructure().length() == 0) ? ' ' : ae[aa]
-                    .getSecondaryStructure().charAt(0), ae[aa].getValue()
+            ae[aa].getDisplayCharacter(), ae[aa].getDescription(),
+                    (ae[aa].getSecondaryStructure() == null || ae[aa]
+                            .getSecondaryStructure().length() == 0) ? ' '
+                            : ae[aa].getSecondaryStructure().charAt(0),
+                    ae[aa].getValue()
 
             );
             // JBPNote: Consider verifying dataflow for IO of secondary
@@ -2092,19 +2537,27 @@ public class Jalview2XML
             // {
             // anot[ae[aa].getPosition()].displayCharacter = "";
             // }
-            anot[ae[aa].getPosition()].colour = new java.awt.Color(ae[aa]
-                    .getColour());
+            anot[anpos].colour = new java.awt.Color(ae[aa].getColour());
+            if (firstColour == null)
+            {
+              firstColour = anot[anpos].colour;
+            }
           }
         }
         jalview.datamodel.AlignmentAnnotation jaa = null;
 
         if (an[i].getGraph())
         {
+          float llim = 0, hlim = 0;
+          // if (autoForView || an[i].isAutoCalculated()) {
+          // hlim=11f;
+          // }
           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
-                  an[i].getDescription(), anot, 0, 0, an[i].getGraphType());
+                  an[i].getDescription(), anot, llim, hlim,
+                  an[i].getGraphType());
 
           jaa.graphGroup = an[i].getGraphGroup();
-
+          jaa._linecolour = firstColour;
           if (an[i].getThresholdLine() != null)
           {
             jaa.setThreshold(new jalview.datamodel.GraphLine(an[i]
@@ -2113,12 +2566,18 @@ public class Jalview2XML
                     an[i].getThresholdLine().getColour())));
 
           }
-
+          if (autoForView || an[i].isAutoCalculated())
+          {
+            // Hardwire the symbol display line to ensure that labels for
+            // histograms are displayed
+            jaa.hasText = true;
+          }
         }
         else
         {
           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
                   an[i].getDescription(), anot);
+          jaa._linecolour = firstColour;
         }
         // register new annotation
         if (an[i].getId() != null)
@@ -2137,9 +2596,16 @@ public class Jalview2XML
           }
         }
         // and make a note of any group association
-        if (an[i].getGroupRef()!=null && an[i].getGroupRef().length()>0)
+        if (an[i].getGroupRef() != null && an[i].getGroupRef().length() > 0)
         {
-          groupAnnotRefs.put(an[i].getGroupRef(),jaa);
+          ArrayList<jalview.datamodel.AlignmentAnnotation> aal = groupAnnotRefs
+                  .get(an[i].getGroupRef());
+          if (aal == null)
+          {
+            aal = new ArrayList<jalview.datamodel.AlignmentAnnotation>();
+            groupAnnotRefs.put(an[i].getGroupRef(), aal);
+          }
+          aal.add(jaa);
         }
 
         if (an[i].hasScore())
@@ -2147,31 +2613,63 @@ public class Jalview2XML
           jaa.setScore(an[i].getScore());
         }
         if (an[i].hasVisible())
+        {
           jaa.visible = an[i].getVisible();
-        
+        }
+
         if (an[i].hasCentreColLabels())
+        {
           jaa.centreColLabels = an[i].getCentreColLabels();
-        
+        }
+
         if (an[i].hasScaleColLabels())
         {
           jaa.scaleColLabel = an[i].getScaleColLabels();
         }
         if (an[i].hasAutoCalculated() && an[i].isAutoCalculated())
         {
-          // newer files have an 'autoCalculated' flag and store calculation state in viewport properties
-          jaa.autoCalculated = true; // means annotation will be marked for update at end of load. 
+          // newer files have an 'autoCalculated' flag and store calculation
+          // state in viewport properties
+          jaa.autoCalculated = true; // means annotation will be marked for
+          // update at end of load.
+        }
+        if (an[i].hasGraphHeight())
+        {
+          jaa.graphHeight = an[i].getGraphHeight();
+        }
+        if (an[i].hasBelowAlignment())
+        {
+          jaa.belowAlignment = an[i].isBelowAlignment();
+        }
+        jaa.setCalcId(an[i].getCalcId());
+        if (an[i].getPropertyCount() > 0)
+        {
+          for (jalview.schemabinding.version2.Property prop : an[i]
+                  .getProperty())
+          {
+            jaa.setProperty(prop.getName(), prop.getValue());
+          }
+        }
+        if (jaa.autoCalculated)
+        {
+          autoAlan.add(new JvAnnotRow(i, jaa));
+        }
+        else
+        // if (!autoForView)
+        {
+          // add autocalculated group annotation and any user created annotation
+          // for the view
+          al.addAnnotation(jaa);
         }
-        al.addAnnotation(jaa);
       }
     }
-
     // ///////////////////////
     // LOAD GROUPS
     // Create alignment markup and styles for this view
     if (jms.getJGroupCount() > 0)
     {
       JGroup[] groups = jms.getJGroup();
-
+      boolean addAnnotSchemeGroup = false;
       for (int i = 0; i < groups.length; i++)
       {
         ColourSchemeI cs = null;
@@ -2182,6 +2680,12 @@ public class Jalview2XML
           {
             cs = GetUserColourScheme(jms, groups[i].getColour());
           }
+          else if (groups[i].getColour().equals("AnnotationColourGradient")
+                  && groups[i].getAnnotationColours() != null)
+          {
+            addAnnotSchemeGroup = true;
+            cs = null;
+          }
           else
           {
             cs = ColourSchemeProperty.getColour(al, groups[i].getColour());
@@ -2217,20 +2721,25 @@ public class Jalview2XML
                 groups[i].getDisplayText(), groups[i].getColourText(),
                 groups[i].getStart(), groups[i].getEnd());
 
-        sg
-                .setOutlineColour(new java.awt.Color(groups[i]
-                        .getOutlineColour()));
+        sg.setOutlineColour(new java.awt.Color(groups[i].getOutlineColour()));
 
         sg.textColour = new java.awt.Color(groups[i].getTextCol1());
         sg.textColour2 = new java.awt.Color(groups[i].getTextCol2());
-        sg.setShowunconserved(groups[i].hasShowUnconserved() ? groups[i]
+        sg.setShowNonconserved(groups[i].hasShowUnconserved() ? groups[i]
                 .isShowUnconserved() : false);
         sg.thresholdTextColour = groups[i].getTextColThreshold();
-        if (groups[i].hasShowConsensusHistogram()) {
+        if (groups[i].hasShowConsensusHistogram())
+        {
           sg.setShowConsensusHistogram(groups[i].isShowConsensusHistogram());
-        };
-        if (groups[i].hasShowConsensusProfile()) {
-          sg.setIncludeAllConsSymbols(groups[i].isShowConsensusProfile());
+        }
+        ;
+        if (groups[i].hasShowSequenceLogo())
+        {
+          sg.setshowSequenceLogo(groups[i].isShowSequenceLogo());
+        }
+        if (groups[i].hasNormaliseSequenceLogo())
+        {
+          sg.setNormaliseSequenceLogo(groups[i].isNormaliseSequenceLogo());
         }
         if (groups[i].hasIgnoreGapsinConsensus())
         {
@@ -2239,28 +2748,55 @@ public class Jalview2XML
         if (groups[i].getConsThreshold() != 0)
         {
           jalview.analysis.Conservation c = new jalview.analysis.Conservation(
-                  "All", ResidueProperties.propHash, 3, sg
-                          .getSequences(null), 0, sg.getWidth() - 1);
+                  "All", ResidueProperties.propHash, 3,
+                  sg.getSequences(null), 0, sg.getWidth() - 1);
           c.calculate();
           c.verdict(false, 25);
           sg.cs.setConservation(c);
         }
-        
-        if (groups[i].getId()!=null
-                && groupAnnotRefs.size()>0)
+
+        if (groups[i].getId() != null && groupAnnotRefs.size() > 0)
         {
           // re-instate unique group/annotation row reference
-          jalview.datamodel.AlignmentAnnotation jaa = (jalview.datamodel.AlignmentAnnotation) groupAnnotRefs.get(groups[i].getId());
-          if (jaa!=null)
+          ArrayList<jalview.datamodel.AlignmentAnnotation> jaal = groupAnnotRefs
+                  .get(groups[i].getId());
+          if (jaal != null)
           {
-            jaa.groupRef = sg;
+            for (jalview.datamodel.AlignmentAnnotation jaa : jaal)
+            {
+              jaa.groupRef = sg;
+              if (jaa.autoCalculated)
+              {
+                // match up and try to set group autocalc alignment row for this
+                // annotation
+                if (jaa.label.startsWith("Consensus for "))
+                {
+                  sg.setConsensus(jaa);
+                }
+                // match up and try to set group autocalc alignment row for this
+                // annotation
+                if (jaa.label.startsWith("Conservation for "))
+                {
+                  sg.setConservationRow(jaa);
+                }
+              }
+            }
           }
         }
         al.addGroup(sg);
-        
+        if (addAnnotSchemeGroup)
+        {
+          // reconstruct the annotation colourscheme
+          sg.cs = constructAnnotationColour(
+                  groups[i].getAnnotationColours(), null, al, jms, false);
+        }
       }
     }
-
+    if (view == null)
+    {
+      // only dataset in this model, so just return.
+      return null;
+    }
     // ///////////////////////////////
     // LOAD VIEWPORT
 
@@ -2304,6 +2840,13 @@ 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());
+
     AlignmentPanel ap = null;
     boolean isnewview = true;
     if (viewId != null)
@@ -2331,9 +2874,8 @@ public class Jalview2XML
 
     if (isnewview)
     {
-      af = loadViewport(file, JSEQ, hiddenSeqs, al, hideConsensus,
-              hideQuality, hideConservation, jms, view, uniqueSeqSetId,
-              viewId);
+      af = loadViewport(file, JSEQ, hiddenSeqs, al, jms, view,
+              uniqueSeqSetId, viewId, autoAlan);
       av = af.viewport;
       ap = af.alignPanel;
     }
@@ -2351,9 +2893,10 @@ public class Jalview2XML
           TreePanel tp = (TreePanel) retrieveExistingObj(tree.getId());
           if (tp == null)
           {
-            tp = af.ShowNewickTree(new jalview.io.NewickFile(tree
-                    .getNewick()), tree.getTitle(), tree.getWidth(), tree
-                    .getHeight(), tree.getXpos(), tree.getYpos());
+            tp = af.ShowNewickTree(
+                    new jalview.io.NewickFile(tree.getNewick()),
+                    tree.getTitle(), tree.getWidth(), tree.getHeight(),
+                    tree.getXpos(), tree.getYpos());
             if (tree.getId() != null)
             {
               // perhaps bind the tree id to something ?
@@ -2416,6 +2959,10 @@ public class Jalview2XML
     // //LOAD STRUCTURES
     if (loadTreesAndStructures)
     {
+      // run through all PDB ids on the alignment, and collect mappings between
+      // jmol view ids and all sequences referring to it
+      Hashtable<String, Object[]> jmolViewIds = new Hashtable();
+
       for (int i = 0; i < JSEQ.length; i++)
       {
         if (JSEQ[i].getPdbidsCount() > 0)
@@ -2440,118 +2987,416 @@ public class Jalview2XML
               int y = ids[p].getStructureState(s).getYpos();
               int width = ids[p].getStructureState(s).getWidth();
               int height = ids[p].getStructureState(s).getHeight();
-              AppJmol comp = null;
-              JInternalFrame[] frames = null;
-              do
+
+              // 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());
+              jalview.datamodel.SequenceI seq = (jalview.datamodel.SequenceI) seqRefIds
+                      .get(JSEQ[i].getId() + "");
+              if (sviewid == null)
+              {
+                sviewid = "_jalview_pre2_4_" + x + "," + y + "," + width
+                        + "," + height;
+              }
+              if (!jmolViewIds.containsKey(sviewid))
+              {
+                jmolViewIds.put(sviewid, new Object[]
+                { new int[]
+                { x, y, width, height }, "",
+                    new Hashtable<String, Object[]>(), new boolean[]
+                    { false, false, true } });
+                // Legacy pre-2.7 conversion JAL-823 :
+                // do not assume any view has to be linked for colour by
+                // sequence
+              }
+
+              // assemble String[] { pdb files }, String[] { id for each
+              // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
+              // seqs_file 2}, boolean[] {
+              // linkAlignPanel,superposeWithAlignpanel}} from hash
+              Object[] jmoldat = jmolViewIds.get(sviewid);
+              ((boolean[]) jmoldat[3])[0] |= ids[p].getStructureState(s)
+                      .hasAlignwithAlignPanel() ? ids[p].getStructureState(
+                      s).getAlignwithAlignPanel() : false;
+              // never colour by linked panel if not specified
+              ((boolean[]) jmoldat[3])[1] |= ids[p].getStructureState(s)
+                      .hasColourwithAlignPanel() ? ids[p]
+                      .getStructureState(s).getColourwithAlignPanel()
+                      : false;
+              // default for pre-2.7 projects is that Jmol colouring is enabled
+              ((boolean[]) jmoldat[3])[2] &= ids[p].getStructureState(s)
+                      .hasColourByJmol() ? ids[p].getStructureState(s)
+                      .getColourByJmol() : true;
+
+              if (((String) jmoldat[1]).length() < ids[p]
+                      .getStructureState(s).getContent().length())
               {
-                try
-                {
-                  frames = Desktop.desktop.getAllFrames();
-                } catch (ArrayIndexOutOfBoundsException e)
                 {
-                  // occasional No such child exceptions are thrown here...
-                  frames = null;
-                  try
-                  {
-                    Thread.sleep(10);
-                  } catch (Exception f)
-                  {
-                  }
-                  ;
+                  jmoldat[1] = ids[p].getStructureState(s).getContent();
                 }
-              } while (frames == null);
-              // search for any Jmol windows already open from other
-              // alignment views that exactly match the stored structure state
-              for (int f = 0; comp == null && f < frames.length; f++)
+              }
+              if (ids[p].getFile() != null)
               {
-                if (frames[f] instanceof AppJmol)
+                File mapkey = new File(ids[p].getFile());
+                Object[] seqstrmaps = (Object[]) ((Hashtable) jmoldat[2])
+                        .get(mapkey);
+                if (seqstrmaps == null)
                 {
-                  if (sviewid != null
-                          && ((AppJmol) frames[f]).getViewId().equals(
-                                  sviewid))
-                  {
-                    // post jalview 2.4 schema includes structure view id
-                    comp = (AppJmol) frames[f];
-                  }
-                  else if (frames[f].getX() == x && frames[f].getY() == y
-                          && frames[f].getHeight() == height
-                          && frames[f].getWidth() == width)
-                  {
-                    comp = (AppJmol) frames[f];
-                  }
+                  ((Hashtable) jmoldat[2]).put(mapkey,
+                          seqstrmaps = new Object[]
+                          { pdbFile, ids[p].getId(), new Vector(),
+                              new Vector() });
+                }
+                if (!((Vector) seqstrmaps[2]).contains(seq))
+                {
+                  ((Vector) seqstrmaps[2]).addElement(seq);
+                  // ((Vector)seqstrmaps[3]).addElement(n) :
+                  // in principle, chains
+                  // should be stored here : do we need to
+                  // TODO: store and recover seq/pdb_id :
+                  // chain mappings
                 }
               }
-              // 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());
-
-              jalview.datamodel.SequenceI[] seq = new jalview.datamodel.SequenceI[]
-              { (jalview.datamodel.SequenceI) seqRefIds.get(JSEQ[i].getId()
-                      + "") };
+              else
+              {
+                errorMessage = ("The Jmol views in this project were imported\nfrom an older version of Jalview.\nPlease review the sequence colour associations\nin the Colour by section of the Jmol View menu.\n\nIn the case of problems, see note at\nhttp://issues.jalview.org/browse/JAL-747");
+                warn(errorMessage);
+              }
+            }
+          }
+        }
+      }
+      {
 
-              if (comp == null)
+        // Instantiate the associated Jmol views
+        for (Entry<String, Object[]> entry : jmolViewIds.entrySet())
+        {
+          String sviewid = entry.getKey();
+          Object[] svattrib = entry.getValue();
+          int[] geom = (int[]) svattrib[0];
+          String state = (String) svattrib[1];
+          Hashtable<File, Object[]> oldFiles = (Hashtable<File, Object[]>) svattrib[2];
+          final boolean useinJmolsuperpos = ((boolean[]) svattrib[3])[0], usetoColourbyseq = ((boolean[]) svattrib[3])[1], jmolColouring = ((boolean[]) svattrib[3])[2];
+          int x = geom[0], y = geom[1], width = geom[2], height = geom[3];
+          // collate the pdbfile -> sequence mappings from this view
+          Vector<String> pdbfilenames = new Vector<String>();
+          Vector<SequenceI[]> seqmaps = new Vector<SequenceI[]>();
+          Vector<String> pdbids = new Vector<String>();
+
+          // Search to see if we've already created this Jmol view
+          AppJmol comp = null;
+          JInternalFrame[] frames = null;
+          do
+          {
+            try
+            {
+              frames = Desktop.desktop.getAllFrames();
+            } catch (ArrayIndexOutOfBoundsException e)
+            {
+              // occasional No such child exceptions are thrown here...
+              frames = null;
+              try
               {
-                // create a new Jmol window
-                String state = ids[p].getStructureState(s).getContent();
-                StringBuffer newFileLoc = null;
-                if (state.indexOf("load") > -1)
-                {
-                  newFileLoc = new StringBuffer(state.substring(0, state
-                          .indexOf("\"", state.indexOf("load")) + 1));
+                Thread.sleep(10);
+              } catch (Exception f)
+              {
+              }
+              ;
+            }
+          } while (frames == null);
+          // search for any Jmol windows already open from other
+          // alignment views that exactly match the stored structure state
+          for (int f = 0; comp == null && f < frames.length; f++)
+          {
+            if (frames[f] instanceof AppJmol)
+            {
+              if (sviewid != null
+                      && ((AppJmol) frames[f]).getViewId().equals(sviewid))
+              {
+                // post jalview 2.4 schema includes structure view id
+                comp = (AppJmol) frames[f];
+              }
+              else if (frames[f].getX() == x && frames[f].getY() == y
+                      && frames[f].getHeight() == height
+                      && frames[f].getWidth() == width)
+              {
+                comp = (AppJmol) frames[f];
+              }
+            }
+          }
 
-                  newFileLoc.append(jpdb.getFile());
-                  newFileLoc.append(state.substring(state.indexOf("\"",
-                          state.indexOf("load \"") + 6)));
-                }
-                else
-                {
-                  System.err
-                          .println("Ignoring incomplete Jmol state for PDB "
-                                  + ids[p].getId());
+          if (comp == null)
+          {
+            // create a new Jmol window.
+            // First parse the Jmol state to translate filenames loaded into the
+            // view, and record the order in which files are shown in the Jmol
+            // view, so we can add the sequence mappings in same order.
+            StringBuffer newFileLoc = null;
+            int cp = 0, ncp, ecp;
+            while ((ncp = state.indexOf("load ", cp)) > -1)
+            {
+              if (newFileLoc == null)
+              {
+                newFileLoc = new StringBuffer();
+              }
+              do
+              {
+                // look for next filename in load statement
+                newFileLoc.append(state.substring(cp,
+                        ncp = (state.indexOf("\"", ncp + 1) + 1)));
+                String oldfilenam = state.substring(ncp,
+                        ecp = state.indexOf("\"", ncp));
+                // recover the new mapping data for this old filename
+                // have to normalize filename - since Jmol and jalview do
+                // filename
+                // translation differently.
+                Object[] filedat = oldFiles.get(new File(oldfilenam));
+                newFileLoc.append(Platform
+                        .escapeString((String) filedat[0]));
+                pdbfilenames.addElement((String) filedat[0]);
+                pdbids.addElement((String) filedat[1]);
+                seqmaps.addElement(((Vector<SequenceI>) filedat[2])
+                        .toArray(new SequenceI[0]));
+                newFileLoc.append("\"");
+                cp = ecp + 1; // advance beyond last \" and set cursor so we can
+                              // look for next file statement.
+              } while ((ncp = state.indexOf("/*file*/", cp)) > -1);
+            }
+            if (cp > 0)
+            {
+              // just append rest of state
+              newFileLoc.append(state.substring(cp));
+            }
+            else
+            {
+              System.err
+                      .print("Ignoring incomplete Jmol state for PDB ids: ");
+              newFileLoc = new StringBuffer(state);
+              newFileLoc.append("; load append ");
+              for (File id : oldFiles.keySet())
+              {
+                // add this and any other pdb files that should be present in
+                // the viewer
+                Object[] filedat = oldFiles.get(id);
+                String nfilename;
+                newFileLoc.append(((String) filedat[0]));
+                pdbfilenames.addElement((String) filedat[0]);
+                pdbids.addElement((String) filedat[1]);
+                seqmaps.addElement(((Vector<SequenceI>) filedat[2])
+                        .toArray(new SequenceI[0]));
+                newFileLoc.append(" \"");
+                newFileLoc.append((String) filedat[0]);
+                newFileLoc.append("\"");
 
-                  newFileLoc = new StringBuffer(state);
-                  newFileLoc.append("; load \"");
-                  newFileLoc.append(jpdb.getFile());
-                  newFileLoc.append("\";");
-                }
+              }
+              newFileLoc.append(";");
+            }
 
-                if (newFileLoc != null)
+            if (newFileLoc != null)
+            {
+              int histbug = newFileLoc.indexOf("history = ");
+              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"))
                 {
-                  new AppJmol(pdbFile, ids[p].getId(), seq, af.alignPanel,
-                          newFileLoc.toString(), new java.awt.Rectangle(x,
-                                  y, width, height), sviewid);
+                  if (val.trim().equals("true"))
+                  {
+                    val = "1";
+                  }
+                  else
+                  {
+                    val = "0";
+                  }
+                  newFileLoc.replace(histbug, diff, val);
                 }
-
               }
-              else
-              // if (comp != null)
+              // TODO: assemble String[] { pdb files }, String[] { id for each
+              // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
+              // seqs_file 2}} from hash
+              final String[] pdbf = pdbfilenames
+                      .toArray(new String[pdbfilenames.size()]), id = pdbids
+                      .toArray(new String[pdbids.size()]);
+              final SequenceI[][] sq = seqmaps
+                      .toArray(new SequenceI[seqmaps.size()][]);
+              final String fileloc = newFileLoc.toString(), vid = sviewid;
+              final AlignFrame alf = af;
+              final java.awt.Rectangle rect = new java.awt.Rectangle(x, y,
+                      width, height);
+              try
               {
-                // NOTE: if the jalview project is part of a shared session then
-                // view synchronization should/could be done here.
-
-                // add mapping for this sequence to the already open Jmol
-                // instance (if it doesn't already exist)
-                // These
-                StructureSelectionManager.getStructureSelectionManager()
-                        .setMapping(seq, null, pdbFile,
-                                jalview.io.AppletFormatAdapter.FILE);
+                javax.swing.SwingUtilities.invokeAndWait(new Runnable()
+                {
+                  @Override
+                  public void run()
+                  {
+                    JalviewStructureDisplayI sview = null;
+                    try
+                    {
+                      // JAL-1333 note - we probably can't migrate Jmol views to UCSF Chimera!
+                      sview = new StructureViewer(alf.alignPanel.getStructureSelectionManager()).createView(StructureViewer.Viewer.JMOL, pdbf, id, sq, alf.alignPanel,
+                              useinJmolsuperpos, usetoColourbyseq,
+                              jmolColouring, fileloc, rect, vid);
+                      addNewStructureViewer(sview);
+                    } catch (OutOfMemoryError ex)
+                    {
+                      new OOMWarning("restoring structure view for PDB id "
+                              + id, (OutOfMemoryError) ex.getCause());
+                      if (sview != null && sview.isVisible())
+                      {
+                        sview.closeViewer();
+                        sview.setVisible(false);
+                        sview.dispose();
+                      }
+                    }
+                  }
+                });
+              } catch (InvocationTargetException ex)
+              {
+                warn("Unexpected error when opening Jmol view.", ex);
 
-                ((AppJmol) comp).addSequence(seq);
+              } catch (InterruptedException e)
+              {
+                // e.printStackTrace();
               }
             }
+
+          }
+          else
+          // if (comp != null)
+          {
+            // NOTE: if the jalview project is part of a shared session then
+            // view synchronization should/could be done here.
+
+            // add mapping for sequences in this view to an already open Jmol
+            // instance
+            for (File id : oldFiles.keySet())
+            {
+              // add this and any other pdb files that should be present in the
+              // viewer
+              Object[] filedat = oldFiles.get(id);
+              String pdbFile = (String) filedat[0];
+              SequenceI[] seq = ((Vector<SequenceI>) filedat[2])
+                      .toArray(new SequenceI[0]);
+              comp.jmb.ssm.setMapping(seq, null, pdbFile,
+                      jalview.io.AppletFormatAdapter.FILE);
+              comp.jmb.addSequenceForStructFile(pdbFile, seq);
+            }
+            // and add the AlignmentPanel's reference to the Jmol view
+            comp.addAlignmentPanel(ap);
+            if (useinJmolsuperpos)
+            {
+              comp.useAlignmentPanelForSuperposition(ap);
+            }
+            else
+            {
+              comp.excludeAlignmentPanelForSuperposition(ap);
+            }
+            if (usetoColourbyseq)
+            {
+              comp.useAlignmentPanelForColourbyseq(ap, !jmolColouring);
+            }
+            else
+            {
+              comp.excludeAlignmentPanelForColourbyseq(ap);
+            }
           }
         }
       }
     }
-
+    // and finally return.
     return af;
   }
 
+  /**
+   * 
+   * @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]*)
+   */
+  private boolean isVersionStringLaterThan(String supported, String version)
+  {
+    if (version == null || version.equalsIgnoreCase("DEVELOPMENT BUILD")
+            || version.equalsIgnoreCase("Test")
+            || version.equalsIgnoreCase("AUTOMATED BUILD"))
+    {
+      System.err.println("Assuming project file with "
+              + (version == null ? "null" : version)
+              + " is compatible with Jalview version " + supported);
+      return true;
+    }
+    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 true;
+  }
+
+  Vector<JalviewStructureDisplayI> newStructureViewers = null;
+
+  protected void addNewStructureViewer(JalviewStructureDisplayI sview)
+  {
+    if (newStructureViewers != null)
+    {
+      sview.getBinding().setFinishedLoadingFromArchive(false);
+      newStructureViewers.add(sview);
+    }
+  }
+
+  protected void setLoadingFinishedForNewStructureViewers()
+  {
+    if (newStructureViewers != null)
+    {
+      for (JalviewStructureDisplayI sview : newStructureViewers)
+      {
+        sview.getBinding().setFinishedLoadingFromArchive(true);
+      }
+      newStructureViewers.clear();
+      newStructureViewers = null;
+    }
+  }
+
   AlignFrame loadViewport(String file, JSeq[] JSEQ, Vector hiddenSeqs,
-          Alignment al, boolean hideConsensus, boolean hideQuality,
-          boolean hideConservation, JalviewModelSequence jms,
-          Viewport view, String uniqueSeqSetId, String viewId)
+          Alignment al, JalviewModelSequence jms, Viewport view,
+          String uniqueSeqSetId, String viewId,
+          ArrayList<JvAnnotRow> autoAlan)
   {
     AlignFrame af = null;
     af = new AlignFrame(al, view.getWidth(), view.getHeight(),
@@ -2561,8 +3406,8 @@ public class Jalview2XML
 
     for (int i = 0; i < JSEQ.length; i++)
     {
-      af.viewport.setSequenceColour(af.viewport.alignment.getSequenceAt(i),
-              new java.awt.Color(JSEQ[i].getColour()));
+      af.viewport.setSequenceColour(af.viewport.getAlignment()
+              .getSequenceAt(i), new java.awt.Color(JSEQ[i].getColour()));
     }
 
     af.viewport.gatherViewsHere = view.getGatheredViews();
@@ -2572,7 +3417,7 @@ public class Jalview2XML
       jalview.gui.AlignViewport av = (jalview.gui.AlignViewport) viewportsAdded
               .get(uniqueSeqSetId);
 
-      af.viewport.sequenceSetID = uniqueSeqSetId;
+      af.viewport.setSequenceSetId(uniqueSeqSetId);
       if (av != null)
       {
         // propagate shared settings to this new view
@@ -2596,8 +3441,8 @@ public class Jalview2XML
 
         for (int r = 0; r < JSEQ[s].getHiddenSequencesCount(); r++)
         {
-          hidden.addSequence(al
-                  .getSequenceAt(JSEQ[s].getHiddenSequences(r)), false);
+          hidden.addSequence(
+                  al.getSequenceAt(JSEQ[s].getHiddenSequences(r)), false);
         }
         af.viewport.hideRepSequences(al.getSequenceAt(s), hidden);
       }
@@ -2613,35 +3458,14 @@ public class Jalview2XML
       af.viewport.hideSequence(hseqs);
 
     }
-    // set visibility of annotation in view
-    if ((hideConsensus || hideQuality || hideConservation)
-            && al.getAlignmentAnnotation() != null)
-    {
-      int hSize = al.getAlignmentAnnotation().length;
-      for (int h = 0; h < hSize; h++)
-      {
-        if ((hideConsensus && al.getAlignmentAnnotation()[h].label
-                .equals("Consensus"))
-                || (hideQuality && al.getAlignmentAnnotation()[h].label
-                        .equals("Quality"))
-                || (hideConservation && al.getAlignmentAnnotation()[h].label
-                        .equals("Conservation")))
-        {
-          al.deleteAnnotation(al.getAlignmentAnnotation()[h]);
-          hSize--;
-          h--;
-        }
-      }
-      af.alignPanel.adjustAnnotationHeight();
-    }
     // recover view properties and display parameters
     if (view.getViewName() != null)
     {
       af.viewport.viewName = view.getViewName();
       af.setInitialTabVisible();
     }
-    af.setBounds(view.getXpos(), view.getYpos(), view.getWidth(), view
-            .getHeight());
+    af.setBounds(view.getXpos(), view.getYpos(), view.getWidth(),
+            view.getHeight());
 
     af.viewport.setShowAnnotation(view.getShowAnnotation());
     af.viewport.setAbovePIDThreshold(view.getPidSelected());
@@ -2682,87 +3506,11 @@ public class Jalview2XML
       }
       else if (view.getBgColour().startsWith("Annotation"))
       {
-        // int find annotation
-        for (int i = 0; i < af.viewport.alignment.getAlignmentAnnotation().length; i++)
-        {
-          if (af.viewport.alignment.getAlignmentAnnotation()[i].label
-                  .equals(view.getAnnotationColours().getAnnotation()))
-          {
-            if (af.viewport.alignment.getAlignmentAnnotation()[i]
-                    .getThreshold() == null)
-            {
-              af.viewport.alignment.getAlignmentAnnotation()[i]
-                      .setThreshold(new jalview.datamodel.GraphLine(view
-                              .getAnnotationColours().getThreshold(),
-                              "Threshold", java.awt.Color.black)
-
-                      );
-            }
-
-            if (view.getAnnotationColours().getColourScheme()
-                    .equals("None"))
-            {
-              cs = new AnnotationColourGradient(af.viewport.alignment
-                      .getAlignmentAnnotation()[i], new java.awt.Color(view
-                      .getAnnotationColours().getMinColour()),
-                      new java.awt.Color(view.getAnnotationColours()
-                              .getMaxColour()), view.getAnnotationColours()
-                              .getAboveThreshold());
-            }
-            else if (view.getAnnotationColours().getColourScheme()
-                    .startsWith("ucs"))
-            {
-              cs = new AnnotationColourGradient(af.viewport.alignment
-                      .getAlignmentAnnotation()[i], GetUserColourScheme(
-                      jms, view.getAnnotationColours().getColourScheme()),
-                      view.getAnnotationColours().getAboveThreshold());
-            }
-            else
-            {
-              cs = new AnnotationColourGradient(af.viewport.alignment
-                      .getAlignmentAnnotation()[i], ColourSchemeProperty
-                      .getColour(al, view.getAnnotationColours()
-                              .getColourScheme()), view
-                      .getAnnotationColours().getAboveThreshold());
-            }
+        AnnotationColours viewAnnColour = view.getAnnotationColours();
+        cs = constructAnnotationColour(viewAnnColour, af, al, jms, true);
 
-            // Also use these settings for all the groups
-            if (al.getGroups() != null)
-            {
-              for (int g = 0; g < al.getGroups().size(); g++)
-              {
-                jalview.datamodel.SequenceGroup sg = (jalview.datamodel.SequenceGroup) al
-                        .getGroups().elementAt(g);
+        // annpos
 
-                if (sg.cs == null)
-                {
-                  continue;
-                }
-
-                /*
-                 * if
-                 * (view.getAnnotationColours().getColourScheme().equals("None"
-                 * )) { sg.cs = new AnnotationColourGradient(
-                 * af.viewport.alignment.getAlignmentAnnotation()[i], new
-                 * java.awt.Color(view.getAnnotationColours(). getMinColour()),
-                 * new java.awt.Color(view.getAnnotationColours().
-                 * getMaxColour()),
-                 * view.getAnnotationColours().getAboveThreshold()); } else
-                 */
-                {
-                  sg.cs = new AnnotationColourGradient(
-                          af.viewport.alignment.getAlignmentAnnotation()[i],
-                          sg.cs, view.getAnnotationColours()
-                                  .getAboveThreshold());
-                }
-
-              }
-            }
-
-            break;
-          }
-
-        }
       }
       else
       {
@@ -2772,7 +3520,7 @@ public class Jalview2XML
       if (cs != null)
       {
         cs.setThreshold(view.getPidThreshold(), true);
-        cs.setConsensus(af.viewport.hconsensus);
+        cs.setConsensus(af.viewport.getSequenceConsensusHash());
       }
     }
 
@@ -2798,7 +3546,8 @@ public class Jalview2XML
     }
     if (view.hasIgnoreGapsinConsensus())
     {
-      af.viewport.ignoreGapsInConsensusCalculation=view.getIgnoreGapsinConsensus();
+      af.viewport.setIgnoreGapsConsensus(view.getIgnoreGapsinConsensus(),
+              null);
     }
     if (view.hasFollowHighlight())
     {
@@ -2810,11 +3559,24 @@ public class Jalview2XML
     }
     if (view.hasShowConsensusHistogram())
     {
-      af.viewport.setShowConsensusHistogram(view.getShowConsensusHistogram());
+      af.viewport.setShowConsensusHistogram(view
+              .getShowConsensusHistogram());
+    }
+    else
+    {
+      af.viewport.setShowConsensusHistogram(true);
+    }
+    if (view.hasShowSequenceLogo())
+    {
+      af.viewport.setShowSequenceLogo(view.getShowSequenceLogo());
+    }
+    else
+    {
+      af.viewport.setShowSequenceLogo(false);
     }
-    if (view.hasShowConsensusProfile())
+    if (view.hasNormaliseSequenceLogo())
     {
-      af.viewport.setShowConsensusProfile(view.getShowConsensusProfile());
+      af.viewport.setNormaliseSequenceLogo(view.getNormaliseSequenceLogo());
     }
     if (view.hasShowDbRefTooltip())
     {
@@ -2828,11 +3590,19 @@ public class Jalview2XML
     {
       af.viewport.setShowGroupConsensus(view.getShowGroupConsensus());
     }
+    else
+    {
+      af.viewport.setShowGroupConsensus(false);
+    }
     if (view.hasShowGroupConservation())
     {
       af.viewport.setShowGroupConservation(view.getShowGroupConservation());
     }
-    
+    else
+    {
+      af.viewport.setShowGroupConservation(false);
+    }
+
     // recover featre settings
     if (jms.getFeatureSettings() != null)
     {
@@ -2844,12 +3614,12 @@ public class Jalview2XML
         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);
+          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);
           if (setting.hasThreshold())
           {
             gc.setThresh(setting.getThreshold());
@@ -2865,7 +3635,8 @@ public class Jalview2XML
             gc.setColourByLabel(setting.getColourByLabel());
           }
           // and put in the feature colour table.
-          af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setColour(setting.getType(),gc);
+          af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setColour(
+                  setting.getType(), gc);
         }
         else
         {
@@ -2875,12 +3646,16 @@ public class Jalview2XML
         }
         renderOrder[fs] = setting.getType();
         if (setting.hasOrder())
+        {
           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
                   setting.getType(), setting.getOrder());
+        }
         else
+        {
           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
                   setting.getType(),
                   fs / jms.getFeatureSettings().getSettingCount());
+        }
         if (setting.getDisplay())
         {
           af.viewport.featuresDisplayed.put(setting.getType(), new Integer(
@@ -2906,15 +3681,257 @@ public class Jalview2XML
                 );
       }
     }
-
+    if (view.getCalcIdParam() != null)
+    {
+      for (CalcIdParam calcIdParam : view.getCalcIdParam())
+      {
+        if (calcIdParam != null)
+        {
+          if (recoverCalcIdParam(calcIdParam, af.viewport))
+          {
+          }
+          else
+          {
+            warn("Couldn't recover parameters for "
+                    + calcIdParam.getCalcId());
+          }
+        }
+      }
+    }
     af.setMenusFromViewport(af.viewport);
-    af.alignPanel.updateAnnotation(false); // recompute any autoannotation
     // TODO: we don't need to do this if the viewport is aready visible.
-    Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(), view
-            .getHeight());
+    Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(),
+            view.getHeight());
+    af.alignPanel.updateAnnotation(false, true); // recompute any autoannotation
+    reorderAutoannotation(af, al, autoAlan);
+    af.alignPanel.alignmentChanged();
     return af;
   }
 
+  private ColourSchemeI constructAnnotationColour(
+          AnnotationColours viewAnnColour, AlignFrame af, Alignment al,
+          JalviewModelSequence jms, boolean checkGroupAnnColour)
+  {
+    boolean propagateAnnColour = false;
+    ColourSchemeI cs = null;
+    AlignmentI annAlignment = af != null ? af.viewport.getAlignment() : al;
+    if (checkGroupAnnColour && al.getGroups() != null
+            && al.getGroups().size() > 0)
+    {
+      // pre 2.8.1 behaviour
+      // check to see if we should transfer annotation colours
+      propagateAnnColour = true;
+      for (jalview.datamodel.SequenceGroup sg : al.getGroups())
+      {
+        if (sg.cs instanceof AnnotationColourGradient)
+        {
+          propagateAnnColour = false;
+        }
+      }
+    }
+    // int find annotation
+    if (annAlignment.getAlignmentAnnotation() != null)
+    {
+      for (int i = 0; i < annAlignment.getAlignmentAnnotation().length; i++)
+      {
+        if (annAlignment.getAlignmentAnnotation()[i].label
+                .equals(viewAnnColour.getAnnotation()))
+        {
+          if (annAlignment.getAlignmentAnnotation()[i].getThreshold() == null)
+          {
+            annAlignment.getAlignmentAnnotation()[i]
+                    .setThreshold(new jalview.datamodel.GraphLine(
+                            viewAnnColour.getThreshold(), "Threshold",
+                            java.awt.Color.black)
+
+                    );
+          }
+
+          if (viewAnnColour.getColourScheme().equals("None"))
+          {
+            cs = new AnnotationColourGradient(
+                    annAlignment.getAlignmentAnnotation()[i],
+                    new java.awt.Color(viewAnnColour.getMinColour()),
+                    new java.awt.Color(viewAnnColour.getMaxColour()),
+                    viewAnnColour.getAboveThreshold());
+          }
+          else if (viewAnnColour.getColourScheme().startsWith("ucs"))
+          {
+            cs = new AnnotationColourGradient(
+                    annAlignment.getAlignmentAnnotation()[i],
+                    GetUserColourScheme(jms,
+                            viewAnnColour.getColourScheme()),
+                    viewAnnColour.getAboveThreshold());
+          }
+          else
+          {
+            cs = new AnnotationColourGradient(
+                    annAlignment.getAlignmentAnnotation()[i],
+                    ColourSchemeProperty.getColour(al,
+                            viewAnnColour.getColourScheme()),
+                    viewAnnColour.getAboveThreshold());
+          }
+          if (viewAnnColour.hasPerSequence())
+          {
+            ((AnnotationColourGradient) cs).setSeqAssociated(viewAnnColour
+                    .isPerSequence());
+          }
+          if (viewAnnColour.hasPredefinedColours())
+          {
+            ((AnnotationColourGradient) cs)
+                    .setPredefinedColours(viewAnnColour
+                            .isPredefinedColours());
+          }
+          if (propagateAnnColour && al.getGroups() != null)
+          {
+            // Also use these settings for all the groups
+            for (int g = 0; g < al.getGroups().size(); g++)
+            {
+              jalview.datamodel.SequenceGroup sg = al.getGroups().get(g);
+
+              if (sg.cs == null)
+              {
+                continue;
+              }
+
+              /*
+               * if (viewAnnColour.getColourScheme().equals("None" )) { sg.cs =
+               * new AnnotationColourGradient(
+               * annAlignment.getAlignmentAnnotation()[i], new
+               * java.awt.Color(viewAnnColour. getMinColour()), new
+               * java.awt.Color(viewAnnColour. getMaxColour()),
+               * viewAnnColour.getAboveThreshold()); } else
+               */
+              {
+                sg.cs = new AnnotationColourGradient(
+                        annAlignment.getAlignmentAnnotation()[i], sg.cs,
+                        viewAnnColour.getAboveThreshold());
+                if (cs instanceof AnnotationColourGradient)
+                {
+                  if (viewAnnColour.hasPerSequence())
+                  {
+                    ((AnnotationColourGradient) cs)
+                            .setSeqAssociated(viewAnnColour.isPerSequence());
+                  }
+                  if (viewAnnColour.hasPredefinedColours())
+                  {
+                    ((AnnotationColourGradient) cs)
+                            .setPredefinedColours(viewAnnColour
+                                    .isPredefinedColours());
+                  }
+                }
+              }
+
+            }
+          }
+
+          break;
+        }
+
+      }
+    }
+    return cs;
+  }
+
+  private void reorderAutoannotation(AlignFrame af, Alignment al,
+          ArrayList<JvAnnotRow> autoAlan)
+  {
+    // copy over visualization settings for autocalculated annotation in the
+    // view
+    if (al.getAlignmentAnnotation() != null)
+    {
+      /**
+       * Kludge for magic autoannotation names (see JAL-811)
+       */
+      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)
+      {
+        visan.put(nm, nullAnnot);
+      }
+      for (JvAnnotRow auan : autoAlan)
+      {
+        visan.put(auan.template.label
+                + (auan.template.getCalcId() == null ? "" : "\t"
+                        + auan.template.getCalcId()), auan);
+      }
+      int hSize = al.getAlignmentAnnotation().length;
+      ArrayList<JvAnnotRow> reorder = new ArrayList<JvAnnotRow>();
+      // work through any autoCalculated annotation already on the view
+      // removing it if it should be placed in a different location on the
+      // annotation panel.
+      List<String> remains = new ArrayList(visan.keySet());
+      for (int h = 0; h < hSize; h++)
+      {
+        jalview.datamodel.AlignmentAnnotation jalan = al
+                .getAlignmentAnnotation()[h];
+        if (jalan.autoCalculated)
+        {
+          String k;
+          JvAnnotRow valan = visan.get(k = jalan.label);
+          if (jalan.getCalcId() != null)
+          {
+            valan = visan.get(k = jalan.label + "\t" + jalan.getCalcId());
+          }
+
+          if (valan != null)
+          {
+            // delete the auto calculated row from the alignment
+            al.deleteAnnotation(jalan, false);
+            remains.remove(k);
+            hSize--;
+            h--;
+            if (valan != nullAnnot)
+            {
+              if (jalan != valan.template)
+              {
+                // newly created autoannotation row instance
+                // so keep a reference to the visible annotation row
+                // and copy over all relevant attributes
+                if (valan.template.graphHeight >= 0)
+
+                {
+                  jalan.graphHeight = valan.template.graphHeight;
+                }
+                jalan.visible = valan.template.visible;
+              }
+              reorder.add(new JvAnnotRow(valan.order, jalan));
+            }
+          }
+        }
+      }
+      // Add any (possibly stale) autocalculated rows that were not appended to
+      // the view during construction
+      for (String other : remains)
+      {
+        JvAnnotRow othera = visan.get(other);
+        if (othera != nullAnnot && othera.template.getCalcId() != null
+                && othera.template.getCalcId().length() > 0)
+        {
+          reorder.add(othera);
+        }
+      }
+      // now put the automatic annotation in its correct place
+      int s = 0, srt[] = new int[reorder.size()];
+      JvAnnotRow[] rws = new JvAnnotRow[reorder.size()];
+      for (JvAnnotRow jvar : reorder)
+      {
+        rws[s] = jvar;
+        srt[s++] = jvar.order;
+      }
+      reorder.clear();
+      jalview.util.QuickSort.sort(srt, rws);
+      // and re-insert the annotation at its correct position
+      for (JvAnnotRow jvar : rws)
+      {
+        al.addAnnotation(jvar.template, jvar.order);
+      }
+      af.alignPanel.adjustAnnotationHeight();
+    }
+  }
+
   Hashtable skipList = null;
 
   /**
@@ -2971,7 +3988,8 @@ public class Jalview2XML
     }
   }
 
-  private void recoverDatasetFor(SequenceSet vamsasSet, Alignment al)
+  private void recoverDatasetFor(SequenceSet vamsasSet, Alignment al,
+          boolean ignoreUnrefed)
   {
     jalview.datamodel.Alignment ds = getDatasetFor(vamsasSet.getDatasetId());
     Vector dseqs = null;
@@ -2983,7 +4001,7 @@ public class Jalview2XML
     for (int i = 0, iSize = vamsasSet.getSequenceCount(); i < iSize; i++)
     {
       Sequence vamsasSeq = vamsasSet.getSequence(i);
-      ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs);
+      ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs, ignoreUnrefed);
     }
     // create a new dataset
     if (ds == null)
@@ -2996,7 +4014,7 @@ public class Jalview2XML
       addDatasetRef(vamsasSet.getDatasetId(), ds);
     }
     // set the dataset for the newly imported alignment.
-    if (al.getDataset() == null)
+    if (al.getDataset() == null && !ignoreUnrefed)
     {
       al.setDataset(ds);
     }
@@ -3012,7 +4030,7 @@ public class Jalview2XML
    *          vector to add new dataset sequence to
    */
   private void ensureJalviewDatasetSequence(Sequence vamsasSeq,
-          AlignmentI ds, Vector dseqs)
+          AlignmentI ds, Vector dseqs, boolean ignoreUnrefed)
   {
     // JBP TODO: Check this is called for AlCodonFrames to support recovery of
     // xRef Codon Maps
@@ -3021,9 +4039,12 @@ public class Jalview2XML
     jalview.datamodel.SequenceI dsq = null;
     if (sq != null && sq.getDatasetSequence() != null)
     {
-      dsq = (jalview.datamodel.SequenceI) sq.getDatasetSequence();
+      dsq = sq.getDatasetSequence();
+    }
+    if (sq == null && ignoreUnrefed)
+    {
+      return;
     }
-
     String sqid = vamsasSeq.getDsseqid();
     if (dsq == null)
     {
@@ -3061,6 +4082,24 @@ public class Jalview2XML
         if (sq != dsq)
         { // make this dataset sequence sq's dataset sequence
           sq.setDatasetSequence(dsq);
+          // and update the current dataset alignment
+          if (ds == null)
+          {
+            if (dseqs != null)
+            {
+              if (!dseqs.contains(dsq))
+              {
+                dseqs.add(dsq);
+              }
+            }
+            else
+            {
+              if (ds.findIndex(dsq) < 0)
+              {
+                ds.addSequence(dsq);
+              }
+            }
+          }
         }
       }
     }
@@ -3087,11 +4126,12 @@ public class Jalview2XML
            * sb.append(newres.substring(newres.length() - sq.getEnd() -
            * dsq.getEnd())); dsq.setEnd(sq.getEnd()); }
            */
-          dsq.setSequence(sb.toString());
+          dsq.setSequence(newres);
         }
         // TODO: merges will never happen if we 'know' we have the real dataset
         // sequence - this should be detected when id==dssid
-        System.err.println("DEBUG Notice:  Merged dataset sequence"); // ("
+        System.err
+                .println("DEBUG Notice:  Merged dataset sequence (if you see this often, post at http://issues.jalview.org/browse/JAL-1474)"); // ("
         // + (pre ? "prepended" : "") + " "
         // + (post ? "appended" : ""));
       }
@@ -3244,8 +4284,8 @@ public class Jalview2XML
           /**
            * make a new dataset sequence and add it to refIds hash
            */
-          djs = new jalview.datamodel.Sequence(ms.getName(), ms
-                  .getSequence());
+          djs = new jalview.datamodel.Sequence(ms.getName(),
+                  ms.getSequence());
           djs.setStart(jmap.getMap().getToLowest());
           djs.setEnd(jmap.getMap().getToHighest());
           djs.setVamsasId(uniqueSetSuffix + sqid);
@@ -3278,9 +4318,9 @@ public class Jalview2XML
     {
       uniqueSetSuffix = "";
       jm.getJalviewModelSequence().getViewport(0).setId(null); // we don't
-                                                               // overwrite the
-                                                               // view we just
-                                                               // copied
+      // overwrite the
+      // view we just
+      // copied
     }
     if (this.frefedSequence == null)
     {
@@ -3294,11 +4334,11 @@ public class Jalview2XML
     af.closeMenuItem_actionPerformed(true);
 
     /*
-     * if(ap.av.alignment.getAlignmentAnnotation()!=null) { for(int i=0;
-     * i<ap.av.alignment.getAlignmentAnnotation().length; i++) {
-     * if(!ap.av.alignment.getAlignmentAnnotation()[i].autoCalculated) {
-     * af.alignPanel.av.alignment.getAlignmentAnnotation()[i] =
-     * ap.av.alignment.getAlignmentAnnotation()[i]; } } }
+     * if(ap.av.getAlignment().getAlignmentAnnotation()!=null) { for(int i=0;
+     * i<ap.av.getAlignment().getAlignmentAnnotation().length; i++) {
+     * if(!ap.av.getAlignment().getAlignmentAnnotation()[i].autoCalculated) {
+     * af.alignPanel.av.getAlignment().getAlignmentAnnotation()[i] =
+     * ap.av.getAlignment().getAlignmentAnnotation()[i]; } } }
      */
 
     return af.alignPanel;
@@ -3308,7 +4348,7 @@ public class Jalview2XML
    * flag indicating if hashtables should be cleared on finalization TODO this
    * flag may not be necessary
    */
-  private boolean _cleartables = true;
+  private final boolean _cleartables = true;
 
   private Hashtable jvids2vobj;
 
@@ -3317,6 +4357,7 @@ public class Jalview2XML
    * 
    * @see java.lang.Object#finalize()
    */
+  @Override
   protected void finalize() throws Throwable
   {
     // really make sure we have no buried refs left.
@@ -3461,7 +4502,9 @@ public class Jalview2XML
         }
       }
       else
+      {
         Cache.log.debug("Ignoring " + jvobj.getClass() + " (ID = " + id);
+      }
     }
   }