JAL-1588 new class renamed
[jalview.git] / src / jalview / gui / Jalview2XML.java
index c960287..a2cd147 100644 (file)
@@ -27,6 +27,8 @@ 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;
@@ -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,44 +133,6 @@ import org.exolab.castor.xml.Unmarshaller;
  */
 public class Jalview2XML
 {
-
-  private class ViewerData
-  {
-
-    private int x;
-
-    private int y;
-
-    private int width;
-
-    private int height;
-
-    private boolean alignWithPanel;
-
-    private boolean colourWithAlignPanel;
-
-    private boolean colourByViewer;
-
-    private 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.
@@ -600,6 +567,7 @@ public class Jalview2XML
           boolean storeDS, JarOutputStream jout)
   {
     initSeqRefs();
+    List<String> viewIds = new ArrayList<String>();
     List<UserColourScheme> userColours = new ArrayList<UserColourScheme>();
 
     AlignViewport av = ap.av;
@@ -771,11 +739,10 @@ public class Jalview2XML
           pdb.setType(entry.getType());
 
           /*
-           * Store any JMol or Chimera views associated with this sequence. This
+           * Store any structure views associated with this sequence. This
            * section copes with duplicate entries in the project, so a dataset
            * only view *should* be coped with sensibly.
            */
-          List<String> viewIds = new ArrayList<String>();
           // This must have been loaded, is it still visible?
           JInternalFrame[] frames = Desktop.desktop.getAllFrames();
           String matchedFile = null;
@@ -1131,12 +1098,12 @@ public class Jalview2XML
       view.setFontSize(av.font.getSize());
       view.setFontStyle(av.font.getStyle());
       view.setRenderGaps(av.renderGaps);
-      view.setShowAnnotation(av.getShowAnnotation());
+      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());
@@ -1157,7 +1124,8 @@ public class Jalview2XML
       {
         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;
@@ -1188,8 +1156,8 @@ public class Jalview2XML
                       .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)
@@ -1202,8 +1170,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();
@@ -1227,8 +1195,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())
         {
@@ -1240,7 +1209,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);
@@ -1261,8 +1230,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]);
@@ -1523,6 +1492,7 @@ public class Jalview2XML
           an.addProperty(prop);
         }
       }
+
       AnnotationElement ae;
       if (aa[i].annotations != null)
       {
@@ -3070,9 +3040,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++)
     {
@@ -3112,9 +3084,9 @@ public class Jalview2XML
               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,
+              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
@@ -3125,44 +3097,53 @@ 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
+            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
@@ -3174,249 +3155,326 @@ public class Jalview2XML
         }
       }
     }
