Resolved diffs from 2.8.3
[jalview.git] / src / jalview / gui / Jalview2XML.java
index 917b6d9..5ed494e 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
- * Copyright (C) 2014 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
  */
 package jalview.gui;
 
+import java.awt.Rectangle;
+import java.io.BufferedReader;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+import java.util.jar.JarOutputStream;
+
+import javax.swing.JInternalFrame;
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+
+import org.exolab.castor.xml.Unmarshaller;
+
 import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
 import jalview.datamodel.AlignedCodonFrame;
@@ -83,44 +121,6 @@ import jalview.ws.params.ArgumentI;
 import jalview.ws.params.AutoCalcSetting;
 import jalview.ws.params.WsParamSetI;
 
-import java.awt.Rectangle;
-import java.io.BufferedReader;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.lang.reflect.InvocationTargetException;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Hashtable;
-import java.util.IdentityHashMap;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.StringTokenizer;
-import java.util.Vector;
-import java.util.jar.JarEntry;
-import java.util.jar.JarInputStream;
-import java.util.jar.JarOutputStream;
-
-import javax.swing.JInternalFrame;
-import javax.swing.JOptionPane;
-import javax.swing.SwingUtilities;
-
-import org.exolab.castor.xml.Unmarshaller;
-
 /**
  * Write out the current jalview desktop state as a Jalview XML stream.
  * 
@@ -150,6 +150,12 @@ public class Jalview2XML
 
   boolean raiseGUI = true; // whether errors are raised in dialog boxes or not
 
+  /*
+   * Map of reconstructed AlignFrame objects that appear to have come from
+   * SplitFrame objects (have a dna/protein complement view).
+   */
+  private Map<Viewport, AlignFrame> splitFrameCandidates = new HashMap<Viewport, AlignFrame>();
+
   /**
    * create/return unique hash string for sq
    * 
@@ -298,12 +304,12 @@ public class Jalview2XML
   }
 
   /**
-   * This maintains a list of viewports, the key being the seqSetId. Important
-   * to set historyItem and redoList for multiple views
+   * This maintains a map of viewports, the key being the seqSetId. Important to
+   * set historyItem and redoList for multiple views
    */
-  Hashtable viewportsAdded;
+  Map<String, AlignViewport> viewportsAdded = new HashMap<String, AlignViewport>();
 
-  Hashtable annotationIds = new Hashtable();
+  Map<String, AlignmentAnnotation> annotationIds = new HashMap<String, AlignmentAnnotation>();
 
   String uniqueSetSuffix = "";
 
@@ -676,9 +682,9 @@ public class Jalview2XML
         }
       }
 
