JAL-1177 patches to loader to cope with dataset only jalview xml documents
[jalview.git] / src / jalview / gui / Jalview2XML.java
index 3c5259e..3499f6d 100644 (file)
@@ -1,19 +1,20 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)
- * Copyright (C) 2011 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.0b1)
+ * Copyright (C) 2014 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
  * 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 Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
 package jalview.gui;
 
@@ -29,7 +30,6 @@ import javax.swing.*;
 
 import org.exolab.castor.xml.*;
 
-import uk.ac.vamsas.objects.utils.MapList;
 import jalview.bin.Cache;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
@@ -37,8 +37,14 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceI;
 import jalview.schemabinding.version2.*;
 import jalview.schemes.*;
-import jalview.structure.StructureSelectionManager;
+import jalview.util.Platform;
 import jalview.util.jarInputStreamProvider;
+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;
 
 /**
  * Write out the current jalview desktop state as a Jalview XML stream.
@@ -414,6 +420,26 @@ public class Jalview2XML
   public JalviewModel SaveState(AlignmentPanel ap, String fileName,
           JarOutputStream jout)
   {
+    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();
@@ -424,11 +450,11 @@ 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();
     }
@@ -443,6 +469,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)
     {
@@ -458,13 +489,15 @@ 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)
@@ -496,30 +529,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)
@@ -561,9 +598,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();
@@ -572,7 +609,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();
@@ -612,8 +652,8 @@ public class Jalview2XML
 
                 for (int smap = 0; smap < jmol.jmb.sequence[peid].length; smap++)
                 {
-//                  if (jal.findIndex(jmol.jmb.sequence[peid][smap]) > -1)
-                  if (jds==jmol.jmb.sequence[peid][smap])
+                  // if (jal.findIndex(jmol.jmb.sequence[peid][smap]) > -1)
+                  if (jds == jmol.jmb.sequence[peid][smap])
                   {
                     StructureState state = new StructureState();
                     state.setVisible(true);
@@ -639,6 +679,7 @@ public class Jalview2XML
                     }
                     pdb.addStructureState(state);
                   }
+
                 }
               }
             }
@@ -707,9 +748,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)
@@ -751,7 +792,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
@@ -765,7 +806,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());
@@ -793,148 +834,42 @@ 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++)
-      {
-        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());
-        }
-
-        // 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);
-
-        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.quality || aa[i] == av.conservation
-                || aa[i] == av.consensus || aa[i].autoCalculated)
-        {
-          // new way of indicating autocalculated annotation -
-          an.setAutoCalculated(aa[i].autoCalculated);
-        }
-        if (aa[i].hasScore())
-        {
-          an.setScore(aa[i].getScore());
-        }
-        AnnotationElement ae;
-        if (aa[i].annotations != null)
+        for (SequenceI sq:jal.getSequences())
         {
-          an.setScoreOnly(false);
-          for (int a = 0; a < aa[i].annotations.length; a++)
+       // Store annotation on dataset sequences only
+          jalview.datamodel.AlignmentAnnotation[] aa = sq.getAnnotation();
+          if (aa!=null && aa.length>0)
           {
-            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);
-            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;
-            }
+            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());
@@ -991,6 +926,7 @@ public class Jalview2XML
         groups[i].setIgnoreGapsinConsensus(sg.getIgnoreGapsConsensus());
         groups[i].setShowConsensusHistogram(sg.isShowConsensusHistogram());
         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
@@ -1001,234 +937,255 @@ public class Jalview2XML
 
       jms.setJGroup(groups);
     }
+    if (!storeDS)
+    {
+      // /////////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);
+      }
 
-    // /////////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)
-    {
-      jalview.schemes.AnnotationColourGradient acg = (jalview.schemes.AnnotationColourGradient) av
-              .getGlobalColourScheme();
+      view.setStartRes(av.startRes);
+      view.setStartSeq(av.startSeq);
 
-      AnnotationColours ac = new AnnotationColours();
-      ac.setAboveThreshold(acg.getAboveThreshold());
-      ac.setThreshold(acg.getAnnotationThreshold());
-      ac.setAnnotation(acg.getAnnotation());
-      if (acg.getBaseColour() instanceof jalview.schemes.UserColourScheme)
+      if (av.getGlobalColourScheme() instanceof jalview.schemes.UserColourScheme)
       {
-        ac.setColourScheme(SetUserColourScheme(acg.getBaseColour(),
+        view.setBgColour(SetUserColourScheme(av.getGlobalColourScheme(),
                 userColours, jms));
       }
+      else if (av.getGlobalColourScheme() instanceof jalview.schemes.AnnotationColourGradient)
+      {
+        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)
+        {
+          ac.setColourScheme(SetUserColourScheme(acg.getBaseColour(),
+                  userColours, jms));
+        }
+        else
+        {
+          ac.setColourScheme(ColourSchemeProperty.getColourName(acg
+                  .getBaseColour()));
+        }
+
+        ac.setMaxColour(acg.getMaxColour().getRGB());
+        ac.setMinColour(acg.getMinColour().getRGB());
+        ac.setPerSequence(acg.isSeqAssociated());
+        ac.setPredefinedColours(acg.isPredefinedColours());
+        view.setAnnotationColours(ac);
+        view.setBgColour("AnnotationColourGradient");
+      }
       else
       {
-        ac.setColourScheme(ColourSchemeProperty.getColourName(acg
-                .getBaseColour()));
+        view.setBgColour(ColourSchemeProperty.getColourName(av
+                .getGlobalColourScheme()));
       }
 
-      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();
 
-    ColourSchemeI cs = av.getGlobalColourScheme();
-
-    if (cs != null)
-    {
-      if (cs.conservationApplied())
+      if (cs != null)
       {
-        view.setConsThreshold(cs.getConservationInc());
-        if (cs instanceof jalview.schemes.UserColourScheme)
+        if (cs.conservationApplied())
         {
-          view.setBgColour(SetUserColourScheme(cs, userColours, jms));
+          view.setConsThreshold(cs.getConservationInc());
+          if (cs instanceof jalview.schemes.UserColourScheme)
+          {
+            view.setBgColour(SetUserColourScheme(cs, userColours, jms));
+          }
         }
-      }
 
-      if (cs instanceof ResidueColourScheme)
-      {
-        view.setPidThreshold(cs.getThreshold());
+        if (cs instanceof ResidueColourScheme)
+        {
+          view.setPidThreshold(cs.getThreshold());
+        }
       }
-    }
 
-    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.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();
+      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)
+      {
+        jalview.schemabinding.version2.FeatureSettings fs = new jalview.schemabinding.version2.FeatureSettings();
 
-      String[] renderOrder = ap.seqPanel.seqCanvas.getFeatureRenderer().renderOrder;
+        String[] renderOrder = ap.seqPanel.seqCanvas.getFeatureRenderer().renderOrder;
 
-      Vector settingsAdded = new Vector();
-      Object gstyle = null;
-      GraduatedColor gcol = null;
-      if (renderOrder != null)
-      {
-        for (int ro = 0; ro < renderOrder.length; ro++)
+        Vector settingsAdded = new Vector();
+        Object gstyle = null;
+        GraduatedColor gcol = null;
+        if (renderOrder != 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());
+          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.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]);
           }
-          else
+        }
+
+        // Make sure we save none displayed feature settings
+        Iterator en = ap.seqPanel.seqCanvas.getFeatureRenderer().featureColours
+                .keySet().iterator();
+        while (en.hasNext())
+        {
+          String key = en.next().toString();
+          if (settingsAdded.contains(key))
           {
-            setting.setColour(ap.seqPanel.seqCanvas.getFeatureRenderer()
-                    .getColour(renderOrder[ro]).getRGB());
+            continue;
           }
 
-          setting.setDisplay(av.featuresDisplayed
-                  .containsKey(renderOrder[ro]));
+          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(renderOrder[ro]);
+                  .getOrder(key);
           if (rorder > -1)
           {
             setting.setOrder(rorder);
           }
           fs.addSetting(setting);
-          settingsAdded.addElement(renderOrder[ro]);
+          settingsAdded.addElement(key);
         }
-      }
-
-      // Make sure we save none displayed feature settings
-      Enumeration en = ap.seqPanel.seqCanvas.getFeatureRenderer().featureColours
-              .keys();
-      while (en.hasMoreElements())
-      {
-        String key = en.nextElement().toString();
-        if (settingsAdded.contains(key))
+        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);
         }
+        jms.setFeatureSettings(fs);
 
-        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)
+      if (av.hasHiddenColumns())
+      {
+        if (av.getColumnSelection() == null
+                || av.getColumnSelection().getHiddenColumns() == null)
         {
-          setting.setOrder(rorder);
+          warn("REPORT BUG: avoided null columnselection bug (DMAM reported). Please contact Jim about this.");
         }
-        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))
+        else
         {
-          continue;
+          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);
+          }
         }
-        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);
-
-    }
-
-    if (av.hasHiddenColumns)
-    {
-      if (av.getColumnSelection() == null
-              || av.getColumnSelection().getHiddenColumns() == null)
+      if (calcIdSet.size() > 0)
       {
-        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++)
+        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);
 
@@ -1257,6 +1214,238 @@ public class Jalview2XML
     return object;
   }
 
+  private void storeAlignmentAnnotation(AlignmentAnnotation[] aa, IdentityHashMap groupRefs, AlignmentViewport av, Set<String> calcIdSet, boolean storeDS, SequenceSet vamsasSet)
+  {
+
+    for (int i = 0; i < aa.length; i++)
+    {
+      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());
+      }
+
+      // 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());
+      }
+
+      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 != ' '
+                  && 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);
+          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("Unsupported Version for calcIdparam "
+            + 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
@@ -1546,6 +1735,8 @@ public class Jalview2XML
 
     try
     {
+      // create list to store references for any new Jmol viewers created
+      newStructureViewers=new Vector<AppJmol>();
       // UNMARSHALLER SEEMS TO CLOSE JARINPUTSTREAM, MOST ANNOYING
       // Workaround is to make sure caller implements the JarInputStreamProvider
       // interface
@@ -1553,11 +1744,27 @@ 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;
   }
 
@@ -1579,6 +1786,7 @@ public class Jalview2XML
     return new jarInputStreamProvider()
     {
 
+      @Override
       public JarInputStream getJarInputStream() throws IOException
       {
         if (_url != null)
@@ -1591,6 +1799,7 @@ public class Jalview2XML
         }
       }
 
+      @Override
       public String getFilename()
       {
         return file;
@@ -1627,7 +1836,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
@@ -1654,10 +1863,15 @@ 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 (object.getJalviewModelSequence().getViewportCount() > 1
+                      && af.viewport.gatherViewsHere)
+              {
+                gatherToThisFrame.put(af.viewport.getSequenceSetId(), af);
+              }
             }
           }
           entryCount++;
@@ -1755,6 +1969,7 @@ public class Jalview2XML
       {
         javax.swing.SwingUtilities.invokeLater(new Runnable()
         {
+          @Override
           public void run()
           {
             JOptionPane.showInternalMessageDialog(Desktop.desktop,
@@ -1779,7 +1994,7 @@ public class Jalview2XML
    * 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)
   {
@@ -1823,7 +2038,7 @@ public class Jalview2XML
         }
         ;
         out.close();
-        String t=outFile.toURI().getPath().substring(1);
+        String t = outFile.getAbsolutePath();
         alreadyLoadedPDB.put(pdbId, t);
         return t;
       }
@@ -1879,7 +2094,8 @@ public class Jalview2XML
 
     JalviewModelSequence jms = object.getJalviewModelSequence();
 
-    Viewport view = jms.getViewport(0);
+    Viewport view = (jms.getViewportCount()>0) ? jms.getViewport(0) : null;
+    
     // ////////////////////////////////
     // LOAD SEQUENCES
 
@@ -1898,7 +2114,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
@@ -1921,8 +2137,7 @@ public class Jalview2XML
           hiddenSeqs = new Vector();
         }
 
-        hiddenSeqs.addElement((jalview.datamodel.Sequence) seqRefIds
-                .get(seqId));
+        hiddenSeqs.addElement(seqRefIds.get(seqId));
       }
 
     }
@@ -2143,16 +2358,19 @@ 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]
@@ -2168,8 +2386,11 @@ 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;
@@ -2185,7 +2406,7 @@ public class Jalview2XML
                   an[i].getGraphType());
 
           jaa.graphGroup = an[i].getGraphGroup();
-
+          jaa._linecolour = firstColour;
           if (an[i].getThresholdLine() != null)
           {
             jaa.setThreshold(new jalview.datamodel.GraphLine(an[i]
@@ -2205,25 +2426,22 @@ public class Jalview2XML
         {
           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
                   an[i].getDescription(), anot);
+          jaa._linecolour = firstColour;
         }
-        if (autoForView)
+        // register new annotation
+        if (an[i].getId() != null)
         {
-          // register new annotation
-          if (an[i].getId() != null)
-          {
-            annotationIds.put(an[i].getId(), jaa);
-            jaa.annotationId = an[i].getId();
-          }
-          // recover sequence association
-          if (an[i].getSequenceRef() != null)
+          annotationIds.put(an[i].getId(), jaa);
+          jaa.annotationId = an[i].getId();
+        }
+        // recover sequence association
+        if (an[i].getSequenceRef() != null)
+        {
+          if (al.findName(an[i].getSequenceRef()) != null)
           {
-            if (al.findName(an[i].getSequenceRef()) != null)
-            {
-              jaa.createSequenceMapping(
-                      al.findName(an[i].getSequenceRef()), 1, true);
-              al.findName(an[i].getSequenceRef()).addAlignmentAnnotation(
-                      jaa);
-            }
+            jaa.createSequenceMapping(al.findName(an[i].getSequenceRef()),
+                    1, true);
+            al.findName(an[i].getSequenceRef()).addAlignmentAnnotation(jaa);
           }
         }
         // and make a note of any group association
@@ -2264,6 +2482,12 @@ public class Jalview2XML
         {
           jaa.graphHeight = an[i].getGraphHeight();
         }
+        if (an[i].hasBelowAlignment())
+        {
+          jaa.belowAlignment = an[i].isBelowAlignment();
+        }
+        jaa.setCalcId(an[i].getCalcId());
+
         if (jaa.autoCalculated)
         {
           autoAlan.add(new JvAnnotRow(i, jaa));
@@ -2346,6 +2570,10 @@ public class Jalview2XML
         {
           sg.setshowSequenceLogo(groups[i].isShowSequenceLogo());
         }
+        if (groups[i].hasNormaliseSequenceLogo())
+        {
+          sg.setNormaliseSequenceLogo(groups[i].isNormaliseSequenceLogo());
+        }
         if (groups[i].hasIgnoreGapsinConsensus())
         {
           sg.setIgnoreGapsConsensus(groups[i].getIgnoreGapsinConsensus());
@@ -2392,7 +2620,11 @@ public class Jalview2XML
 
       }
     }
-
+    if (view==null)
+    {
+      // only dataset in this model, so just return.
+      return null;
+    }
     // ///////////////////////////////
     // LOAD VIEWPORT
 
@@ -2604,7 +2836,7 @@ public class Jalview2XML
               // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
               // seqs_file 2}, boolean[] {
               // linkAlignPanel,superposeWithAlignpanel}} from hash
-              Object[] jmoldat = (Object[]) jmolViewIds.get(sviewid);
+              Object[] jmoldat = jmolViewIds.get(sviewid);
               ((boolean[]) jmoldat[3])[0] |= ids[p].getStructureState(s)
                       .hasAlignwithAlignPanel() ? ids[p].getStructureState(
                       s).getAlignwithAlignPanel() : false;
@@ -2627,13 +2859,12 @@ public class Jalview2XML
               }
               if (ids[p].getFile() != null)
               {
-                File mapkey=new File(ids[p].getFile());
+                File mapkey = new File(ids[p].getFile());
                 Object[] seqstrmaps = (Object[]) ((Hashtable) jmoldat[2])
                         .get(mapkey);
                 if (seqstrmaps == null)
                 {
-                  ((Hashtable) jmoldat[2]).put(
-                          mapkey,
+                  ((Hashtable) jmoldat[2]).put(mapkey,
                           seqstrmaps = new Object[]
                           { pdbFile, ids[p].getId(), new Vector(),
                               new Vector() });
@@ -2730,22 +2961,28 @@ public class Jalview2XML
               {
                 newFileLoc = new StringBuffer();
               }
-              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(((String) filedat[0]));
-              pdbfilenames.addElement((String) filedat[0]);
-              pdbids.addElement((String) filedat[1]);
-              seqmaps.addElement((SequenceI[]) ((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.
+              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)
             {
@@ -2767,7 +3004,7 @@ public class Jalview2XML
                 newFileLoc.append(((String) filedat[0]));
                 pdbfilenames.addElement((String) filedat[0]);
                 pdbids.addElement((String) filedat[1]);
-                seqmaps.addElement((SequenceI[]) ((Vector<SequenceI>) filedat[2])
+                seqmaps.addElement(((Vector<SequenceI>) filedat[2])
                         .toArray(new SequenceI[0]));
                 newFileLoc.append(" \"");
                 newFileLoc.append((String) filedat[0]);
@@ -2803,10 +3040,10 @@ public class Jalview2XML
               // TODO: assemble String[] { pdb files }, String[] { id for each
               // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
               // seqs_file 2}} from hash
-              final String[] pdbf = (String[]) pdbfilenames
-                      .toArray(new String[pdbfilenames.size()]), id = (String[]) pdbids
+              final String[] pdbf = pdbfilenames
+                      .toArray(new String[pdbfilenames.size()]), id = pdbids
                       .toArray(new String[pdbids.size()]);
-              final SequenceI[][] sq = (SequenceI[][]) seqmaps
+              final SequenceI[][] sq = seqmaps
                       .toArray(new SequenceI[seqmaps.size()][]);
               final String fileloc = newFileLoc.toString(), vid = sviewid;
               final AlignFrame alf = af;
@@ -2816,6 +3053,7 @@ public class Jalview2XML
               {
                 javax.swing.SwingUtilities.invokeAndWait(new Runnable()
                 {
+                  @Override
                   public void run()
                   {
                     AppJmol sview = null;
@@ -2824,6 +3062,7 @@ public class Jalview2XML
                       sview = new AppJmol(pdbf, id, sq, alf.alignPanel,
                               useinJmolsuperpos, usetoColourbyseq,
                               jmolColouring, fileloc, rect, vid);
+                      addNewStructureViewer(sview);
                     } catch (OutOfMemoryError ex)
                     {
                       new OOMWarning("restoring structure view for PDB id "
@@ -2862,30 +3101,29 @@ public class Jalview2XML
               // viewer
               Object[] filedat = oldFiles.get(id);
               String pdbFile = (String) filedat[0];
-              SequenceI[] seq = (SequenceI[]) ((Vector<SequenceI>) filedat[2])
+              SequenceI[] seq = ((Vector<SequenceI>) filedat[2])
                       .toArray(new SequenceI[0]);
-              ((AppJmol) comp).jmb.ssm.setMapping(seq, null, pdbFile,
+              comp.jmb.ssm.setMapping(seq, null, pdbFile,
                       jalview.io.AppletFormatAdapter.FILE);
-              ((AppJmol) comp).jmb.addSequenceForStructFile(pdbFile, seq);
+              comp.jmb.addSequenceForStructFile(pdbFile, seq);
             }
             // and add the AlignmentPanel's reference to the Jmol view
-            ((AppJmol) comp).addAlignmentPanel(ap);
+            comp.addAlignmentPanel(ap);
             if (useinJmolsuperpos)
             {
-              ((AppJmol) comp).useAlignmentPanelForSuperposition(ap);
+              comp.useAlignmentPanelForSuperposition(ap);
             }
             else
             {
-              ((AppJmol) comp).excludeAlignmentPanelForSuperposition(ap);
+              comp.excludeAlignmentPanelForSuperposition(ap);
             }
             if (usetoColourbyseq)
             {
-              ((AppJmol) comp).useAlignmentPanelForColourbyseq(ap,
-                      !jmolColouring);
+              comp.useAlignmentPanelForColourbyseq(ap, !jmolColouring);
             }
             else
             {
-              ((AppJmol) comp).excludeAlignmentPanelForColourbyseq(ap);
+              comp.excludeAlignmentPanelForColourbyseq(ap);
             }
           }
         }
@@ -2894,6 +3132,27 @@ public class Jalview2XML
     // and finally return.
     return af;
   }
+  Vector<AppJmol> newStructureViewers=null;
+  protected void addNewStructureViewer(AppJmol sview)
+  {
+    if (newStructureViewers!=null)
+    {
+      sview.jmb.setFinishedLoadingFromArchive(false);
+      newStructureViewers.add(sview);
+    }
+  }
+  protected void setLoadingFinishedForNewStructureViewers()
+  {
+    if (newStructureViewers!=null)
+    {
+      for (AppJmol sview:newStructureViewers)
+      {
+        sview.jmb.setFinishedLoadingFromArchive(true);
+      }
+      newStructureViewers.clear();
+      newStructureViewers=null;
+    }
+  }
 
   AlignFrame loadViewport(String file, JSeq[] JSEQ, Vector hiddenSeqs,
           Alignment al, JalviewModelSequence jms, Viewport view,
@@ -2908,8 +3167,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();
@@ -2919,7 +3178,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
@@ -3009,18 +3268,18 @@ public class Jalview2XML
       else if (view.getBgColour().startsWith("Annotation"))
       {
         // int find annotation
-        if (af.viewport.alignment.getAlignmentAnnotation() != null)
+        if (af.viewport.getAlignment().getAlignmentAnnotation() != null)
         {
-          for (int i = 0; i < af.viewport.alignment
+          for (int i = 0; i < af.viewport.getAlignment()
                   .getAlignmentAnnotation().length; i++)
           {
-            if (af.viewport.alignment.getAlignmentAnnotation()[i].label
+            if (af.viewport.getAlignment().getAlignmentAnnotation()[i].label
                     .equals(view.getAnnotationColours().getAnnotation()))
             {
-              if (af.viewport.alignment.getAlignmentAnnotation()[i]
+              if (af.viewport.getAlignment().getAlignmentAnnotation()[i]
                       .getThreshold() == null)
               {
-                af.viewport.alignment.getAlignmentAnnotation()[i]
+                af.viewport.getAlignment().getAlignmentAnnotation()[i]
                         .setThreshold(new jalview.datamodel.GraphLine(view
                                 .getAnnotationColours().getThreshold(),
                                 "Threshold", java.awt.Color.black)
@@ -3031,8 +3290,8 @@ public class Jalview2XML
               if (view.getAnnotationColours().getColourScheme()
                       .equals("None"))
               {
-                cs = new AnnotationColourGradient(
-                        af.viewport.alignment.getAlignmentAnnotation()[i],
+                cs = new AnnotationColourGradient(af.viewport
+                        .getAlignment().getAlignmentAnnotation()[i],
                         new java.awt.Color(view.getAnnotationColours()
                                 .getMinColour()), new java.awt.Color(view
                                 .getAnnotationColours().getMaxColour()),
@@ -3041,28 +3300,35 @@ public class Jalview2XML
               else if (view.getAnnotationColours().getColourScheme()
                       .startsWith("ucs"))
               {
-                cs = new AnnotationColourGradient(
-                        af.viewport.alignment.getAlignmentAnnotation()[i],
+                cs = new AnnotationColourGradient(af.viewport
+                        .getAlignment().getAlignmentAnnotation()[i],
                         GetUserColourScheme(jms, view
                                 .getAnnotationColours().getColourScheme()),
                         view.getAnnotationColours().getAboveThreshold());
               }
               else
               {
-                cs = new AnnotationColourGradient(
-                        af.viewport.alignment.getAlignmentAnnotation()[i],
+                cs = new AnnotationColourGradient(af.viewport
+                        .getAlignment().getAlignmentAnnotation()[i],
                         ColourSchemeProperty.getColour(al, view
                                 .getAnnotationColours().getColourScheme()),
                         view.getAnnotationColours().getAboveThreshold());
               }
-
+              if (view.getAnnotationColours().hasPerSequence())
+              {
+                ((AnnotationColourGradient)cs).setSeqAssociated(view.getAnnotationColours().isPerSequence());
+              }
+              if (view.getAnnotationColours().hasPredefinedColours())
+              {
+                ((AnnotationColourGradient)cs).setPredefinedColours(view.getAnnotationColours().isPredefinedColours());
+              }
               // 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);
+                  jalview.datamodel.SequenceGroup sg = al.getGroups()
+                          .get(g);
 
                   if (sg.cs == null)
                   {
@@ -3073,7 +3339,7 @@ public class Jalview2XML
                    * if
                    * (view.getAnnotationColours().getColourScheme().equals("None"
                    * )) { sg.cs = new AnnotationColourGradient(
-                   * af.viewport.alignment.getAlignmentAnnotation()[i], new
+                   * af.viewport.getAlignment().getAlignmentAnnotation()[i], new
                    * java.awt.Color(view.getAnnotationColours().
                    * getMinColour()), new
                    * java.awt.Color(view.getAnnotationColours().
@@ -3081,10 +3347,21 @@ public class Jalview2XML
                    * view.getAnnotationColours().getAboveThreshold()); } else
                    */
                   {
-                    sg.cs = new AnnotationColourGradient(
-                            af.viewport.alignment.getAlignmentAnnotation()[i],
+                    sg.cs = new AnnotationColourGradient(af.viewport
+                            .getAlignment().getAlignmentAnnotation()[i],
                             sg.cs, view.getAnnotationColours()
                                     .getAboveThreshold());
+                    if (cs instanceof AnnotationColourGradient) 
+                    {
+                      if (view.getAnnotationColours().hasPerSequence())
+                      { 
+                        ((AnnotationColourGradient)cs).setSeqAssociated(view.getAnnotationColours().isPerSequence());
+                      }
+                      if (view.getAnnotationColours().hasPredefinedColours())
+                      {
+                        ((AnnotationColourGradient)cs).setPredefinedColours(view.getAnnotationColours().isPredefinedColours());
+                      }
+                    }
                   }
 
                 }