+      // Instantiate the associated structure views
+      for (Entry<String, StructureViewerModel> entry : structureViewers.entrySet())
+      {
+        createOrLinkStructureViewer(entry, af, ap);
+      }
+  }
+
+  /**
+   * 
+   * @param viewerData
+   * @param af
+   * @param ap
+   */
+  protected void createOrLinkStructureViewer(
+          Entry<String, StructureViewerModel> viewerData, AlignFrame af,
+          AlignmentPanel ap)
+  {
+    final StructureViewerModel svattrib = viewerData.getValue();
+
+    /*
+     * Search for any viewer windows already open from other alignment views
+     * that exactly match the stored structure state
+     */
+    StructureViewerBase comp = findMatchingViewer(viewerData);
+
+    if (comp != null)
     {
+      linkStructureViewer(ap, comp, svattrib);
+      return;
+    }
 
-      // Instantiate the associated Jmol views
-      for (Entry<String, ViewerData> entry : jmolViewIds.entrySet())
+    /*
+     * Pending an XML element for ViewerType, just check if stateData contains
+     * "chimera" (part of the chimera session filename).
+     */
+    if (svattrib.getStateData().indexOf("chimera") > -1)
+    {
+      createChimeraViewer(viewerData, af);
+    }
+    else
+    {
+      createJmolViewer(viewerData, af);
+    }
+  }
+
+  /**
+   * Create a new Chimera viewer.
+   * 
+   * @param viewerData
+   * @param af
+   */
+  protected void createChimeraViewer(Entry<String, StructureViewerModel> viewerData,
+          AlignFrame af)
+  {
+    final StructureViewerModel data = viewerData.getValue();
+    String chimeraSession = data.getStateData();
+
+    if (new File(chimeraSession).exists())
+    {
+      Set<Entry<File, StructureData>> fileData = data.getFileData()
+              .entrySet();
+      List<PDBEntry> pdbs = new ArrayList<PDBEntry>();
+      List<SequenceI[]> allseqs = new ArrayList<SequenceI[]>();
+      for (Entry<File, StructureData> pdb : fileData)
       {
-        String sviewid = entry.getKey();
-        ViewerData svattrib = entry.getValue();
-        String state = svattrib.stateData;
-        Map<File, Object[]> oldFiles = svattrib.fileData;
-        final boolean useinJmolsuperpos = svattrib.alignWithPanel;
-        final boolean usetoColourbyseq = svattrib.colourWithAlignPanel;
-        final boolean jmolColouring = svattrib.colourByViewer;
-        int x = svattrib.x;
-        int y = svattrib.y;
-        int width = svattrib.width;
-        int height = svattrib.height;
-        // 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>();
+        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);
+      }
 
-        /*
-         * Search for any Jmol windows already open from other alignment views
-         * that exactly match the stored structure state
-         */
-        AppJmol comp = null;
-        JInternalFrame[] frames = getAllFrames();
-        for (JInternalFrame frame : frames)
-        {
-          if (frame instanceof AppJmol)
-          {
-            /*
-             * Post jalview 2.4 schema includes structure view id
-             */
-            if (sviewid != null
-                    && ((StructureViewerBase) frame).getViewId().equals(
-                            sviewid))
-            {
-              comp = (AppJmol) frame;
-            }
-            /*
-             * Otherwise test for matching position and size of viewer frame
-             */
-            else if (frame.getX() == x && frame.getY() == y
-                    && frame.getHeight() == height
-                    && frame.getWidth() == width)
-            {
-              comp = (AppJmol) frame;
-            }
-          }
-        }
+      boolean colourByChimera = data.isColourByViewer();
+      boolean colourBySequence = data.isColourWithAlignPanel();
 
-        if (comp == null)
+      // 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()][]);
+      new ChimeraViewFrame(chimeraSession, af.alignPanel, pdbArray,
+              seqsArray,
+              colourByChimera, colourBySequence);
+    }
+    else
+    {
+      Cache.log.error("Chimera session file " + chimeraSession
+              + " not found");
+    }
+  }
+
+  /**
+   * 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.
+   * 
+   * @param viewerData
+   * @param af
+   */
+  protected void createJmolViewer(
+          final Entry<String, StructureViewerModel> viewerData, AlignFrame af)
+  {
+    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, StructureData> oldFiles = svattrib.getFileData();
+    while ((ncp = state.indexOf("load ", cp)) > -1)
+    {
+      do
+      {
+        // look for next filename in load statement
+        newFileLoc.append(state.substring(cp,
+                ncp = (state.indexOf("\"", ncp + 1) + 1)));
+        String oldfilenam = state.substring(ncp,
+                ecp = state.indexOf("\"", ncp));
+        // recover the new mapping data for this old filename
+        // have to normalize filename - since Jmol and jalview do
+        // filename
+        // translation differently.
+        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.
+      } while ((ncp = state.indexOf("/*file*/", cp)) > -1);
+    }
+    if (cp > 0)
+    {
+      // just append rest of state
+      newFileLoc.append(state.substring(cp));
+    }
+    else
+    {
+      System.err.print("Ignoring incomplete Jmol state for PDB ids: ");
+      newFileLoc = new StringBuilder(state);
+      newFileLoc.append("; load append ");
+      for (File id : oldFiles.keySet())
+      {
+        // add this and any other pdb files that should be present in
+        // the viewer
+        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(filedat.getFilePath());
+        newFileLoc.append("\"");
+
+      }
+      newFileLoc.append(";");
+    }
+
+    if (newFileLoc.length() > 0)
+    {
+      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"))
         {
-          /*
-           * Create a new Jmol window. First parse the Jmol state to translate
-           * filenames loaded into the view, and record the order in which files
-           * are shown in the Jmol view, so we can add the sequence mappings in
-           * same order.
-           */
-          StringBuffer newFileLoc = null;
-          int cp = 0, ncp, ecp;
-          while ((ncp = state.indexOf("load ", cp)) > -1)
-          {
-            if (newFileLoc == null)
-            {
-              newFileLoc = new StringBuffer();
-            }
-            do
-            {
-              // look for next filename in load statement
-              newFileLoc.append(state.substring(cp,
-                      ncp = (state.indexOf("\"", ncp + 1) + 1)));
-              String oldfilenam = state.substring(ncp,
-                      ecp = state.indexOf("\"", ncp));
-              // recover the new mapping data for this old filename
-              // have to normalize filename - since Jmol and jalview do
-              // filename
-              // translation differently.
-              Object[] filedat = oldFiles.get(new File(oldfilenam));
-              newFileLoc.append(Platform
-                      .escapeString((String) filedat[0]));
-              pdbfilenames.addElement((String) filedat[0]);
-              pdbids.addElement((String) filedat[1]);
-              seqmaps.addElement(((Vector<SequenceI>) filedat[2])
-                      .toArray(new SequenceI[0]));
-              newFileLoc.append("\"");
-              cp = ecp + 1; // advance beyond last \" and set cursor so we can
-                            // look for next file statement.
-            } while ((ncp = state.indexOf("/*file*/", cp)) > -1);
-          }
-          if (cp > 0)
+          if (val.trim().equals("true"))
           {
-            // just append rest of state
-            newFileLoc.append(state.substring(cp));
+            val = "1";
           }
           else
           {
-            System.err
-                    .print("Ignoring incomplete Jmol state for PDB ids: ");
-            newFileLoc = new StringBuffer(state);
-            newFileLoc.append("; load append ");
-            for (File id : oldFiles.keySet())
-            {
-              // add this and any other pdb files that should be present in
-              // the viewer
-              Object[] filedat = oldFiles.get(id);
-              String nfilename;
-              newFileLoc.append(((String) filedat[0]));
-              pdbfilenames.addElement((String) filedat[0]);
-              pdbids.addElement((String) filedat[1]);
-              seqmaps.addElement(((Vector<SequenceI>) filedat[2])
-                      .toArray(new SequenceI[0]));
-              newFileLoc.append(" \"");
-              newFileLoc.append((String) filedat[0]);
-              newFileLoc.append("\"");
-
-            }
-            newFileLoc.append(";");
+            val = "0";
           }
+          newFileLoc.replace(histbug, diff, val);
+        }
+      }
 
-          if (newFileLoc != null)
+      final String[] pdbf = pdbfilenames.toArray(new String[pdbfilenames
+              .size()]);
+      final String[] id = pdbids.toArray(new String[pdbids.size()]);
+      final SequenceI[][] sq = seqmaps
+              .toArray(new SequenceI[seqmaps.size()][]);
+      final String fileloc = newFileLoc.toString();
+      final String sviewid = viewerData.getKey();
+      final AlignFrame alf = af;
+      final Rectangle rect = new Rectangle(svattrib.getX(),
+              svattrib.getY(), svattrib.getWidth(), svattrib.getHeight());
+      try
+      {
+        javax.swing.SwingUtilities.invokeAndWait(new Runnable()
+        {
+          @Override
+          public void run()
           {
-            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"))
-              {
-                if (val.trim().equals("true"))
-                {
-                  val = "1";
-                }
-                else
-                {
-                  val = "0";
-                }
-                newFileLoc.replace(histbug, diff, val);
-              }
-            }
-            // TODO: assemble String[] { pdb files }, String[] { id for each
-            // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
-            // seqs_file 2}} from hash
-            final String[] pdbf = pdbfilenames
-                    .toArray(new String[pdbfilenames.size()]), id = pdbids
-                    .toArray(new String[pdbids.size()]);
-            final SequenceI[][] sq = seqmaps
-                    .toArray(new SequenceI[seqmaps.size()][]);
-            final String fileloc = newFileLoc.toString(), vid = sviewid;
-            final AlignFrame alf = af;
-            final java.awt.Rectangle rect = new java.awt.Rectangle(x, y,
-                    width, height);
+            JalviewStructureDisplayI sview = null;
             try
             {
-              javax.swing.SwingUtilities.invokeAndWait(new Runnable()
-              {
-                @Override
-                public void run()
-                {
-                  JalviewStructureDisplayI sview = null;
-                  try
-                  {
-                    // JAL-1333 note - we probably can't migrate Jmol views to UCSF Chimera!
-                    sview = new StructureViewer(alf.alignPanel
-                            .getStructureSelectionManager()).createView(
-                            StructureViewer.ViewerType.JMOL, pdbf, id, sq,
-                            alf.alignPanel,
-                            useinJmolsuperpos, usetoColourbyseq,
-                            jmolColouring, fileloc, rect, vid);
-                    addNewStructureViewer(sview);
-                  } catch (OutOfMemoryError ex)
-                  {
-                    new OOMWarning("restoring structure view for PDB id "
-                            + id, (OutOfMemoryError) ex.getCause());
-                    if (sview != null && sview.isVisible())
-                    {
-                      sview.closeViewer();
-                      sview.setVisible(false);
-                      sview.dispose();
-                    }
-                  }
-                }
-              });
-            } catch (InvocationTargetException ex)
+              // JAL-1333 note - we probably can't migrate Jmol views to UCSF
+              // Chimera!
+              sview = new StructureViewer(alf.alignPanel
+                      .getStructureSelectionManager()).createView(
+                      StructureViewer.ViewerType.JMOL, pdbf, id, sq,
+                      alf.alignPanel, svattrib, fileloc, rect, sviewid);
+              addNewStructureViewer(sview);
+            } catch (OutOfMemoryError ex)
             {
-              warn("Unexpected error when opening Jmol view.", ex);
-
-            } catch (InterruptedException e)
-            {
-              // e.printStackTrace();
+              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);
+
+      } catch (InterruptedException e)
+      {
+        // e.printStackTrace();
+      }
+    }
+  }
 