-      if (jdatasq.getSequenceFeatures() != null)
+      if (jds.getSequenceFeatures() != null)
       {
-        jalview.datamodel.SequenceFeature[] sf = jdatasq
+        jalview.datamodel.SequenceFeature[] sf = jds
                 .getSequenceFeatures();
         int index = 0;
         while (index < sf.length)
@@ -1042,23 +1048,22 @@ public class Jalview2XML
       view.setSequenceSetId(makeHashCode(av.getSequenceSetId(),
               av.getSequenceSetId()));
       view.setId(av.getViewId());
-      view.setViewName(av.viewName);
-      view.setGatheredViews(av.gatherViewsHere);
-
-      if (ap.av.explodedPosition != null)
+      if (av.getCodingComplement() != null)
       {
-        view.setXpos(av.explodedPosition.x);
-        view.setYpos(av.explodedPosition.y);
-        view.setWidth(av.explodedPosition.width);
-        view.setHeight(av.explodedPosition.height);
+        view.setComplementId(av.getCodingComplement().getViewId());
       }
-      else
+      view.setViewName(av.viewName);
+      view.setGatheredViews(av.isGatherViewsHere());
+
+      Rectangle position = ap.av.getExplodedGeometry();
+      if (position == null)
       {
-        view.setXpos(ap.alignFrame.getBounds().x);
-        view.setYpos(ap.alignFrame.getBounds().y);
-        view.setWidth(ap.alignFrame.getBounds().width);
-        view.setHeight(ap.alignFrame.getBounds().height);
+        position = ap.alignFrame.getBounds();
       }
+      view.setXpos(position.x);
+      view.setYpos(position.y);
+      view.setWidth(position.width);
+      view.setHeight(position.height);
 
       view.setStartRes(av.startRes);
       view.setStartSeq(av.startSeq);
@@ -1108,7 +1113,7 @@ public class Jalview2XML
       view.setFontName(av.font.getName());
       view.setFontSize(av.font.getSize());
       view.setFontStyle(av.font.getStyle());
-      view.setRenderGaps(av.renderGaps);
+      view.setRenderGaps(av.isRenderGaps());
       view.setShowAnnotation(av.isShowAnnotation());
       view.setShowBoxes(av.getShowBoxes());
       view.setShowColourText(av.getColourText());
@@ -1118,19 +1123,19 @@ public class Jalview2XML
       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.setTextCol1(av.getTextColour().getRGB());
+      view.setTextCol2(av.getTextColour2().getRGB());
+      view.setTextColThreshold(av.getThresholdTextColour());
       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.setShowNPfeatureTooltip(av.isShowNPFeats());
+      view.setShowDbRefTooltip(av.isShowDBRefs());
       view.setFollowHighlight(av.followHighlight);
       view.setFollowSelection(av.followSelection);
-      view.setIgnoreGapsinConsensus(av.getIgnoreGapsConsensus());
+      view.setIgnoreGapsinConsensus(av.isIgnoreGapsConsensus());
       if (av.getFeaturesDisplayed() != null)
       {
         jalview.schemabinding.version2.FeatureSettings fs = new jalview.schemabinding.version2.FeatureSettings();
@@ -1653,7 +1658,7 @@ public class Jalview2XML
       }
     }
     throw new Error(MessageManager.formatMessage(
-            "error.unsupported_version_calcIdparam", new String[]
+            "error.unsupported_version_calcIdparam", new Object[]
             { calcIdParam.toString() }));
   }
 
@@ -1776,20 +1781,20 @@ public class Jalview2XML
       mp = new Mapping();
 
       jalview.util.MapList mlst = jmp.getMap();
-      int r[] = mlst.getFromRanges();
-      for (int s = 0; s < r.length; s += 2)
+      List<int[]> r = mlst.getFromRanges();
+      for (int[] range : r)
       {
         MapListFrom mfrom = new MapListFrom();
-        mfrom.setStart(r[s]);
-        mfrom.setEnd(r[s + 1]);
+        mfrom.setStart(range[0]);
+        mfrom.setEnd(range[1]);
         mp.addMapListFrom(mfrom);
       }
       r = mlst.getToRanges();
-      for (int s = 0; s < r.length; s += 2)
+      for (int[] range : r)
       {
         MapListTo mto = new MapListTo();
-        mto.setStart(r[s]);
-        mto.setEnd(r[s + 1]);
+        mto.setStart(range[0]);
+        mto.setEnd(range[1]);
         mp.addMapListTo(mto);
       }
       mp.setMapFromUnit(mlst.getFromRatio());
@@ -1986,7 +1991,7 @@ public class Jalview2XML
     errorMessage = null;
     uniqueSetSuffix = null;
     seqRefIds = null;
-    viewportsAdded = null;
+    viewportsAdded.clear();
     frefedSequence = null;
 
     if (file.startsWith("http://"))
@@ -2038,17 +2043,13 @@ public class Jalview2XML
     {
       seqRefIds = new HashMap<String, SequenceI>();
     }
-    if (viewportsAdded == null)
-    {
-      viewportsAdded = new Hashtable();
-    }
     if (frefedSequence == null)
     {
       frefedSequence = new Vector();
     }
 
