refactor to allow distinct StructureSelectionManager instances for
[jalview.git] / src / jalview / gui / Jalview2XML.java
old mode 100755 (executable)
new mode 100644 (file)
index c59a125..e639844
@@ -1,5 +1,5 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5)
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.6)
  * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
  * 
  * This file is part of Jalview.
@@ -19,8 +19,10 @@ package jalview.gui;
 
 import java.awt.Rectangle;
 import java.io.*;
+import java.lang.reflect.InvocationTargetException;
 import java.net.*;
 import java.util.*;
+import java.util.Map.Entry;
 import java.util.jar.*;
 
 import javax.swing.*;
@@ -30,6 +32,7 @@ import org.exolab.castor.xml.*;
 import uk.ac.vamsas.objects.utils.MapList;
 import jalview.bin.Cache;
 import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceI;
 import jalview.schemabinding.version2.*;
@@ -45,7 +48,7 @@ import jalview.util.jarInputStreamProvider;
  * will be :)
  * 
  * @author $author$
- * @version $Revision$
+ * @version $Revision: 1.134 $
  */
 public class Jalview2XML
 {
@@ -412,7 +415,7 @@ public class Jalview2XML
           JarOutputStream jout)
   {
     initSeqRefs();
-
+    Vector jmolViewIds = new Vector(); //
     Vector userColours = new Vector();
 
     AlignViewport av = ap.av;
@@ -579,32 +582,62 @@ 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)
+                  {
+                    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);
+                  }
                 }
               }
             }
@@ -803,19 +836,13 @@ public class Jalview2XML
           }
           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;
-        }
 
+        // 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);
@@ -836,6 +863,13 @@ public class Jalview2XML
         }
 
         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());
@@ -863,9 +897,8 @@ public class Jalview2XML
             ae.setPosition(a);
             if (aa[i].annotations[a].secondaryStructure != ' '
                     && aa[i].annotations[a].secondaryStructure != '\0')
-              ae
-                      .setSecondaryStructure(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)
@@ -874,6 +907,11 @@ public class Jalview2XML
             }
 
             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
@@ -964,8 +1002,8 @@ public class Jalview2XML
     // /////////SAVE VIEWPORT
     Viewport view = new Viewport();
     view.setTitle(ap.alignFrame.getTitle());
-    view.setSequenceSetId(makeHashCode(av.getSequenceSetId(), av
-            .getSequenceSetId()));
+    view.setSequenceSetId(makeHashCode(av.getSequenceSetId(),
+            av.getSequenceSetId()));
     view.setId(av.getViewId());
     view.setViewName(av.viewName);
     view.setGatheredViews(av.gatherViewsHere);
@@ -1152,10 +1190,8 @@ public class Jalview2XML
         }
         Group g = new Group();
         g.setName(grp);
-        g
-                .setDisplay(((Boolean) ap.seqPanel.seqCanvas
-                        .getFeatureRenderer().featureGroups.get(grp))
-                        .booleanValue());
+        g.setDisplay(((Boolean) ap.seqPanel.seqCanvas.getFeatureRenderer().featureGroups
+                .get(grp)).booleanValue());
         fs.addGroup(g);
         groupsAdded.addElement(grp);
       }
@@ -1797,6 +1833,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
    * 
