JAL-845 SplitFrame for "show product" and after aligning from SplitFrame
[jalview.git] / src / jalview / gui / Jalview2XML.java
index bb4ef8b..8342151 100644 (file)
@@ -22,13 +22,15 @@ package jalview.gui;
 
 import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
+import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
+import jalview.datamodel.StructureViewerModel;
+import jalview.datamodel.StructureViewerModel.StructureData;
 import jalview.schemabinding.version2.AlcodMap;
-import jalview.schemabinding.version2.Alcodon;
 import jalview.schemabinding.version2.AlcodonFrame;
 import jalview.schemabinding.version2.Annotation;
 import jalview.schemabinding.version2.AnnotationColours;
@@ -72,6 +74,8 @@ import jalview.util.MessageManager;
 import jalview.util.Platform;
 import jalview.util.jarInputStreamProvider;
 import jalview.viewmodel.AlignmentViewport;
+import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
+import jalview.viewmodel.seqfeatures.FeaturesDisplayed;
 import jalview.ws.jws2.Jws2Discoverer;
 import jalview.ws.jws2.dm.AAConSettings;
 import jalview.ws.jws2.jabaws2.Jws2Instance;
@@ -100,6 +104,7 @@ 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;
@@ -128,47 +133,6 @@ import org.exolab.castor.xml.Unmarshaller;
  */
 public class Jalview2XML
 {
-
-  /**
-   * A data bean to hold stored data about a structure viewer.
-   */
-  public class ViewerData
-  {
-
-    private int x;
-
-    private int y;
-
-    private int width;
-
-    private int height;
-
-    public boolean alignWithPanel;
-
-    public boolean colourWithAlignPanel;
-
-    public boolean colourByViewer;
-
-    String stateData = "";
-
-    // todo: java bean in place of Object []
-    private Map<File, Object[]> fileData = new HashMap<File, Object[]>();
-
-    public ViewerData(int x, int y, int width, int height,
-            boolean alignWithPanel, boolean colourWithAlignPanel,
-            boolean colourByViewer)
-    {
-      this.x = x;
-      this.y = y;
-      this.width = width;
-      this.height = height;
-      this.alignWithPanel = alignWithPanel;
-      this.colourWithAlignPanel = colourWithAlignPanel;
-      this.colourByViewer = colourByViewer;
-    }
-
-  }
-
   /*
    * SequenceI reference -> XML ID string in jalview XML. Populated as XML reps
    * of sequence objects are created.
@@ -186,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
    * 
@@ -334,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 = "";
 
@@ -395,7 +365,7 @@ public class Jalview2XML
    */
   public void saveState(JarOutputStream jout)
   {
-    JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+    AlignFrame[] frames = Desktop.getAlignFrames(); // Desktop.desktop.getAllFrames();
 
     if (frames == null)
     {
@@ -415,67 +385,61 @@ public class Jalview2XML
       // REVERSE ORDER
       for (int i = frames.length - 1; i > -1; i--)
       {
-        if (frames[i] instanceof AlignFrame)
+        AlignFrame af = frames[i];
+        // skip ?
+        if (skipList != null
+                && skipList
+                        .containsKey(af.getViewport().getSequenceSetId()))
         {
-          AlignFrame af = (AlignFrame) frames[i];
-          // skip ?
-          if (skipList != null
-                  && skipList.containsKey(af.getViewport()
-                          .getSequenceSetId()))
-          {
-            continue;
-          }
+          continue;
+        }
 
-          String shortName = af.getTitle();
+        String shortName = af.getTitle();
 
-          if (shortName.indexOf(File.separatorChar) > -1)
+        if (shortName.indexOf(File.separatorChar) > -1)
+        {
+          shortName = shortName.substring(shortName
+                  .lastIndexOf(File.separatorChar) + 1);
+        }
+
+        int count = 1;
+
+        while (shortNames.contains(shortName))
+        {
+          if (shortName.endsWith("_" + (count - 1)))
           {
-            shortName = shortName.substring(shortName
-                    .lastIndexOf(File.separatorChar) + 1);
+            shortName = shortName.substring(0, shortName.lastIndexOf("_"));
           }
 
-          int count = 1;
+          shortName = shortName.concat("_" + count);
+          count++;
+        }
 
-          while (shortNames.contains(shortName))
-          {
-            if (shortName.endsWith("_" + (count - 1)))
-            {
-              shortName = shortName
-                      .substring(0, shortName.lastIndexOf("_"));
-            }
+        shortNames.addElement(shortName);
 
-            shortName = shortName.concat("_" + count);
-            count++;
-          }
+        if (!shortName.endsWith(".xml"))
+        {
+          shortName = shortName + ".xml";
+        }
 
-          shortNames.addElement(shortName);
+        int ap, apSize = af.alignPanels.size();
 
-          if (!shortName.endsWith(".xml"))
+        for (ap = 0; ap < apSize; ap++)
+        {
+          AlignmentPanel apanel = af.alignPanels.get(ap);
+          String fileName = apSize == 1 ? shortName : ap + shortName;
+          if (!fileName.endsWith(".xml"))
           {
-            shortName = shortName + ".xml";
+            fileName = fileName + ".xml";
           }
 
-          int ap, apSize = af.alignPanels.size();
+          saveState(apanel, fileName, jout);
 
-          for (ap = 0; ap < apSize; ap++)
+          String dssid = getDatasetIdRef(af.getViewport().getAlignment()
+                  .getDataset());
+          if (!dsses.containsKey(dssid))
           {
-            AlignmentPanel apanel = (AlignmentPanel) af.alignPanels
-                    .elementAt(ap);
-            String fileName = apSize == 1 ? shortName : ap + shortName;
-            if (!fileName.endsWith(".xml"))
-            {
-              fileName = fileName + ".xml";
-            }
-
-            saveState(apanel, fileName, jout);
-
-            String dssid = getDatasetIdRef(af.getViewport().getAlignment()
-                    .getDataset());
-            if (!dsses.containsKey(dssid))
-            {
-              dsses.put(dssid, af);
-            }
-
+            dsses.put(dssid, af);
           }
         }
       }
@@ -509,15 +473,15 @@ public class Jalview2XML
   {
     try
     {
-      int ap, apSize = af.alignPanels.size();
+      int ap = 0;
+      int apSize = af.alignPanels.size();
       FileOutputStream fos = new FileOutputStream(jarFile);
       JarOutputStream jout = new JarOutputStream(fos);
       Hashtable<String, AlignFrame> dsses = new Hashtable<String, AlignFrame>();
-      for (ap = 0; ap < apSize; ap++)
+      for (AlignmentPanel apanel : af.alignPanels)
       {
-        AlignmentPanel apanel = (AlignmentPanel) af.alignPanels
-                .elementAt(ap);
         String jfileName = apSize == 1 ? fileName : fileName + ap;
+        ap++;
         if (!jfileName.endsWith(".xml"))
         {
           jfileName = jfileName + ".xml";
@@ -816,8 +780,7 @@ public class Jalview2XML
                 {
                   byte[] data = new byte[(int) file.length()];
                   jout.putNextEntry(new JarEntry(entry.getId()));
-                  dis = new DataInputStream(
-                          new FileInputStream(file));
+                  dis = new DataInputStream(new FileInputStream(file));
                   dis.readFully(data);
 
                   DataOutputStream dout = new DataOutputStream(jout);
@@ -873,31 +836,18 @@ public class Jalview2XML
       jal = av.getAlignment();
     }
     // SAVE MAPPINGS
-    if (jal.getCodonFrames() != null && jal.getCodonFrames().length > 0)
+    if (jal.getCodonFrames() != null)
     {
-      jalview.datamodel.AlignedCodonFrame[] jac = jal.getCodonFrames();
-      for (int i = 0; i < jac.length; i++)
+      Set<AlignedCodonFrame> jac = jal.getCodonFrames();
+      for (AlignedCodonFrame acf : jac)
       {
         AlcodonFrame alc = new AlcodonFrame();
         vamsasSet.addAlcodonFrame(alc);
-        for (int p = 0; p < jac[i].aaWidth; p++)
+        if (acf.getProtMappings() != null
+                && acf.getProtMappings().length > 0)
         {
-          Alcodon cmap = new Alcodon();
-          if (jac[i].codons[p] != null)
-          {
-            // Null codons indicate a gapped column in the translated peptide
-            // alignment.
-            cmap.setPos1(jac[i].codons[p][0]);
-            cmap.setPos2(jac[i].codons[p][1]);
-            cmap.setPos3(jac[i].codons[p][2]);
-          }
-          alc.addAlcodon(cmap);
-        }
-        if (jac[i].getProtMappings() != null
-                && jac[i].getProtMappings().length > 0)
-        {
-          SequenceI[] dnas = jac[i].getdnaSeqs();
-          jalview.datamodel.Mapping[] pmaps = jac[i].getProtMappings();
+          SequenceI[] dnas = acf.getdnaSeqs();
+          jalview.datamodel.Mapping[] pmaps = acf.getProtMappings();
           for (int m = 0; m < pmaps.length; m++)
           {
             AlcodMap alcmap = new AlcodMap();
@@ -907,6 +857,37 @@ public class Jalview2XML
             alc.addAlcodMap(alcmap);
           }
         }
+
+//      {
+//        AlcodonFrame alc = new AlcodonFrame();
+//        vamsasSet.addAlcodonFrame(alc);
+//        for (int p = 0; p < acf.aaWidth; p++)
+//        {
+//          Alcodon cmap = new Alcodon();
+//          if (acf.codons[p] != null)
+//          {
+//            // Null codons indicate a gapped column in the translated peptide
+//            // alignment.
+//            cmap.setPos1(acf.codons[p][0]);
+//            cmap.setPos2(acf.codons[p][1]);
+//            cmap.setPos3(acf.codons[p][2]);
+//          }
+//          alc.addAlcodon(cmap);
+//        }
+//        if (acf.getProtMappings() != null
+//                && acf.getProtMappings().length > 0)
+//        {
+//          SequenceI[] dnas = acf.getdnaSeqs();
+//          jalview.datamodel.Mapping[] pmaps = acf.getProtMappings();
+//          for (int m = 0; m < pmaps.length; m++)
+//          {
+//            AlcodMap alcmap = new AlcodMap();
+//            alcmap.setDnasq(seqHash(dnas[m]));
+//            alcmap.setMapping(createVamsasMapping(pmaps[m], dnas[m], null,
+//                    false));
+//            alc.addAlcodMap(alcmap);
+//          }
+//        }
       }
     }
 
@@ -1067,15 +1048,20 @@ public class Jalview2XML
       view.setSequenceSetId(makeHashCode(av.getSequenceSetId(),
               av.getSequenceSetId()));
       view.setId(av.getViewId());
+      if (av.getCodingComplement() != null)
+      {
+        view.setComplementId(av.getCodingComplement().getViewId());
+      }
       view.setViewName(av.viewName);
-      view.setGatheredViews(av.gatherViewsHere);
+      view.setGatheredViews(av.isGatherViewsHere());
 
-      if (ap.av.explodedPosition != null)
+      Rectangle position = ap.av.getExplodedGeometry();
+      if (position != null)
       {
-        view.setXpos(av.explodedPosition.x);
-        view.setYpos(av.explodedPosition.y);
-        view.setWidth(av.explodedPosition.width);
-        view.setHeight(av.explodedPosition.height);
+        view.setXpos(position.x);
+        view.setYpos(position.y);
+        view.setWidth(position.width);
+        view.setHeight(position.height);
       }
       else
       {
@@ -1133,34 +1119,36 @@ public class Jalview2XML
       view.setFontName(av.font.getName());
       view.setFontSize(av.font.getSize());
       view.setFontStyle(av.font.getStyle());
-      view.setRenderGaps(av.renderGaps);
-      view.setShowAnnotation(av.getShowAnnotation());
+      view.setRenderGaps(av.isRenderGaps());
+      view.setShowAnnotation(av.isShowAnnotation());
       view.setShowBoxes(av.getShowBoxes());
       view.setShowColourText(av.getColourText());
       view.setShowFullId(av.getShowJVSuffix());
       view.setRightAlignIds(av.isRightAlignIds());
-      view.setShowSequenceFeatures(av.showSequenceFeatures);
+      view.setShowSequenceFeatures(av.isShowSequenceFeatures());
       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();
 
-        String[] renderOrder = ap.getSeqPanel().seqCanvas.getFeatureRenderer().renderOrder;
+        String[] renderOrder = ap.getSeqPanel().seqCanvas
+                .getFeatureRenderer().getRenderOrder()
+                .toArray(new String[0]);
 
         Vector settingsAdded = new Vector();
         Object gstyle = null;
@@ -1187,12 +1175,13 @@ public class Jalview2XML
             }
             else
             {
-              setting.setColour(ap.getSeqPanel().seqCanvas.getFeatureRenderer()
+              setting.setColour(ap.getSeqPanel().seqCanvas
+                      .getFeatureRenderer()
                       .getColour(renderOrder[ro]).getRGB());
             }
 
-            setting.setDisplay(av.getFeaturesDisplayed()
-                    .containsKey(renderOrder[ro]));
+            setting.setDisplay(av.getFeaturesDisplayed().isVisible(
+                    renderOrder[ro]));
             float rorder = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
                     .getOrder(renderOrder[ro]);
             if (rorder > -1)
@@ -1205,8 +1194,8 @@ public class Jalview2XML
         }
 
         // Make sure we save none displayed feature settings
-        Iterator en = ap.getSeqPanel().seqCanvas.getFeatureRenderer().featureColours
-                .keySet().iterator();
+        Iterator en = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
+                .getFeatureColours().keySet().iterator();
         while (en.hasNext())
         {
           String key = en.next().toString();
@@ -1230,8 +1219,9 @@ public class Jalview2XML
           fs.addSetting(setting);
           settingsAdded.addElement(key);
         }
-        en = ap.getSeqPanel().seqCanvas.getFeatureRenderer().featureGroups
-                .keySet().iterator();
+        // is groups actually supposed to be a map here ?
+        en = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
+                .getFeatureGroups().iterator();
         Vector groupsAdded = new Vector();
         while (en.hasNext())
         {
@@ -1243,7 +1233,7 @@ public class Jalview2XML
           Group g = new Group();
           g.setName(grp);
           g.setDisplay(((Boolean) ap.getSeqPanel().seqCanvas
-                  .getFeatureRenderer().featureGroups.get(grp))
+                  .getFeatureRenderer().checkGroupVisibility(grp, false))
                   .booleanValue());
           fs.addGroup(g);
           groupsAdded.addElement(grp);
@@ -1264,8 +1254,8 @@ public class Jalview2XML
           for (int c = 0; c < av.getColumnSelection().getHiddenColumns()
                   .size(); c++)
           {
-            int[] region = (int[]) av.getColumnSelection()
-                    .getHiddenColumns().elementAt(c);
+            int[] region = av.getColumnSelection().getHiddenColumns()
+                    .get(c);
             HiddenColumns hc = new HiddenColumns();
             hc.setStart(region[0]);
             hc.setEnd(region[1]);
@@ -1336,17 +1326,14 @@ public class Jalview2XML
           Pdbids pdb, PDBEntry entry, List<String> viewIds,
           String matchedFile, StructureViewerBase viewFrame)
   {
-    final AAStructureBindingModel bindingModel = viewFrame
-            .getBinding();
-    for (int peid = 0; peid < bindingModel
-            .getPdbCount(); peid++)
+    final AAStructureBindingModel bindingModel = viewFrame.getBinding();
+    for (int peid = 0; peid < bindingModel.getPdbCount(); peid++)
     {
       final PDBEntry pdbentry = bindingModel.getPdbEntry(peid);
       final String pdbId = pdbentry.getId();
       if (!pdbId.equals(entry.getId())
               && !(entry.getId().length() > 4 && entry.getId()
-                      .toLowerCase()
-                      .startsWith(pdbId.toLowerCase())))
+                      .toLowerCase().startsWith(pdbId.toLowerCase())))
       {
         continue;
       }
@@ -1354,8 +1341,7 @@ public class Jalview2XML
       {
         matchedFile = pdbentry.getFile();
       }
-      else if (!matchedFile.equals(pdbentry
-              .getFile()))
+      else if (!matchedFile.equals(pdbentry.getFile()))
       {
         Cache.log
                 .warn("Probably lost some PDB-Sequence mappings for this structure file (which apparently has same PDB Entry code): "
@@ -1368,8 +1354,7 @@ public class Jalview2XML
       // 1QIP==1qipA)
       String statestring = viewFrame.getStateInfo();
 
-      for (int smap = 0; smap < viewFrame.getBinding()
-              .getSequence()[peid].length; smap++)
+      for (int smap = 0; smap < viewFrame.getBinding().getSequence()[peid].length; smap++)
       {
         // if (jal.findIndex(jmol.jmb.sequence[peid][smap]) > -1)
         if (jds == viewFrame.getBinding().getSequence()[peid][smap])
@@ -1383,8 +1368,7 @@ public class Jalview2XML
           final String viewId = viewFrame.getViewId();
           state.setViewId(viewId);
           state.setAlignwithAlignPanel(viewFrame.isUsedforaligment(ap));
-          state.setColourwithAlignPanel(viewFrame
-                  .isUsedforcolourby(ap));
+          state.setColourwithAlignPanel(viewFrame.isUsedforcolourby(ap));
           state.setColourByJmol(viewFrame.isColouredByViewer());
           /*
            * Only store each structure viewer's state once in each XML document.
@@ -1526,6 +1510,7 @@ public class Jalview2XML
           an.addProperty(prop);
         }
       }
+
       AnnotationElement ae;
       if (aa[i].annotations != null)
       {
@@ -1678,7 +1663,9 @@ public class Jalview2XML
         return false;
       }
     }
-    throw new Error(MessageManager.formatMessage("error.unsupported_version_calcIdparam", new String[]{calcIdParam.toString()}));
+    throw new Error(MessageManager.formatMessage(
+            "error.unsupported_version_calcIdparam", new Object[]
+            { calcIdParam.toString() }));
   }
 
   /**
@@ -2010,7 +1997,7 @@ public class Jalview2XML
     errorMessage = null;
     uniqueSetSuffix = null;
     seqRefIds = null;
-    viewportsAdded = null;
+    viewportsAdded.clear();
     frefedSequence = null;
 
     if (file.startsWith("http://"))
@@ -2062,17 +2049,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
     {
@@ -2102,7 +2085,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);
               }
@@ -2172,11 +2155,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();
@@ -2185,6 +2177,99 @@ 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");
+    Desktop.addInternalFrame(splitFrame, title, -1, -1);
+    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.
@@ -2221,7 +2306,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
@@ -2232,11 +2317,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();
@@ -2338,10 +2418,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;
 
@@ -2373,10 +2453,10 @@ public class Jalview2XML
       {
         if (hiddenSeqs == null)
         {
-          hiddenSeqs = new Vector();
+          hiddenSeqs = new ArrayList<SequenceI>();
         }
 
-        hiddenSeqs.addElement(seqRefIds.get(seqId));
+        hiddenSeqs.add(seqRefIds.get(seqId));
       }
 
     }
@@ -2384,13 +2464,10 @@ public class Jalview2XML
     // /
     // Create the alignment object from the sequence set
     // ///////////////////////////////
-    jalview.datamodel.Sequence[] orderedSeqs = new jalview.datamodel.Sequence[tmpseqs
-            .size()];
-
-    tmpseqs.toArray(orderedSeqs);
+    SequenceI[] orderedSeqs = tmpseqs
+            .toArray(new SequenceI[tmpseqs.size()]);
 
-    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++)
@@ -2417,7 +2494,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
@@ -2476,8 +2553,7 @@ public class Jalview2XML
               }
             }
             StructureSelectionManager.getStructureSelectionManager(
-                    Desktop.instance)
-                    .registerPDBEntry(entry);
+                    Desktop.instance).registerPDBEntry(entry);
             al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
           }
         }
@@ -2494,35 +2570,13 @@ public class Jalview2XML
       AlcodonFrame[] alc = vamsasSet.getAlcodonFrame();
       for (int i = 0; i < alc.length; i++)
       {
-        jalview.datamodel.AlignedCodonFrame cf = new jalview.datamodel.AlignedCodonFrame(
-                alc[i].getAlcodonCount());
-        if (alc[i].getAlcodonCount() > 0)
-        {
-          Alcodon[] alcods = alc[i].getAlcodon();
-          for (int p = 0; p < cf.codons.length; p++)
-          {
-            if (alcods[p].hasPos1() && alcods[p].hasPos2()
-                    && alcods[p].hasPos3())
-            {
-              // translated codons require three valid positions
-              cf.codons[p] = new int[3];
-              cf.codons[p][0] = (int) alcods[p].getPos1();
-              cf.codons[p][1] = (int) alcods[p].getPos2();
-              cf.codons[p][2] = (int) alcods[p].getPos3();
-            }
-            else
-            {
-              cf.codons[p] = null;
-            }
-          }
-        }
+        AlignedCodonFrame cf = new AlignedCodonFrame();
         if (alc[i].getAlcodMapCount() > 0)
         {
           AlcodMap[] maps = alc[i].getAlcodMap();
           for (int m = 0; m < maps.length; m++)
           {
-            SequenceI dnaseq = seqRefIds
-                    .get(maps[m].getDnasq());
+            SequenceI dnaseq = seqRefIds.get(maps[m].getDnasq());
             // Load Mapping
             jalview.datamodel.Mapping mapping = null;
             // attach to dna sequence reference.
@@ -2544,12 +2598,11 @@ public class Jalview2XML
         }
         al.addCodonFrame(cf);
       }
-
     }
 
     // ////////////////////////////////
     // LOAD ANNOTATIONS
-    ArrayList<JvAnnotRow> autoAlan = new ArrayList<JvAnnotRow>();
+    List<JvAnnotRow> autoAlan = new ArrayList<JvAnnotRow>();
     /**
      * store any annotations which forward reference a group's ID
      */
@@ -2588,8 +2641,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())
@@ -2799,8 +2851,7 @@ public class Jalview2XML
         for (int s = 0; s < groups[i].getSeqCount(); s++)
         {
           String seqId = groups[i].getSeq(s) + "";
-          jalview.datamodel.SequenceI ts = seqRefIds
-                  .get(seqId);
+          jalview.datamodel.SequenceI ts = seqRefIds.get(seqId);
 
           if (ts != null)
           {
@@ -3073,9 +3124,11 @@ public class Jalview2XML
   protected void loadStructures(jarInputStreamProvider jprovider,
           JSeq[] jseqs, AlignFrame af, AlignmentPanel ap)
   {
-    // run through all PDB ids on the alignment, and collect mappings between
-    // jmol view ids and all sequences referring to it
-    Map<String, ViewerData> jmolViewIds = new HashMap<String, ViewerData>();
+    /*
+     * Run through all PDB ids on the alignment, and collect mappings between
+     * distinct view ids and all sequences referring to that view.
+     */
+    Map<String, StructureViewerModel> structureViewers = new LinkedHashMap<String, StructureViewerModel>();
 
     for (int i = 0; i < jseqs.length; i++)
     {
@@ -3088,10 +3141,10 @@ public class Jalview2XML
           for (int s = 0; s < structureStateCount; s++)
           {
             // check to see if we haven't already created this structure view
-            final StructureState structureState = ids[p].getStructureState(s);
+            final StructureState structureState = ids[p]
+                    .getStructureState(s);
             String sviewid = (structureState.getViewId() == null) ? null
-                    : structureState.getViewId()
-                            + uniqueSetSuffix;
+                    : structureState.getViewId() + uniqueSetSuffix;
             jalview.datamodel.PDBEntry jpdb = new jalview.datamodel.PDBEntry();
             // Originally : ids[p].getFile()
             // : TODO: verify external PDB file recovery still works in normal
@@ -3108,17 +3161,17 @@ public class Jalview2XML
             // 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 = seqRefIds
-                    .get(jseqs[i].getId() + "");
+            jalview.datamodel.SequenceI seq = seqRefIds.get(jseqs[i]
+                    .getId() + "");
             if (sviewid == null)
             {
               sviewid = "_jalview_pre2_4_" + x + "," + y + "," + width
                       + "," + height;
             }
-            if (!jmolViewIds.containsKey(sviewid))
+            if (!structureViewers.containsKey(sviewid))
             {
-              jmolViewIds.put(sviewid, new ViewerData(x, y, width, height,
-                      false, false, true));
+              structureViewers.put(sviewid, new StructureViewerModel(x, y,
+                      width, height, false, false, true));
               // Legacy pre-2.7 conversion JAL-823 :
               // do not assume any view has to be linked for colour by
               // sequence
@@ -3128,44 +3181,52 @@ public class Jalview2XML
             // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
             // seqs_file 2}, boolean[] {
             // linkAlignPanel,superposeWithAlignpanel}} from hash
-            ViewerData jmoldat = jmolViewIds.get(sviewid);
-            jmoldat.alignWithPanel |= structureState
-                    .hasAlignwithAlignPanel() ? structureState.getAlignwithAlignPanel() : false;
-            // never colour by linked panel if not specified
-            jmoldat.colourWithAlignPanel |= structureState
-                    .hasColourwithAlignPanel() ? structureState.getColourwithAlignPanel()
-                    : false;
-            // default for pre-2.7 projects is that Jmol colouring is enabled
-            jmoldat.colourByViewer &= structureState
-                    .hasColourByJmol() ? structureState
+            StructureViewerModel jmoldat = structureViewers.get(sviewid);
+            jmoldat.setAlignWithPanel(jmoldat.isAlignWithPanel()
+                    | (structureState.hasAlignwithAlignPanel() ? structureState
+                            .getAlignwithAlignPanel() : false));
+
+            /*
+             * Default colour by linked panel to false if not specified (e.g.
+             * for pre-2.7 projects)
+             */
+            boolean colourWithAlignPanel = jmoldat.isColourWithAlignPanel();
+            colourWithAlignPanel |= (structureState
+                    .hasColourwithAlignPanel() ? structureState
+                    .getColourwithAlignPanel() : false);
+            jmoldat.setColourWithAlignPanel(colourWithAlignPanel);
+
+            /*
+             * Default colour by viewer to true if not specified (e.g. for
+             * pre-2.7 projects)
+             */
+            boolean colourByViewer = jmoldat.isColourByViewer();
+            colourByViewer &= structureState.hasColourByJmol() ? structureState
                     .getColourByJmol() : true;
+            jmoldat.setColourByViewer(colourByViewer);
 
-            if (jmoldat.stateData.length() < structureState.getContent()
-                    .length())
+            if (jmoldat.getStateData().length() < structureState
+                    .getContent().length())
             {
               {
-                jmoldat.stateData = structureState.getContent();
+                jmoldat.setStateData(structureState.getContent());
               }
             }
             if (ids[p].getFile() != null)
             {
               File mapkey = new File(ids[p].getFile());
-              Object[] seqstrmaps = jmoldat.fileData.get(mapkey);
+              StructureData seqstrmaps = jmoldat.getFileData().get(mapkey);
               if (seqstrmaps == null)
               {
-                jmoldat.fileData.put(mapkey,
-                        seqstrmaps = new Object[]
-                        { pdbFile, ids[p].getId(), new Vector(),
-                            new Vector() });
+                jmoldat.getFileData().put(
+                        mapkey,
+                        seqstrmaps = jmoldat.new StructureData(pdbFile,
+                                ids[p].getId()));
               }
-              if (!((Vector) seqstrmaps[2]).contains(seq))
+              if (!seqstrmaps.getSeqList().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
+                seqstrmaps.getSeqList().add(seq);
+                // TODO and chains?
               }
             }
             else
@@ -3177,11 +3238,12 @@ public class Jalview2XML
         }
       }
     }
-      // Instantiate the associated structure views
-      for (Entry<String, ViewerData> entry : jmolViewIds.entrySet())
+    // Instantiate the associated structure views
+    for (Entry<String, StructureViewerModel> entry : structureViewers
+            .entrySet())
       {
-        createOrLinkStructureViewer(entry, af, ap);
-      }
+      createOrLinkStructureViewer(entry, af, ap);
+    }
   }
 
   /**
@@ -3191,10 +3253,10 @@ public class Jalview2XML
    * @param ap
    */
   protected void createOrLinkStructureViewer(
-          Entry<String, ViewerData> viewerData, AlignFrame af,
+          Entry<String, StructureViewerModel> viewerData, AlignFrame af,
           AlignmentPanel ap)
   {
-    final ViewerData svattrib = viewerData.getValue();
+    final StructureViewerModel svattrib = viewerData.getValue();
 
     /*
      * Search for any viewer windows already open from other alignment views
@@ -3212,7 +3274,7 @@ public class Jalview2XML
      * Pending an XML element for ViewerType, just check if stateData contains
      * "chimera" (part of the chimera session filename).
      */
-    if (svattrib.stateData.indexOf("chimera") > -1)
+    if (svattrib.getStateData().indexOf("chimera") > -1)
     {
       createChimeraViewer(viewerData, af);
     }
@@ -3228,40 +3290,37 @@ public class Jalview2XML
    * @param viewerData
    * @param af
    */
-  protected void createChimeraViewer(Entry<String, ViewerData> viewerData,
-          AlignFrame af)
+  protected void createChimeraViewer(
+          Entry<String, StructureViewerModel> viewerData, AlignFrame af)
   {
-    final ViewerData data = viewerData.getValue();
-    String chimeraSession = data.stateData;
-    List<String> pdbfilenames = new ArrayList<String>();
-    List<SequenceI[]> seqmaps = new ArrayList<SequenceI[]>();
-    List<String> pdbids = new ArrayList<String>();
+    final StructureViewerModel data = viewerData.getValue();
+    String chimeraSession = data.getStateData();
 
     if (new File(chimeraSession).exists())
     {
-      Set<Entry<File, Object[]>> fileData = data.fileData.entrySet();
+      Set<Entry<File, StructureData>> fileData = data.getFileData()
+              .entrySet();
       List<PDBEntry> pdbs = new ArrayList<PDBEntry>();
       List<SequenceI[]> allseqs = new ArrayList<SequenceI[]>();
-      for (Entry<File, Object[]> pdb : fileData)
-      {
-        String filePath = (String) pdb.getValue()[0];
-        String pdbId = (String) pdb.getValue()[1];
-        final Vector seqList = (Vector) pdb.getValue()[2];
-        PDBEntry pdbentry = new PDBEntry();
-        pdbentry.setFile(filePath);
-        pdbentry.setId(pdbId);
-        pdbs.add(pdbentry);
-        SequenceI[] seqs = new SequenceI[seqList.size()];
-        seqList.copyInto(seqs);
+      for (Entry<File, StructureData> pdb : fileData)
+      {
+        String filePath = pdb.getValue().getFilePath();
+        String pdbId = pdb.getValue().getPdbId();
+        pdbs.add(new PDBEntry(filePath, pdbId));
+        final List<SequenceI> seqList = pdb.getValue().getSeqList();
+        SequenceI[] seqs = seqList.toArray(new SequenceI[seqList.size()]);
         allseqs.add(seqs);
       }
 
+      boolean colourByChimera = data.isColourByViewer();
+      boolean colourBySequence = data.isColourWithAlignPanel();
+
       // TODO can/should this be done via StructureViewer (like Jmol)?
-      final PDBEntry[] pdbArray = pdbs.toArray(new PDBEntry[pdbs
-              .size()]);
-      final SequenceI[][] seqsArray = allseqs.toArray(new SequenceI[allseqs.size()][]);
-      ChimeraViewFrame cvf = // new ChimeraViewFrame(data, af);
-      new ChimeraViewFrame(af.alignPanel, pdbArray, seqsArray);
+      final PDBEntry[] pdbArray = pdbs.toArray(new PDBEntry[pdbs.size()]);
+      final SequenceI[][] seqsArray = allseqs.toArray(new SequenceI[allseqs
+              .size()][]);
+      new ChimeraViewFrame(chimeraSession, af.alignPanel, pdbArray,
+              seqsArray, colourByChimera, colourBySequence);
     }
     else
     {
@@ -3279,16 +3338,17 @@ public class Jalview2XML
    * @param af
    */
   protected void createJmolViewer(
-          final Entry<String, ViewerData> viewerData, AlignFrame af)
+          final Entry<String, StructureViewerModel> viewerData,
+          AlignFrame af)
   {
-    final ViewerData svattrib = viewerData.getValue();
-    String state = svattrib.stateData;
+    final StructureViewerModel svattrib = viewerData.getValue();
+    String state = svattrib.getStateData();
     List<String> pdbfilenames = new ArrayList<String>();
     List<SequenceI[]> seqmaps = new ArrayList<SequenceI[]>();
     List<String> pdbids = new ArrayList<String>();
     StringBuilder newFileLoc = new StringBuilder(64);
     int cp = 0, ncp, ecp;
-    Map<File, Object[]> oldFiles = svattrib.fileData;
+    Map<File, StructureData> oldFiles = svattrib.getFileData();
     while ((ncp = state.indexOf("load ", cp)) > -1)
     {
       do
@@ -3302,12 +3362,11 @@ public class Jalview2XML
         // 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.add((String) filedat[0]);
-        pdbids.add((String) filedat[1]);
-        seqmaps.add(((Vector<SequenceI>) filedat[2])
-                .toArray(new SequenceI[0]));
+        StructureData filedat = oldFiles.get(new File(oldfilenam));
+        newFileLoc.append(Platform.escapeString(filedat.getFilePath()));
+        pdbfilenames.add(filedat.getFilePath());
+        pdbids.add(filedat.getPdbId());
+        seqmaps.add(filedat.getSeqList().toArray(new SequenceI[0]));
         newFileLoc.append("\"");
         cp = ecp + 1; // advance beyond last \" and set cursor so we can
                       // look for next file statement.
@@ -3327,15 +3386,13 @@ public class Jalview2XML
       {
         // 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.add((String) filedat[0]);
-        pdbids.add((String) filedat[1]);
-        seqmaps.add(((Vector<SequenceI>) filedat[2])
-                .toArray(new SequenceI[0]));
+        StructureData filedat = oldFiles.get(id);
+        newFileLoc.append(filedat.getFilePath());
+        pdbfilenames.add(filedat.getFilePath());
+        pdbids.add(filedat.getPdbId());
+        seqmaps.add(filedat.getSeqList().toArray(new SequenceI[0]));
         newFileLoc.append(" \"");
-        newFileLoc.append((String) filedat[0]);
+        newFileLoc.append(filedat.getFilePath());
         newFileLoc.append("\"");
 
       }
@@ -3373,8 +3430,8 @@ public class Jalview2XML
       final String fileloc = newFileLoc.toString();
       final String sviewid = viewerData.getKey();
       final AlignFrame alf = af;
-      final java.awt.Rectangle rect = new java.awt.Rectangle(svattrib.x,
-              svattrib.y, svattrib.width, svattrib.height);
+      final Rectangle rect = new Rectangle(svattrib.getX(),
+              svattrib.getY(), svattrib.getWidth(), svattrib.getHeight());
       try
       {
         javax.swing.SwingUtilities.invokeAndWait(new Runnable()
@@ -3425,10 +3482,10 @@ public class Jalview2XML
    * @return
    */
   protected StructureViewerBase findMatchingViewer(
-          Entry<String, ViewerData> viewerData)
+          Entry<String, StructureViewerModel> viewerData)
   {
     final String sviewid = viewerData.getKey();
-    final ViewerData svattrib = viewerData.getValue();
+    final StructureViewerModel svattrib = viewerData.getValue();
     StructureViewerBase comp = null;
     JInternalFrame[] frames = getAllFrames();
     for (JInternalFrame frame : frames)
@@ -3439,8 +3496,8 @@ public class Jalview2XML
          * Post jalview 2.4 schema includes structure view id
          */
         if (sviewid != null
-                && ((StructureViewerBase) frame).getViewId().equals(
-                        sviewid))
+                && ((StructureViewerBase) frame).getViewId()
+                        .equals(sviewid))
         {
           comp = (AppJmol) frame;
           // todo: break?
@@ -3448,9 +3505,10 @@ public class Jalview2XML
         /*
          * Otherwise test for matching position and size of viewer frame
          */
-        else if (frame.getX() == svattrib.x && frame.getY() == svattrib.y
-                && frame.getHeight() == svattrib.height
-                && frame.getWidth() == svattrib.width)
+        else if (frame.getX() == svattrib.getX()
+                && frame.getY() == svattrib.getY()
+                && frame.getHeight() == svattrib.getHeight()
+                && frame.getWidth() == svattrib.getWidth())
         {
           comp = (AppJmol) frame;
           // todo: break?
@@ -3471,15 +3529,15 @@ public class Jalview2XML
    * @param viewerColouring
    */
   protected void linkStructureViewer(AlignmentPanel ap,
-          StructureViewerBase viewer, ViewerData svattrib)
+          StructureViewerBase viewer, StructureViewerModel svattrib)
   {
     // NOTE: if the jalview project is part of a shared session then
     // view synchronization should/could be done here.
 
-    final boolean useinViewerSuperpos = svattrib.alignWithPanel;
-    final boolean usetoColourbyseq = svattrib.colourWithAlignPanel;
-    final boolean viewerColouring = svattrib.colourByViewer;
-    Map<File, Object[]> oldFiles = svattrib.fileData;
+    final boolean useinViewerSuperpos = svattrib.isAlignWithPanel();
+    final boolean usetoColourbyseq = svattrib.isColourWithAlignPanel();
+    final boolean viewerColouring = svattrib.isColourByViewer();
+    Map<File, StructureData> oldFiles = svattrib.getFileData();
 
     /*
      * Add mapping for sequences in this view to an already open viewer
@@ -3489,13 +3547,10 @@ public class Jalview2XML
     {
       // add this and any other pdb files that should be present in the
       // viewer
-      Object[] filedat = oldFiles.get(id);
-      String pdbFile = (String) filedat[0];
-      SequenceI[] seq = ((Vector<SequenceI>) filedat[2])
-              .toArray(new SequenceI[0]);
-      binding
-              .getSsm()
-              .setMapping(seq, null, pdbFile,
+      StructureData filedat = oldFiles.get(id);
+      String pdbFile = filedat.getFilePath();
+      SequenceI[] seq = filedat.getSeqList().toArray(new SequenceI[0]);
+      binding.getSsm().setMapping(seq, null, pdbFile,
               jalview.io.AppletFormatAdapter.FILE);
       binding.addSequenceForStructFile(pdbFile, seq);
     }
@@ -3627,10 +3682,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(),
@@ -3644,19 +3699,18 @@ 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)
       {
         // propagate shared settings to this new view
-        af.viewport.historyList = av.historyList;
-        af.viewport.redoList = av.redoList;
+        af.viewport.setHistoryList(av.getHistoryList());
+        af.viewport.setRedoList(av.getRedoList());
       }
       else
       {
@@ -3681,14 +3735,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);
 
     }
@@ -3709,27 +3766,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)
@@ -3770,10 +3827,8 @@ public class Jalview2XML
 
     af.viewport.setColourAppliesToAllGroups(true);
 
-    if (view.getShowSequenceFeatures())
-    {
-      af.viewport.showSequenceFeatures = true;
-    }
+    af.viewport.setShowSequenceFeatures(view.getShowSequenceFeatures());
+
     if (view.hasCentreColumnLabels())
     {
       af.viewport.setCentreColumnLabels(view.getCentreColumnLabels());
@@ -3814,11 +3869,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())
     {
@@ -3840,9 +3895,14 @@ public class Jalview2XML
     // recover featre settings
     if (jms.getFeatureSettings() != null)
     {
-      af.viewport.setFeaturesDisplayed(new Hashtable());
+      FeaturesDisplayed fdi;
+      af.viewport.setFeaturesDisplayed(fdi = new FeaturesDisplayed());
       String[] renderOrder = new String[jms.getFeatureSettings()
               .getSettingCount()];
+      Hashtable featureGroups = new Hashtable();
+      Hashtable featureColours = new Hashtable();
+      Hashtable featureOrder = new Hashtable();
+
       for (int fs = 0; fs < jms.getFeatureSettings().getSettingCount(); fs++)
       {
         Setting setting = jms.getFeatureSettings().getSetting(fs);
@@ -3869,41 +3929,42 @@ public class Jalview2XML
             gc.setColourByLabel(setting.getColourByLabel());
           }
           // and put in the feature colour table.
-          af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().setColour(
-                  setting.getType(), gc);
+          featureColours.put(setting.getType(), gc);
         }
         else
         {
-          af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().setColour(
-                  setting.getType(),
+          featureColours.put(setting.getType(),
                   new java.awt.Color(setting.getColour()));
         }
         renderOrder[fs] = setting.getType();
         if (setting.hasOrder())
         {
-          af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().setOrder(
-                  setting.getType(), setting.getOrder());
+          featureOrder.put(setting.getType(), setting.getOrder());
         }
         else
         {
-          af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().setOrder(
-                  setting.getType(),
-                  fs / jms.getFeatureSettings().getSettingCount());
+          featureOrder.put(setting.getType(), new Float(fs
+                  / jms.getFeatureSettings().getSettingCount()));
         }
         if (setting.getDisplay())
         {
-          af.viewport.getFeaturesDisplayed().put(setting.getType(), new Integer(
-                  setting.getColour()));
+          fdi.setVisible(setting.getType());
         }
       }
-      af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().renderOrder = renderOrder;
-      Hashtable fgtable;
-      af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().featureGroups = fgtable = new Hashtable();
+      Hashtable fgtable = new Hashtable();
       for (int gs = 0; gs < jms.getFeatureSettings().getGroupCount(); gs++)
       {
         Group grp = jms.getFeatureSettings().getGroup(gs);
         fgtable.put(grp.getName(), new Boolean(grp.getDisplay()));
       }
+      // FeatureRendererSettings frs = new FeatureRendererSettings(renderOrder,
+      // fgtable, featureColours, jms.getFeatureSettings().hasTransparency() ?
+      // jms.getFeatureSettings().getTransparency() : 0.0, featureOrder);
+      FeatureRendererSettings frs = new FeatureRendererSettings(
+              renderOrder, fgtable, featureColours, 1.0f, featureOrder);
+      af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer()
+              .transferSettings(frs);
+
     }
 
     if (view.getHiddenColumnsCount() > 0)
@@ -3933,12 +3994,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;
   }
 
@@ -4068,7 +4144,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
@@ -4092,11 +4168,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
@@ -4268,9 +4344,8 @@ public class Jalview2XML
   {
     // JBP TODO: Check this is called for AlCodonFrames to support recovery of
     // xRef Codon Maps
-    jalview.datamodel.Sequence sq = (jalview.datamodel.Sequence) seqRefIds
-            .get(vamsasSeq.getId());
-    jalview.datamodel.SequenceI dsq = null;
+    SequenceI sq = seqRefIds.get(vamsasSeq.getId());
+    SequenceI dsq = null;
     if (sq != null && sq.getDatasetSequence() != null)
     {
       dsq = sq.getDatasetSequence();
@@ -4345,7 +4420,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())
@@ -4495,14 +4570,14 @@ public class Jalview2XML
          * local sequence definition
          */
         Sequence ms = mc.getSequence();
-        jalview.datamodel.Sequence djs = null;
+        SequenceI djs = null;
         String sqid = ms.getDsseqid();
         if (sqid != null && sqid.length() > 0)
         {
           /*
            * recover dataset sequence
            */
-          djs = (jalview.datamodel.Sequence) seqRefIds.get(sqid);
+          djs = seqRefIds.get(sqid);
         }
         else
         {
@@ -4561,7 +4636,7 @@ public class Jalview2XML
       frefedSequence = new Vector();
     }
 
-    viewportsAdded = new Hashtable();
+    viewportsAdded.clear();
 
     AlignFrame af = loadFromObject(jm, null, false, null);
     af.alignPanels.clear();
@@ -4708,13 +4783,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;
@@ -4765,5 +4836,4 @@ public class Jalview2XML
   {
     skipList = skipList2;
   }
-
 }