JAL-1588 part work save/restore of Chimera session
[jalview.git] / src / jalview / gui / Jalview2XML.java
index c960287..9ba8fb7 100644 (file)
@@ -129,7 +129,10 @@ import org.exolab.castor.xml.Unmarshaller;
 public class Jalview2XML
 {
 
-  private class ViewerData
+  /**
+   * A data bean to hold stored data about a structure viewer.
+   */
+  public class ViewerData
   {
 
     private int x;
@@ -140,13 +143,13 @@ public class Jalview2XML
 
     private int height;
 
-    private boolean alignWithPanel;
+    public boolean alignWithPanel;
 
-    private boolean colourWithAlignPanel;
+    public boolean colourWithAlignPanel;
 
-    private boolean colourByViewer;
+    public boolean colourByViewer;
 
-    private String stateData = "";
+    String stateData = "";
 
     // todo: java bean in place of Object []
     private Map<File, Object[]> fileData = new HashMap<File, Object[]>();
@@ -600,6 +603,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 +775,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;
@@ -3174,234 +3177,279 @@ public class Jalview2XML
         }
       }
     }
+      // Instantiate the associated structure views
+      for (Entry<String, ViewerData> entry : jmolViewIds.entrySet())
+      {
+        createOrLinkStructureViewer(entry, af, ap);
+      }
+  }
+
+  /**
+   * 
+   * @param viewerData
+   * @param af
+   * @param ap
+   */
+  protected void createOrLinkStructureViewer(
+          Entry<String, ViewerData> viewerData, AlignFrame af,
+          AlignmentPanel ap)
+  {
+    final ViewerData 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.stateData.indexOf("chimera") > -1)
+    {
+      createChimeraViewer(viewerData, af);
+    }
+    else
+    {
+      createJmolViewer(viewerData, af);
+    }
+  }
+
+  /**
+   * Create a new Chimera viewer.
+   * 
+   * @param viewerData
+   * @param af
+   */
+  protected void createChimeraViewer(Entry<String, ViewerData> viewerData,
+          AlignFrame af)
+  {
+    final ViewerData svattrib = viewerData.getValue();
+    ChimeraViewFrame cvf = new ChimeraViewFrame(svattrib, af);
+  }
+
+  /**
+   * 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, ViewerData> viewerData, AlignFrame af)
+  {
+    final ViewerData svattrib = viewerData.getValue();
+    String state = svattrib.stateData;
+    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;
+    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.
+        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]));
+        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())
       {
-        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>();
+        // 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]));
+        newFileLoc.append(" \"");
+        newFileLoc.append((String) filedat[0]);
+        newFileLoc.append("\"");
 
-        /*
-         * 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;
-            }
-          }
-        }
+      }
+      newFileLoc.append(";");
+    }
 
-        if (comp == null)
+    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 java.awt.Rectangle rect = new java.awt.Rectangle(svattrib.x,
+              svattrib.y, svattrib.width, svattrib.height);
+      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)
-            {
-              warn("Unexpected error when opening Jmol view.", ex);
-
-            } catch (InterruptedException e)
+              // 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)
             {
-              // 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, ViewerData> viewerData)
+  {
+    final String sviewid = viewerData.getKey();
+    final ViewerData 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.x && frame.getY() == svattrib.y
+                && frame.getHeight() == svattrib.height
+                && frame.getWidth() == svattrib.width)
         {
-          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, ViewerData 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.alignWithPanel;
+    final boolean usetoColourbyseq = svattrib.colourWithAlignPanel;
+    final boolean viewerColouring = svattrib.colourByViewer;
+    Map<File, Object[]> oldFiles = svattrib.fileData;
+
+    /*
+     * 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
@@ -3410,13 +3458,15 @@ public class Jalview2XML
       String pdbFile = (String) filedat[0];
       SequenceI[] seq = ((Vector<SequenceI>) filedat[2])
               .toArray(new SequenceI[0]);
-      viewer.jmb.getSsm().setMapping(seq, null, pdbFile,
+      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 +3476,7 @@ public class Jalview2XML
     }
     if (usetoColourbyseq)
     {
-      viewer.useAlignmentPanelForColourbyseq(ap, !jmolColouring);
+      viewer.useAlignmentPanelForColourbyseq(ap, !viewerColouring);
     }
     else
     {