-    jalview.gui.AlignFrame af = null, _af = null;
-    Hashtable gatherToThisFrame = new Hashtable();
+    AlignFrame af = null, _af = null;
+    Map<String, AlignFrame> gatherToThisFrame = new HashMap<String, AlignFrame>();
     final String file = jprovider.getFilename();
     try
     {
@@ -2078,7 +2079,7 @@ public class Jalview2XML
             if (object.getJalviewModelSequence().getViewportCount() > 0)
             {
               af = _af;
-              if (af.viewport.gatherViewsHere)
+              if (af.viewport.isGatherViewsHere())
               {
                 gatherToThisFrame.put(af.viewport.getSequenceSetId(), af);
               }
@@ -2148,11 +2149,20 @@ public class Jalview2XML
       Desktop.instance.stopLoading();
     }
 
-    Enumeration en = gatherToThisFrame.elements();
-    while (en.hasMoreElements())
+    /*
+     * Regather multiple views (with the same sequence set id) to the frame (if
+     * any) that is flagged as the one to gather to, i.e. convert them to tabbed
+     * views instead of separate frames. Note this doesn't restore a state where
+     * some expanded views in turn have tabbed views - the last "first tab" read
+     * in will play the role of gatherer for all.
+     */
+    for (AlignFrame fr : gatherToThisFrame.values())
     {
-      Desktop.instance.gatherViews((AlignFrame) en.nextElement());
+      Desktop.instance.gatherViews(fr);
     }
+
+    restoreSplitFrames();
+
     if (errorMessage != null)
     {
       reportErrors();
@@ -2161,6 +2171,109 @@ public class Jalview2XML
   }
 
   /**
+   * Try to reconstruct and display SplitFrame windows, where each contains
+   * complementary dna and protein alignments. Done by pairing up AlignFrame
+   * objects (created earlier) which have complementary viewport ids associated.
+   */
+  protected void restoreSplitFrames()
+  {
+    List<SplitFrame> gatherTo = new ArrayList<SplitFrame>();
+    List<AlignFrame> addedToSplitFrames = new ArrayList<AlignFrame>();
+    Map<String, AlignFrame> dna = new HashMap<String, AlignFrame>();
+
+    /*
+     * Identify the DNA alignments
+     */
+    for (Entry<Viewport, AlignFrame> candidate : splitFrameCandidates
+            .entrySet())
+    {
+      AlignFrame af = candidate.getValue();
+      if (af.getViewport().getAlignment().isNucleotide())
+      {
+        dna.put(candidate.getKey().getId(), af);
+      }
+    }
+
+    /*
+     * Try to match up the protein complements
+     */
+    for (Entry<Viewport, AlignFrame> candidate : splitFrameCandidates
+            .entrySet())
+    {
+      AlignFrame af = candidate.getValue();
+      if (!af.getViewport().getAlignment().isNucleotide())
+      {
+        String complementId = candidate.getKey().getComplementId();
+        // only non-null complements should be in the Map
+        if (complementId != null && dna.containsKey(complementId))
+        {
+          final AlignFrame dnaFrame = dna.get(complementId);
+          SplitFrame sf = createSplitFrame(dnaFrame, af);
+          addedToSplitFrames.add(dnaFrame);
+          addedToSplitFrames.add(af);
+          if (af.viewport.isGatherViewsHere())
+          {
+            gatherTo.add(sf);
+          }
+        }
+      }
+    }
+
+    /*
+     * Open any that we failed to pair up (which shouldn't happen!) as
+     * standalone AlignFrame's.
+     */
+    for (Entry<Viewport, AlignFrame> candidate : splitFrameCandidates
+            .entrySet())
+    {
+      AlignFrame af = candidate.getValue();
+      if (!addedToSplitFrames.contains(af)) {
+        Viewport view = candidate.getKey();
+        Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(),
+                view.getHeight());
+        System.err.println("Failed to restore view " + view.getTitle()
+                + " to split frame");
+      }
+    }
+
+    /*
+     * Gather back into tabbed views as flagged.
+     */
+    for (SplitFrame sf : gatherTo)
+    {
+      Desktop.instance.gatherViews(sf);
+    }
+
+    splitFrameCandidates.clear();
+  }
+
+  /**
+   * Construct and display one SplitFrame holding DNA and protein alignments.
+   * 
+   * @param dnaFrame
+   * @param proteinFrame
+   * @return
+   */
+  protected SplitFrame createSplitFrame(AlignFrame dnaFrame,
+          AlignFrame proteinFrame)
+  {
+    SplitFrame splitFrame = new SplitFrame(dnaFrame, proteinFrame);
+    String title = MessageManager.getString("label.linked_view_title");
+    int width = (int) dnaFrame.getBounds().getWidth();
+    int height = (int) (dnaFrame.getBounds().getHeight()
+            + proteinFrame.getBounds().getHeight() + 50);
+    Desktop.addInternalFrame(splitFrame, title, width, height);
+
+    /*
+     * And compute cDNA consensus (couldn't do earlier with consensus as
+     * mappings were not yet present)
+     */
+    proteinFrame.viewport.alignmentChanged(proteinFrame.alignPanel);
+
+    return splitFrame;
+  }
+
+  /**
    * check errorMessage for a valid error message and raise an error box in the
    * GUI or write the current errorMessage to stderr and then clear the error
    * state.
@@ -2197,7 +2310,7 @@ public class Jalview2XML
     errorMessage = null;
   }
 
-  Hashtable<String, String> alreadyLoadedPDB;
+  Map<String, String> alreadyLoadedPDB = new HashMap<String, String>();
 
   /**
    * when set, local views will be updated from view stored in JalviewXML
@@ -2208,11 +2321,6 @@ public class Jalview2XML
 
   String loadPDBFile(jarInputStreamProvider jprovider, String pdbId)
   {
-    if (alreadyLoadedPDB == null)
-    {
-      alreadyLoadedPDB = new Hashtable();
-    }
-
     if (alreadyLoadedPDB.containsKey(pdbId))
     {
       return alreadyLoadedPDB.get(pdbId).toString();
@@ -2314,10 +2422,10 @@ public class Jalview2XML
     // ////////////////////////////////
     // LOAD SEQUENCES
 
-    Vector hiddenSeqs = null;
+    List<SequenceI> hiddenSeqs = null;
     jalview.datamodel.Sequence jseq;
 
-    ArrayList tmpseqs = new ArrayList();
+    List<SequenceI> tmpseqs = new ArrayList<SequenceI>();
 
     boolean multipleView = false;
 
@@ -2349,10 +2457,10 @@ public class Jalview2XML
       {
         if (hiddenSeqs == null)
         {
-          hiddenSeqs = new Vector();
+          hiddenSeqs = new ArrayList<SequenceI>();
         }
 
-        hiddenSeqs.addElement(seqRefIds.get(seqId));
+        hiddenSeqs.add(seqRefIds.get(seqId));
       }
 
     }
@@ -2360,13 +2468,10 @@ public class Jalview2XML
     // /
     // Create the alignment object from the sequence set
     // ///////////////////////////////
-    jalview.datamodel.Sequence[] orderedSeqs = new jalview.datamodel.Sequence[tmpseqs
-            .size()];
+    SequenceI[] orderedSeqs = tmpseqs
+            .toArray(new SequenceI[tmpseqs.size()]);
 
-    tmpseqs.toArray(orderedSeqs);
-
-    jalview.datamodel.Alignment al = new jalview.datamodel.Alignment(
-            orderedSeqs);
+    Alignment al = new Alignment(orderedSeqs);
 
     // / Add the alignment properties
     for (int i = 0; i < vamsasSet.getSequenceSetPropertiesCount(); i++)
@@ -2393,7 +2498,7 @@ public class Jalview2XML
     }
     // ///////////////////////////////
 
-    Hashtable pdbloaded = new Hashtable();
+    Hashtable pdbloaded = new Hashtable(); // TODO nothing writes to this??
     if (!multipleView)
     {
       // load sequence features, database references and any associated PDB
@@ -2501,7 +2606,7 @@ public class Jalview2XML
 
     // ////////////////////////////////
     // LOAD ANNOTATIONS
-    ArrayList<JvAnnotRow> autoAlan = new ArrayList<JvAnnotRow>();
+    List<JvAnnotRow> autoAlan = new ArrayList<JvAnnotRow>();
     /**
      * store any annotations which forward reference a group's ID
      */