@@ -3104,7 +3381,7 @@ public class Jalview2XML
       if (cs != null)
       {
         cs.setThreshold(view.getPidThreshold(), true);
-        cs.setConsensus(af.viewport.hconsensus);
+        cs.setConsensus(af.viewport.getSequenceConsensusHash());
       }
     }
 
@@ -3130,8 +3407,8 @@ public class Jalview2XML
     }
     if (view.hasIgnoreGapsinConsensus())
     {
-      af.viewport.ignoreGapsInConsensusCalculation = view
-              .getIgnoreGapsinConsensus();
+      af.viewport.setIgnoreGapsConsensus(view.getIgnoreGapsinConsensus(),
+              null);
     }
     if (view.hasFollowHighlight())
     {
@@ -3152,11 +3429,15 @@ public class Jalview2XML
     }
     if (view.hasShowSequenceLogo())
     {
-      af.viewport.showSequenceLogo = view.getShowSequenceLogo();
+      af.viewport.setShowSequenceLogo(view.getShowSequenceLogo());
     }
     else
     {
-      af.viewport.showSequenceLogo = false;
+      af.viewport.setShowSequenceLogo(false);
+    }
+    if (view.hasNormaliseSequenceLogo())
+    {
+      af.viewport.setNormaliseSequenceLogo(view.getNormaliseSequenceLogo());
     }
     if (view.hasShowDbRefTooltip())
     {
@@ -3257,13 +3538,30 @@ 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);
     // TODO: we don't need to do this if the viewport is aready visible.
     Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(),
             view.getHeight());