@@ -2029,11 +2084,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)
     {
@@ -2041,22 +2096,27 @@ 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"))
-        {
-          hideQuality = false;
-          continue;
-        }
-        else if (an[i].getLabel().equals("Conservation"))
-        {
-          hideConservation = false;
-          continue;
+        /**
+         * 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"))
+        {
+          // 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("Consensus"))
-        {
-          hideConsensus = false;
-          continue;
+        if (autoForView  || (an[i].hasAutoCalculated() && an[i].isAutoCalculated())) {
+          // remove ID - we don't recover annotation from other views for
+          // view-specific annotation
+          an[i].setId(null);
         }
+
         // set visiblity for other annotation in this view
         if (an[i].getId() != null
                 && annotationIds.containsKey(an[i].getId()))
@@ -2079,7 +2139,6 @@ public class Jalview2XML
         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)
@@ -2087,10 +2146,11 @@ public class Jalview2XML
 
             anot[ae[aa].getPosition()] = 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
@@ -2100,16 +2160,20 @@ public class Jalview2XML
             // {
             // anot[ae[aa].getPosition()].displayCharacter = "";
             // }
-            anot[ae[aa].getPosition()].colour = new java.awt.Color(ae[aa]
-                    .getColour());
+            anot[ae[aa].getPosition()].colour = new java.awt.Color(
+                    ae[aa].getColour());
           }
         }
         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();
 
@@ -2121,33 +2185,45 @@ 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);
         }
-        // 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)
+        if (autoForView)
         {
-          if (al.findName(an[i].getSequenceRef()) != null)
+          // register new annotation
+          if (an[i].getId() != null)
           {
-            jaa.createSequenceMapping(al.findName(an[i].getSequenceRef()),
-                    1, true);
-            al.findName(an[i].getSequenceRef()).addAlignmentAnnotation(jaa);
+            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)
+            {
+              jaa.createSequenceMapping(
+                      al.findName(an[i].getSequenceRef()), 1, true);
+              al.findName(an[i].getSequenceRef()).addAlignmentAnnotation(
+                      jaa);
+            }
           }
         }
         // and make a note of any group association
         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())
@@ -2171,7 +2247,19 @@ public class Jalview2XML
           jaa.autoCalculated = true; // means annotation will be marked for
           // update at end of load.
         }
-        al.addAnnotation(jaa);
+        if (an[i].hasGraphHeight())
+        {
+          jaa.graphHeight = an[i].getGraphHeight();
+        }
+        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);
+        }
       }
     }
 
@@ -2227,9 +2315,7 @@ 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());
@@ -2238,14 +2324,12 @@ public class Jalview2XML
         sg.thresholdTextColour = groups[i].getTextColThreshold();
         if (groups[i].hasShowConsensusHistogram())
         {
-          sg
-                  .setShowConsensusHistogram(groups[i]
-                          .isShowConsensusHistogram());
+          sg.setShowConsensusHistogram(groups[i].isShowConsensusHistogram());
         }
         ;
         if (groups[i].hasShowSequenceLogo())
         {
-          sg.setIncludeAllConsSymbols(groups[i].isShowSequenceLogo());
+          sg.setshowSequenceLogo(groups[i].isShowSequenceLogo());
         }
         if (groups[i].hasIgnoreGapsinConsensus())
         {
@@ -2254,8 +2338,8 @@ 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);
@@ -2264,11 +2348,24 @@ public class Jalview2XML
         if (groups[i].getId() != null && groupAnnotRefs.size() > 0)
         {
           // re-instate unique group/annotation row reference
-          jalview.datamodel.AlignmentAnnotation jaa = (jalview.datamodel.AlignmentAnnotation) groupAnnotRefs
+          ArrayList<jalview.datamodel.AlignmentAnnotation> jaal = groupAnnotRefs
                   .get(groups[i].getId());
-          if (jaa != null)
+          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);
@@ -2346,9 +2443,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;
     }
@@ -2366,9 +2462,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 ?
@@ -2431,6 +2528,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)
@@ -2455,118 +2556,315 @@ 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)
               {
-                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)
-                  {
-                  }
-                  ;
-                }
-              } 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++)
+                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 = (Object[]) 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())
               {
-                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];
-                  }
+                  jmoldat[1] = ids[p].getStructureState(s).getContent();
                 }
               }
-              // 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());
+              Object[] seqstrmaps = (Object[]) ((Hashtable) jmoldat[2])
+                      .get(ids[p].getFile());
+              if (seqstrmaps == null)
+              {
+                ((Hashtable) jmoldat[2]).put(
+                        new File(ids[p].getFile()).toString(),
+                        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
+              }
+            }
+          }
+        }
+      }
+      {
 
-              jalview.datamodel.SequenceI[] seq = new jalview.datamodel.SequenceI[]
-              { (jalview.datamodel.SequenceI) seqRefIds.get(JSEQ[i].getId()
-                      + "") };
+        // 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<String, Object[]> oldFiles = (Hashtable<String, 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>();
 
-              if (comp == null)
+          // 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();
+              }
+              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)
+                      .toString());
+              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.
+            }
+            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 (String 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((SequenceI[]) ((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 = (String[]) pdbfilenames
+                      .toArray(new String[pdbfilenames.size()]), id = (String[]) pdbids
+                      .toArray(new String[pdbids.size()]);
+              final SequenceI[][] sq = (SequenceI[][]) 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()
+                {
+                  public void run()
+                  {
+                    AppJmol sview = null;
+                    try
+                    {
+                      sview = new AppJmol(pdbf, id, sq, alf.alignPanel,
+                              useinJmolsuperpos, usetoColourbyseq, jmolColouring, fileloc,
+                              rect, vid);
+                    } 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 (String 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 = (SequenceI[]) ((Vector<SequenceI>) filedat[2])
+                      .toArray(new SequenceI[0]);
+              ((AppJmol) comp).jmb.ssm.setMapping(seq, null, pdbFile,
+                              jalview.io.AppletFormatAdapter.FILE);
+              ((AppJmol) comp).jmb.addSequenceForStructFile(pdbFile, seq);
+            }
+            // and add the AlignmentPanel's reference to the Jmol view
+            ((AppJmol) comp).addAlignmentPanel(ap);
+            if (useinJmolsuperpos)
+            {
+              ((AppJmol) comp).useAlignmentPanelForSuperposition(ap);
+            }
+            else
+            {
+              ((AppJmol) comp).excludeAlignmentPanelForSuperposition(ap);
+            }
+            if (usetoColourbyseq)
+            {
+              ((AppJmol) comp).useAlignmentPanelForColourbyseq(ap, !jmolColouring);
+            }
+            else
+            {
+              ((AppJmol) comp).excludeAlignmentPanelForColourbyseq(ap);
+            }
           }
         }
       }
     }
-
+    // and finally return.
     return af;
   }
 
   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(),
@@ -2611,8 +2909,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);
       }
@@ -2628,35 +2926,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());
@@ -2717,32 +2994,32 @@ public class Jalview2XML
                         );
               }
 
-              if (view.getAnnotationColours().getColourScheme().equals(
-                      "None"))
+              if (view.getAnnotationColours().getColourScheme()
+                      .equals("None"))
               {
-                cs = new AnnotationColourGradient(af.viewport.alignment
-                        .getAlignmentAnnotation()[i], new java.awt.Color(
-                        view.getAnnotationColours().getMinColour()),
+                cs = new AnnotationColourGradient(
+                        af.viewport.alignment.getAlignmentAnnotation()[i],
                         new java.awt.Color(view.getAnnotationColours()
-                                .getMaxColour()), view
-                                .getAnnotationColours().getAboveThreshold());
+                                .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],
+                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());
+                cs = new AnnotationColourGradient(
+                        af.viewport.alignment.getAlignmentAnnotation()[i],
+                        ColourSchemeProperty.getColour(al, view
+                                .getAnnotationColours().getColourScheme()),
+                        view.getAnnotationColours().getAboveThreshold());
               }
 
               // Also use these settings for all the groups
@@ -2841,11 +3118,11 @@ public class Jalview2XML
     }
     if (view.hasShowSequenceLogo())
     {
-      af.viewport.setShowSequenceLogo(view.getShowSequenceLogo());
+      af.viewport.showSequenceLogo = view.getShowSequenceLogo();
     }
     else
     {
-      af.viewport.setShowSequenceLogo(false);
+      af.viewport.showSequenceLogo = false;
     }
     if (view.hasShowDbRefTooltip())
     {
@@ -2948,15 +3225,86 @@ public class Jalview2XML
     }
 
     af.setMenusFromViewport(af.viewport);
-    // This is done twice - why ?
-    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); // recompute any autoannotation
+    reorderAutoannotation(af,al,autoAlan);
     return af;
   }
 
+  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);
+      }
+      int hSize = al.getAlignmentAnnotation().length;
+      ArrayList<JvAnnotRow> reorder = new ArrayList<JvAnnotRow>();
+      for (int h = 0; h < hSize; h++)
+      {
+        jalview.datamodel.AlignmentAnnotation jalan = al
+                .getAlignmentAnnotation()[h];
+        if (jalan.autoCalculated)
+        {
+          JvAnnotRow valan = visan.get(jalan.label);
+          if (valan != null)
+          {
+            // delete the auto calculated row from the alignment
+            al.deleteAnnotation(al.getAlignmentAnnotation()[h],false);
+            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));
+            }
+          }
+        }
+      }
+      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;
 
   /**
@@ -3286,8 +3634,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);