@@ -2540,8 +2645,7 @@ public class Jalview2XML
         if (an[i].getId() != null
                 && annotationIds.containsKey(an[i].getId()))
         {
-          jalview.datamodel.AlignmentAnnotation jda = (jalview.datamodel.AlignmentAnnotation) annotationIds
-                  .get(an[i].getId());
+          AlignmentAnnotation jda = annotationIds.get(an[i].getId());
           // in principle Visible should always be true for annotation displayed
           // in multiple views
           if (an[i].hasVisible())
@@ -3582,10 +3686,10 @@ public class Jalview2XML
     }
   }
 
-  AlignFrame loadViewport(String file, JSeq[] JSEQ, Vector hiddenSeqs,
-          Alignment al, JalviewModelSequence jms, Viewport view,
-          String uniqueSeqSetId, String viewId,
-          ArrayList<JvAnnotRow> autoAlan)
+  AlignFrame loadViewport(String file, JSeq[] JSEQ,
+          List<SequenceI> hiddenSeqs, Alignment al,
+          JalviewModelSequence jms, Viewport view, String uniqueSeqSetId,
+          String viewId, List<JvAnnotRow> autoAlan)
   {
     AlignFrame af = null;
     af = new AlignFrame(al, view.getWidth(), view.getHeight(),
@@ -3599,12 +3703,11 @@ public class Jalview2XML
               .getSequenceAt(i), new java.awt.Color(JSEQ[i].getColour()));
     }
 