-    af.alignPanel.updateAnnotation(false); // recompute any autoannotation
+    af.alignPanel.updateAnnotation(false, true); // recompute any autoannotation
     reorderAutoannotation(af, al, autoAlan);
+    af.alignPanel.alignmentChanged();
     return af;
   }
 
@@ -3287,21 +3585,34 @@ public class Jalview2XML
       }
       for (JvAnnotRow auan : autoAlan)
       {
-        visan.put(auan.template.label, auan);
+        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)
         {
-          JvAnnotRow valan = visan.get(jalan.label);
+          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(al.getAlignmentAnnotation()[h], false);
+            al.deleteAnnotation(jalan, false);
+            remains.remove(k);
             hSize--;
             h--;
             if (valan != nullAnnot)
@@ -3323,6 +3634,18 @@ public class Jalview2XML
           }
         }
       }
+      // 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)
@@ -3447,7 +3770,7 @@ public class Jalview2XML
     jalview.datamodel.SequenceI dsq = null;
     if (sq != null && sq.getDatasetSequence() != null)
     {
-      dsq = (jalview.datamodel.SequenceI) sq.getDatasetSequence();
+      dsq = sq.getDatasetSequence();
     }
 
     String sqid = vamsasSeq.getDsseqid();
@@ -3487,6 +3810,20 @@ 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);
+              }
+            }
+          }
         }
       }
     }
@@ -3513,11 +3850,11 @@ 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" : ""));
       }
@@ -3720,11 +4057,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;
@@ -3734,7 +4071,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;
 
@@ -3743,6 +4080,7 @@ public class Jalview2XML
    * 
    * @see java.lang.Object#finalize()
    */
+  @Override
   protected void finalize() throws Throwable
   {
     // really make sure we have no buried refs left.