+  /**
+   * Returns any open frame that matches given structure viewer data. The match
+   * is based on the unique viewId, or (for older project versions) the frame's
+   * geometry.
+   * 
+   * @param viewerData
+   * @return
+   */
+  protected StructureViewerBase findMatchingViewer(
+          Entry<String, StructureViewerModel> viewerData)
+  {
+    final String sviewid = viewerData.getKey();
+    final StructureViewerModel svattrib = viewerData.getValue();
+    StructureViewerBase comp = null;
+    JInternalFrame[] frames = getAllFrames();
+    for (JInternalFrame frame : frames)
+    {
+      if (frame instanceof StructureViewerBase)
+      {
+        /*
+         * Post jalview 2.4 schema includes structure view id
+         */
+        if (sviewid != null
+                && ((StructureViewerBase) frame).getViewId().equals(
+                        sviewid))
+        {
+          comp = (AppJmol) frame;
+          // todo: break?
         }
-        else
-        // if (comp != null)
+        /*
+         * Otherwise test for matching position and size of viewer frame
+         */
+        else if (frame.getX() == svattrib.getX()
+                && frame.getY() == svattrib.getY()
+                && frame.getHeight() == svattrib.getHeight()
+                && frame.getWidth() == svattrib.getWidth())
         {
-          linkStructureViewer(ap, comp, oldFiles, useinJmolsuperpos,
-                  usetoColourbyseq, jmolColouring);
+          comp = (AppJmol) frame;
+          // todo: break?
         }
       }
     }
+    return comp;
   }
 
   /**
-   * Link an AlignmentPanel to an existing JMol viewer.
+   * Link an AlignmentPanel to an existing structure viewer.
    * 
    * @param ap
    * @param viewer
    * @param oldFiles
-   * @param useinJmolsuperpos
+   * @param useinViewerSuperpos
    * @param usetoColourbyseq
-   * @param jmolColouring
+   * @param viewerColouring
    */