-    af.viewport.gatherViewsHere = view.getGatheredViews();
+    af.viewport.setGatherViewsHere(view.getGatheredViews());
 
     if (view.getSequenceSetId() != null)
     {
-      jalview.gui.AlignViewport av = (jalview.gui.AlignViewport) viewportsAdded
-              .get(uniqueSeqSetId);
+      AlignmentViewport av = viewportsAdded.get(uniqueSeqSetId);
 
       af.viewport.setSequenceSetId(uniqueSeqSetId);
       if (av != null)
@@ -3636,14 +3739,17 @@ public class Jalview2XML
         af.viewport.hideRepSequences(al.getSequenceAt(s), hidden);
       }
 
-      jalview.datamodel.SequenceI[] hseqs = new jalview.datamodel.SequenceI[hiddenSeqs
-              .size()];
-
-      for (int s = 0; s < hiddenSeqs.size(); s++)
-      {
-        hseqs[s] = (jalview.datamodel.SequenceI) hiddenSeqs.elementAt(s);
-      }
+      // jalview.datamodel.SequenceI[] hseqs = new
+      // jalview.datamodel.SequenceI[hiddenSeqs
+      // .size()];
+      //
+      // for (int s = 0; s < hiddenSeqs.size(); s++)
+      // {
+      // hseqs[s] = (jalview.datamodel.SequenceI) hiddenSeqs.elementAt(s);
+      // }
 
+      SequenceI[] hseqs = hiddenSeqs.toArray(new SequenceI[hiddenSeqs
+              .size()]);
       af.viewport.hideSequence(hseqs);
 
     }
@@ -3664,27 +3770,27 @@ public class Jalview2XML
     af.viewport.setConservationSelected(view.getConservationSelected());
     af.viewport.setShowJVSuffix(view.getShowFullId());
     af.viewport.setRightAlignIds(view.getRightAlignIds());
-    af.viewport.setFont(new java.awt.Font(view.getFontName(), view
-            .getFontStyle(), view.getFontSize()));
-    af.alignPanel.fontChanged();
+    af.viewport.setFont(
+            new java.awt.Font(view.getFontName(), view.getFontStyle(), view
+                    .getFontSize()), true);
+    // TODO: allow custom charWidth/Heights to be restored by updating them
+    // after setting font - which means set above to false
     af.viewport.setRenderGaps(view.getRenderGaps());
     af.viewport.setWrapAlignment(view.getWrapAlignment());
-    af.alignPanel.setWrapAlignment(view.getWrapAlignment());
     af.viewport.setShowAnnotation(view.getShowAnnotation());
-    af.alignPanel.setAnnotationVisible(view.getShowAnnotation());
 
     af.viewport.setShowBoxes(view.getShowBoxes());
 
     af.viewport.setShowText(view.getShowText());
 
-    af.viewport.textColour = new java.awt.Color(view.getTextCol1());
-    af.viewport.textColour2 = new java.awt.Color(view.getTextCol2());
-    af.viewport.thresholdTextColour = view.getTextColThreshold();
+    af.viewport.setTextColour(new java.awt.Color(view.getTextCol1()));
+    af.viewport.setTextColour2(new java.awt.Color(view.getTextCol2()));
+    af.viewport.setThresholdTextColour(view.getTextColThreshold());
     af.viewport.setShowUnconserved(view.hasShowUnconserved() ? view
             .isShowUnconserved() : false);
     af.viewport.setStartRes(view.getStartRes());
     af.viewport.setStartSeq(view.getStartSeq());
-
+    af.alignPanel.updateLayout();
     ColourSchemeI cs = null;
     // apply colourschemes
     if (view.getBgColour() != null)
@@ -3767,11 +3873,11 @@ public class Jalview2XML
     }
     if (view.hasShowDbRefTooltip())
     {
-      af.viewport.setShowDbRefs(view.getShowDbRefTooltip());
+      af.viewport.setShowDBRefs(view.getShowDbRefTooltip());
     }
     if (view.hasShowNPfeatureTooltip())
     {
-      af.viewport.setShowNpFeats(view.hasShowNPfeatureTooltip());
+      af.viewport.setShowNPFeats(view.hasShowNPfeatureTooltip());
     }
     if (view.hasShowGroupConsensus())
     {
@@ -3892,12 +3998,27 @@ public class Jalview2XML
       }
     }
     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, true); // recompute any autoannotation
-    reorderAutoannotation(af, al, autoAlan);
-    af.alignPanel.alignmentChanged();
+    /*
+     * Add the AlignFrame to the desktop (it may be 'gathered' later), unless it
+     * has a 'cdna/protein complement' view, in which case save it in order to
+     * populate a SplitFrame once all views have been read in.
+     */
+    String complementaryViewId = view.getComplementId();
+    if (complementaryViewId == null)
+    {
+      Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(),
+              view.getHeight());
+      // recompute any autoannotation
+      af.alignPanel.updateAnnotation(false, true);
+      reorderAutoannotation(af, al, autoAlan);
+      af.alignPanel.alignmentChanged();
+    }
+    else
+    {
+      splitFrameCandidates.put(view, af);
+    }
     return af;
   }
 
@@ -4027,7 +4148,7 @@ public class Jalview2XML
   }
 
   private void reorderAutoannotation(AlignFrame af, Alignment al,
-          ArrayList<JvAnnotRow> autoAlan)
+          List<JvAnnotRow> autoAlan)
   {
     // copy over visualization settings for autocalculated annotation in the
     // view
@@ -4051,11 +4172,11 @@ public class Jalview2XML
                         + auan.template.getCalcId()), auan);
       }
       int hSize = al.getAlignmentAnnotation().length;
-      ArrayList<JvAnnotRow> reorder = new ArrayList<JvAnnotRow>();
+      List<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());
+      List<String> remains = new ArrayList<String>(visan.keySet());
       for (int h = 0; h < hSize; h++)
       {
         jalview.datamodel.AlignmentAnnotation jalan = al
@@ -4303,7 +4424,7 @@ public class Jalview2XML
     // if (pre || post)
     if (sq != dsq)
     {
-      StringBuffer sb = new StringBuffer();
+      // StringBuffer sb = new StringBuffer();
       String newres = jalview.analysis.AlignSeq.extractGaps(
               jalview.util.Comparison.GapChars, sq.getSequenceAsString());
       if (!newres.equalsIgnoreCase(dsq.getSequenceAsString())
@@ -4519,7 +4640,7 @@ public class Jalview2XML
       frefedSequence = new Vector();
     }
 
-    viewportsAdded = new Hashtable();
+    viewportsAdded.clear();
 
     AlignFrame af = loadFromObject(jm, null, false, null);
     af.alignPanels.clear();
@@ -4666,13 +4787,9 @@ public class Jalview2XML
       }
       else if (jvobj instanceof jalview.datamodel.AlignmentAnnotation)
       {
-        if (annotationIds == null)
-        {
-          annotationIds = new Hashtable();
-        }
         String anid;
-        annotationIds.put(anid = jv2vobj.get(jvobj).toString(), jvobj);
-        jalview.datamodel.AlignmentAnnotation jvann = (jalview.datamodel.AlignmentAnnotation) jvobj;
+        AlignmentAnnotation jvann = (AlignmentAnnotation) jvobj;
+        annotationIds.put(anid = jv2vobj.get(jvobj).toString(), jvann);
         if (jvann.annotationId == null)
         {
           jvann.annotationId = anid;