-  protected void linkStructureViewer(AlignmentPanel ap, AppJmol viewer,
-          Map<File, Object[]> oldFiles,
-          final boolean useinJmolsuperpos, final boolean usetoColourbyseq,
-          final boolean jmolColouring)
+  protected void linkStructureViewer(AlignmentPanel ap,
+          StructureViewerBase viewer, StructureViewerModel svattrib)
   {
     // 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
+    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
+     */
+    final AAStructureBindingModel binding = viewer.getBinding();
     for (File id : oldFiles.keySet())
     {
       // add this and any other pdb files that should be present in the
       // viewer
-      Object[] filedat = oldFiles.get(id);
-      String pdbFile = (String) filedat[0];
-      SequenceI[] seq = ((Vector<SequenceI>) filedat[2])
-              .toArray(new SequenceI[0]);
-      viewer.jmb.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);
-      viewer.jmb.addSequenceForStructFile(pdbFile, seq);
+      binding.addSequenceForStructFile(pdbFile, seq);
     }
-    // and add the AlignmentPanel's reference to the Jmol view
+    // and add the AlignmentPanel's reference to the view panel
     viewer.addAlignmentPanel(ap);
-    if (useinJmolsuperpos)
+    if (useinViewerSuperpos)
     {
       viewer.useAlignmentPanelForSuperposition(ap);
     }
@@ -3426,7 +3484,7 @@ public class Jalview2XML
     }
     if (usetoColourbyseq)
     {
-      viewer.useAlignmentPanelForColourbyseq(ap, !jmolColouring);
+      viewer.useAlignmentPanelForColourbyseq(ap, !viewerColouring);
     }
     else
     {
@@ -3685,10 +3743,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());
@@ -3755,9 +3811,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);
@@ -3784,41 +3845,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)
@@ -4680,5 +4742,4 @@ public class Jalview2XML
   {
     skipList = skipList2;
   }
-
 }