JAL-1761 pattern for structure viewer construction from project file
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 26 May 2020 11:51:17 +0000 (12:51 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 26 May 2020 11:51:17 +0000 (12:51 +0100)
17 files changed:
src/jalview/ext/jmol/JalviewJmolBinding.java
src/jalview/ext/jmol/JmolCommands.java
src/jalview/ext/pymol/PymolCommands.java
src/jalview/ext/rbvi/chimera/ChimeraCommands.java
src/jalview/ext/rbvi/chimera/ChimeraXCommands.java
src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java
src/jalview/gui/AppJmol.java
src/jalview/gui/ChimeraViewFrame.java
src/jalview/gui/ChimeraXViewFrame.java
src/jalview/gui/PymolBindingModel.java
src/jalview/gui/PymolViewer.java
src/jalview/gui/StructureViewer.java
src/jalview/jbgui/GStructureViewer.java
src/jalview/project/Jalview2XML.java
src/jalview/structure/StructureCommandsI.java
src/jalview/structure/StructureSelectionManager.java
src/jalview/structures/models/AAStructureBindingModel.java

index 0b37ceb..63c1fd3 100644 (file)
@@ -598,6 +598,11 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
     modelFileNames = null;
     boolean notifyLoaded = false;
     String[] modelfilenames = getStructureFiles();
+    if (modelfilenames == null)
+    {
+      // Jmol is still loading files!
+      return;
+    }
     // first check if we've lost any structures
     if (oldmodels != null && oldmodels.length > 0)
     {
index 99130b4..085fbd5 100644 (file)
@@ -41,6 +41,7 @@ import jalview.structure.StructureCommandsBase;
 import jalview.structure.StructureMapping;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.Comparison;
+import jalview.util.Platform;
 
 /**
  * Routines for generating Jmol commands for Jalview/Jmol binding
@@ -291,7 +292,9 @@ public class JmolCommands extends StructureCommandsBase
   @Override
   public StructureCommandI loadFile(String file)
   {
-    return null;
+    // https://chemapps.stolaf.edu/jmol/docs/#loadfiles
+    return new StructureCommand("load FILES \"" + 
+            Platform.escapeBackslashes(file) + "\"");
   }
 
   /**
@@ -466,4 +469,10 @@ public class JmolCommands extends StructureCommandsBase
 
     return sb;
   }
+
+  @Override
+  public StructureCommandI openSession(String filepath)
+  {
+    return loadFile(filepath);
+  }
 }
index 53b1ec5..bba11a1 100644 (file)
@@ -309,4 +309,11 @@ public class PymolCommands extends StructureCommandsBase
     return commands;
   }
 
+  @Override
+  public StructureCommandI openSession(String filepath)
+  {
+    // https://pymolwiki.org/index.php/Load
+    return new StructureCommand("load", "", "0", "pse");
+  }
+
 }
index ac10b0b..8e72255 100644 (file)
@@ -154,6 +154,7 @@ public class ChimeraCommands extends StructureCommandsBase
    * @return
    * @see https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/setattr.html
    */
+  @Override
   protected String makeAttributeName(String featureType)
   {
     String attName = super.makeAttributeName(featureType);
@@ -397,4 +398,11 @@ public class ChimeraCommands extends StructureCommandsBase
     return commands;
   }
 
+  @Override
+  public StructureCommandI openSession(String filepath)
+  {
+    // https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/filetypes.html
+    return new StructureCommand("open chimera:" + filepath);
+  }
+
 }
index b7d9ce3..90bdb6b 100644 (file)
  */
 package jalview.ext.rbvi.chimera;
 
+import java.awt.Color;
+import java.util.Arrays;
+import java.util.List;
+
 import jalview.structure.AtomSpecModel;
 import jalview.structure.StructureCommand;
 import jalview.structure.StructureCommandI;
 import jalview.util.ColorUtils;
 
-import java.awt.Color;
-import java.util.Arrays;
-import java.util.List;
-
 /**
  * Routines for generating ChimeraX commands for Jalview/ChimeraX binding
  */
@@ -222,4 +222,10 @@ public class ChimeraXCommands extends ChimeraCommands
     return Arrays.asList(new StructureCommand(cmd.toString()));
   }
 
+  @Override
+  public StructureCommandI openSession(String filepath)
+  {
+    // https://www.cgl.ucsf.edu/chimerax/docs/user/commands/open.html#composite
+    return new StructureCommand("open session:" + filepath);
+  }
 }
index d1b8583..460b156 100644 (file)
@@ -536,25 +536,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
   }
 
   /**
-   * Ask Chimera to open a session file. Returns true if successful, else false.
-   * The filename must have a .py (Chimera) or .cxs (ChimeraX) extension for
-   * this command to work.
-   * 
-   * @param filepath
-   * @return
-   */
-  public boolean openSession(String filepath)
-  {
-    /*
-     * Chimera:  https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/open.html
-     * ChimeraX: https://www.cgl.ucsf.edu/chimerax/docs/user/commands/open.html
-     */
-    executeCommand(getCommandGenerator().loadFile(filepath), true);
-    // todo: test for failure - how?
-    return true;
-  }
-
-  /**
    * Send a 'show' command for all atoms in the currently selected columns
    * 
    * TODO: pull up to abstract structure viewer interface
index 87fb1b4..7cf10e7 100644 (file)
@@ -25,10 +25,10 @@ import java.awt.Color;
 import java.awt.Dimension;
 import java.awt.Font;
 import java.awt.Graphics;
-import java.awt.Rectangle;
 import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 import javax.swing.JPanel;
 import javax.swing.JSplitPane;
@@ -41,6 +41,8 @@ import jalview.bin.Cache;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
+import jalview.datamodel.StructureViewerModel;
+import jalview.datamodel.StructureViewerModel.StructureData;
 import jalview.gui.ImageExporter.ImageWriterI;
 import jalview.gui.StructureViewer.ViewerType;
 import jalview.structure.StructureCommand;
@@ -87,48 +89,55 @@ public class AppJmol extends StructureViewerBase
    * @param bounds
    * @param viewid
    */
-  public AppJmol(String[] files, String[] ids, SequenceI[][] seqs,
-          AlignmentPanel ap, boolean usetoColour, boolean useToAlign,
-          boolean leaveColouringToJmol, String loadStatus, Rectangle bounds,
-          String viewid)
+  public AppJmol(StructureViewerModel viewerModel, AlignmentPanel ap,
+          String sessionFile, String viewid)
   {
-    PDBEntry[] pdbentrys = new PDBEntry[files.length];
-    for (int i = 0; i < pdbentrys.length; i++)
+    Map<File, StructureData> pdbData = viewerModel.getFileData();
+    PDBEntry[] pdbentrys = new PDBEntry[pdbData.size()];
+    SequenceI[][] seqs = new SequenceI[pdbData.size()][];
+    int i = 0;
+    for (StructureData data : pdbData.values())
     {
-      // PDBEntry pdbentry = new PDBEntry(files[i], ids[i]);
-      PDBEntry pdbentry = new PDBEntry(ids[i], null, PDBEntry.Type.PDB,
-              files[i]);
+      PDBEntry pdbentry = new PDBEntry(data.getPdbId(), null,
+              PDBEntry.Type.PDB, data.getFilePath());
       pdbentrys[i] = pdbentry;
+      List<SequenceI> sequencesForPdb = data.getSeqList();
+      seqs[i] = sequencesForPdb
+              .toArray(new SequenceI[sequencesForPdb.size()]);
+      i++;
     }
-    // / TODO: check if protocol is needed to be set, and if chains are
+
+    // TODO: check if protocol is needed to be set, and if chains are
     // autodiscovered.
     jmb = new AppJmolBinding(this, ap.getStructureSelectionManager(),
             pdbentrys, seqs, null);
 
     jmb.setLoadingFromArchive(true);
     addAlignmentPanel(ap);
-    if (useToAlign)
+    if (viewerModel.isAlignWithPanel())
     {
       useAlignmentPanelForSuperposition(ap);
     }
     initMenus();
-    if (leaveColouringToJmol || !usetoColour)
+    boolean useToColour = viewerModel.isColourWithAlignPanel();
+    boolean leaveColouringToJmol = viewerModel.isColourByViewer();
+    if (leaveColouringToJmol || !useToColour)
     {
       jmb.setColourBySequence(false);
       seqColour.setSelected(false);
       viewerColour.setSelected(true);
     }
-    else if (usetoColour)
+    else if (useToColour)
     {
       useAlignmentPanelForColourbyseq(ap);
       jmb.setColourBySequence(true);
       seqColour.setSelected(true);
       viewerColour.setSelected(false);
     }
-    this.setBounds(bounds);
+
+    this.setBounds(viewerModel.getX(), viewerModel.getY(),
+            viewerModel.getWidth(), viewerModel.getHeight());
     setViewId(viewid);
-    // jalview.gui.Desktop.addInternalFrame(this, "Loading File",
-    // bounds.width,bounds.height);
 
     this.addInternalFrameListener(new InternalFrameAdapter()
     {
@@ -139,7 +148,10 @@ public class AppJmol extends StructureViewerBase
         closeViewer(false);
       }
     });
-    initJmol(loadStatus); // pdbentry, seq, JBPCHECK!
+    StringBuilder cmd = new StringBuilder();
+    cmd.append("load FILES ").append(QUOTE)
+            .append(Platform.escapeBackslashes(sessionFile)).append(QUOTE);
+    initJmol(cmd.toString());
   }
 
   @Override
index 65b002b..0e5675c 100644 (file)
@@ -28,6 +28,7 @@ import java.io.File;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
 import javax.swing.JInternalFrame;
 import javax.swing.JMenu;
@@ -40,6 +41,8 @@ import jalview.api.FeatureRenderer;
 import jalview.bin.Cache;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
+import jalview.datamodel.StructureViewerModel;
+import jalview.datamodel.StructureViewerModel.StructureData;
 import jalview.ext.rbvi.chimera.JalviewChimeraBinding;
 import jalview.gui.StructureViewer.ViewerType;
 import jalview.io.DataSourceType;
@@ -240,22 +243,34 @@ public class ChimeraViewFrame extends StructureViewerBase
    * @param colourBySequence
    * @param newViewId
    */
-  public ChimeraViewFrame(String chimeraSessionFile,
-          AlignmentPanel alignPanel, PDBEntry[] pdbArray,
-          SequenceI[][] seqsArray, boolean colourByChimera,
-          boolean colourBySequence, String newViewId)
+  public ChimeraViewFrame(StructureViewerModel viewerData,
+          AlignmentPanel alignPanel, String sessionFile, String vid)
   {
     this();
-    setViewId(newViewId);
-    this.chimeraSessionFile = chimeraSessionFile;
+    setViewId(vid);
+    this.chimeraSessionFile = sessionFile;
+    Map<File, StructureData> pdbData = viewerData.getFileData();
+    PDBEntry[] pdbArray = new PDBEntry[pdbData.size()];
+    SequenceI[][] seqsArray = new SequenceI[pdbData.size()][];
+    int i = 0;
+    for (StructureData data : pdbData.values())
+    {
+      PDBEntry pdbentry = new PDBEntry(data.getPdbId(), null,
+              PDBEntry.Type.PDB, data.getFilePath());
+      pdbArray[i] = pdbentry;
+      List<SequenceI> sequencesForPdb = data.getSeqList();
+      seqsArray[i] = sequencesForPdb
+              .toArray(new SequenceI[sequencesForPdb.size()]);
+      i++;
+    }
     openNewChimera(alignPanel, pdbArray, seqsArray);
-    if (colourByChimera)
+    if (viewerData.isColourByViewer())
     {
       jmb.setColourBySequence(false);
       seqColour.setSelected(false);
       viewerColour.setSelected(true);
     }
-    else if (colourBySequence)
+    else if (viewerData.isColourWithAlignPanel())
     {
       jmb.setColourBySequence(true);
       seqColour.setSelected(true);
index a823235..517eb4f 100644 (file)
@@ -2,6 +2,7 @@ package jalview.gui;
 
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
+import jalview.datamodel.StructureViewerModel;
 import jalview.gui.StructureViewer.ViewerType;
 
 /**
@@ -30,21 +31,15 @@ public class ChimeraXViewFrame extends ChimeraViewFrame
   /**
    * Constructor given a session file to be loaded
    * 
-   * @param chimeraSessionFile
+   * @param viewerData
    * @param alignPanel
-   * @param pdbArray
-   * @param seqsArray
-   * @param colourByChimera
-   * @param colourBySequence
-   * @param newViewId
+   * @param sessionFile
+   * @param vid
    */
-  public ChimeraXViewFrame(String chimeraSessionFile,
-          AlignmentPanel alignPanel, PDBEntry[] pdbArray,
-          SequenceI[][] seqsArray, boolean colourByChimera,
-          boolean colourBySequence, String newViewId)
+  public ChimeraXViewFrame(StructureViewerModel viewerData,
+          AlignmentPanel alignPanel, String sessionFile, String vid)
   {
-    super(chimeraSessionFile, alignPanel, pdbArray, seqsArray,
-            colourByChimera, colourBySequence, newViewId);
+    super(viewerData, alignPanel, sessionFile, vid);
   }
 
   @Override
index 21ba95c..264a49c 100644 (file)
@@ -151,14 +151,6 @@ public class PymolBindingModel extends AAStructureBindingModel
     }
   }
 
-  public boolean openSession(String pymolSessionFile)
-  {
-    StructureCommandI cmd = getCommandGenerator()
-            .loadFile(pymolSessionFile);
-    executeCommand(cmd, false);
-    return true;
-  }
-
   public boolean launchPymol()
   {
     if (pymolManager.isPymolLaunched())
index 4e0ac95..c5a4c9a 100644 (file)
@@ -5,6 +5,7 @@ import java.awt.event.ActionListener;
 import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 import javax.swing.JInternalFrame;
 import javax.swing.JMenuItem;
@@ -16,6 +17,8 @@ import jalview.api.FeatureRenderer;
 import jalview.bin.Cache;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
+import jalview.datamodel.StructureViewerModel;
+import jalview.datamodel.StructureViewerModel.StructureData;
 import jalview.gui.StructureViewer.ViewerType;
 import jalview.io.DataSourceType;
 import jalview.io.StructureFile;
@@ -71,22 +74,36 @@ public class PymolViewer extends StructureViewerBase
    * @param colourBySequence
    * @param newViewId
    */
-  public PymolViewer(String sessionFile, AlignmentPanel alignPanel,
-          PDBEntry[] pdbArray, SequenceI[][] seqsArray,
-          boolean colourByPymol, boolean colourBySequence, String newViewId)
+  public PymolViewer(StructureViewerModel viewerModel,
+          AlignmentPanel alignPanel, String sessionFile, String vid)
   {
     // TODO convert to base/factory class method
     this();
-    setViewId(newViewId);
+    setViewId(vid);
     this.pymolSessionFile = sessionFile;
+    Map<File, StructureData> pdbData = viewerModel.getFileData();
+    PDBEntry[] pdbArray = new PDBEntry[pdbData.size()];
+    SequenceI[][] seqsArray = new SequenceI[pdbData.size()][];
+    int i = 0;
+    for (StructureData data : pdbData.values())
+    {
+      PDBEntry pdbentry = new PDBEntry(data.getPdbId(), null,
+              PDBEntry.Type.PDB, data.getFilePath());
+      pdbArray[i] = pdbentry;
+      List<SequenceI> sequencesForPdb = data.getSeqList();
+      seqsArray[i] = sequencesForPdb
+              .toArray(new SequenceI[sequencesForPdb.size()]);
+      i++;
+    }
+
     openNewPymol(alignPanel, pdbArray, seqsArray);
-    if (colourByPymol)
+    if (viewerModel.isColourByViewer())
     {
       binding.setColourBySequence(false);
       seqColour.setSelected(false);
       viewerColour.setSelected(true);
     }
-    else if (colourBySequence)
+    else if (viewerModel.isColourWithAlignPanel())
     {
       binding.setColourBySequence(true);
       seqColour.setSelected(true);
@@ -306,7 +323,8 @@ public class PymolViewer extends StructureViewerBase
       boolean opened = binding.openSession(pymolSessionFile);
       if (!opened)
       {
-        System.err.println("An error occurred opening PyMOL session file "
+        Cache.log.error(
+                "An error occurred opening PyMOL session file "
                 + pymolSessionFile);
       }
     }
index 360ddf2..617706a 100644 (file)
  */
 package jalview.gui;
 
-import jalview.api.structures.JalviewStructureDisplayI;
-import jalview.bin.Cache;
-import jalview.datamodel.PDBEntry;
-import jalview.datamodel.SequenceI;
-import jalview.datamodel.StructureViewerModel;
-import jalview.structure.StructureSelectionManager;
-
-import java.awt.Rectangle;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
@@ -35,6 +27,13 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 
+import jalview.api.structures.JalviewStructureDisplayI;
+import jalview.bin.Cache;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.StructureViewerModel;
+import jalview.structure.StructureSelectionManager;
+
 /**
  * A proxy for handling structure viewers, that orchestrates adding selected
  * structures, associated with sequences in Jalview, to an existing viewer, or
@@ -330,42 +329,41 @@ public class StructureViewer
   }
 
   /**
-   * Create a new panel controlling a structure viewer.
+   * Creates a new panel controlling a structure viewer
    * 
    * @param type
-   * @param pdbf
-   * @param id
-   * @param sq
    * @param alignPanel
    * @param viewerData
-   * @param fileloc
-   * @param rect
+   * @param sessionFile
    * @param vid
    * @return
    */
-  public JalviewStructureDisplayI createView(ViewerType type, String[] pdbf,
-          String[] id, SequenceI[][] sq, AlignmentPanel alignPanel,
-          StructureViewerModel viewerData, String fileloc, Rectangle rect,
-          String vid)
+  public static JalviewStructureDisplayI createView(ViewerType type,
+          AlignmentPanel alignPanel, StructureViewerModel viewerData,
+          String sessionFile, String vid)
   {
-    final boolean useinViewerSuperpos = viewerData.isAlignWithPanel();
-    final boolean usetoColourbyseq = viewerData.isColourWithAlignPanel();
-    final boolean viewerColouring = viewerData.isColourByViewer();
-
+    JalviewStructureDisplayI viewer = null;
     switch (type)
     {
     case JMOL:
-      sview = new AppJmol(pdbf, id, sq, alignPanel, usetoColourbyseq,
-              useinViewerSuperpos, viewerColouring, fileloc, rect, vid);
+      viewer = new AppJmol(viewerData, alignPanel, sessionFile, vid);
+      // todo or construct and then openSession(sessionFile)?
       break;
     case CHIMERA:
-      Cache.log.error(
-              "Unsupported structure viewer type " + type.toString());
+      viewer = new ChimeraViewFrame(viewerData, alignPanel, sessionFile,
+              vid);
+      break;
+    case CHIMERAX:
+      viewer = new ChimeraXViewFrame(viewerData, alignPanel, sessionFile,
+              vid);
+      break;
+    case PYMOL:
+      viewer = new PymolViewer(viewerData, alignPanel, sessionFile, vid);
       break;
     default:
       Cache.log.error(UNKNOWN_VIEWER_TYPE + type.toString());
     }
-    return sview;
+    return viewer;
   }
 
   public boolean isBusy()
index 6c0beda..73180ee 100644 (file)
@@ -166,7 +166,6 @@ public abstract class GStructureViewer extends JInternalFrame
     JMenu helpMenu = new JMenu();
     helpMenu.setText(MessageManager.getString("action.help"));
     helpItem = new JMenuItem();
-    helpItem.setText(MessageManager.getString("label.jmol_help"));
     helpItem.addActionListener(new ActionListener()
     {
       @Override
index 4cfac7b..8bbe20c 100644 (file)
@@ -103,21 +103,16 @@ import jalview.datamodel.features.FeatureMatcher;
 import jalview.datamodel.features.FeatureMatcherI;
 import jalview.datamodel.features.FeatureMatcherSet;
 import jalview.datamodel.features.FeatureMatcherSetI;
-import jalview.ext.rbvi.chimera.JalviewChimeraBinding;
 import jalview.ext.varna.RnaModel;
 import jalview.gui.AlignFrame;
 import jalview.gui.AlignViewport;
 import jalview.gui.AlignmentPanel;
 import jalview.gui.AppVarna;
-import jalview.gui.ChimeraViewFrame;
-import jalview.gui.ChimeraXViewFrame;
 import jalview.gui.Desktop;
-import jalview.gui.JalviewChimeraXBindingModel;
 import jalview.gui.JvOptionPane;
 import jalview.gui.OOMWarning;
 import jalview.gui.PCAPanel;
 import jalview.gui.PaintRefresher;
-import jalview.gui.PymolViewer;
 import jalview.gui.SplitFrame;
 import jalview.gui.StructureViewer;
 import jalview.gui.StructureViewer.ViewerType;
@@ -4309,10 +4304,15 @@ public class Jalview2XML
             }
             if (!structureViewers.containsKey(sviewid))
             {
+              String viewerType = structureState.getType();
+              if (viewerType == null) // pre Jalview 2.9
+              {
+                viewerType = ViewerType.JMOL.toString();
+              }
               structureViewers.put(sviewid,
                       new StructureViewerModel(x, y, width, height, false,
                               false, true, structureState.getViewId(),
-                              structureState.getType()));
+                              viewerType));
               // Legacy pre-2.7 conversion JAL-823 :
               // do not assume any view has to be linked for colour by
               // sequence
@@ -4413,273 +4413,20 @@ public class Jalview2XML
       return;
     }
 
-    /*
-     * From 2.9: stateData.type contains JMOL or CHIMERA, data is in jar entry
-     * "viewer_"+stateData.viewId
-     */
     String type = stateData.getType();
-    if (type == null)
-    {
-      type = ViewerType.JMOL.toString();
-    }
     try
     {
       ViewerType viewerType = ViewerType.valueOf(type);
-      switch (viewerType)
-      {
-      case CHIMERA:
-        createChimeraViewer(viewerData, af, jprovider, false);
-        break;
-      case CHIMERAX:
-        createChimeraViewer(viewerData, af, jprovider, true);
-        break;
-      case PYMOL:
-        createPymolViewer(viewerData, af, jprovider);
-        break;
-      case JMOL:
-        createJmolViewer(viewerData, af, jprovider);
-      }
+      createStructureViewer(viewerType, viewerData, af, jprovider);
     } catch (IllegalArgumentException | NullPointerException e)
     {
+      // TODO JAL-3619 show error dialog / offer an alternative viewer
       Cache.log.error(
               "Invalid structure viewer type: " + type);
     }
   }
 
   /**
-   * Create a new Chimera or ChimeraX viewer
-   * 
-   * @param data
-   * @param af
-   * @param jprovider
-   * @param isChimeraX
-   */
-  protected void createChimeraViewer(
-          Entry<String, StructureViewerModel> viewerData, AlignFrame af,
-          jarInputStreamProvider jprovider, boolean isChimeraX)
-  {
-    StructureViewerModel data = viewerData.getValue();
-    String chimeraSessionFile = data.getStateData();
-
-    /*
-     * Copy Chimera session from jar entry "viewer_"+viewId to a temporary file
-     * 
-     * NB this is the 'saved' viewId as in the project file XML, _not_ the
-     * 'uniquified' sviewid used to reconstruct the viewer here
-     */
-    String viewerJarEntryName = getViewerJarEntryName(data.getViewId());
-    String extension = isChimeraX
-            ? JalviewChimeraXBindingModel.CHIMERAX_SESSION_EXTENSION
-            : JalviewChimeraBinding.CHIMERA_SESSION_EXTENSION;
-    chimeraSessionFile = copyJarEntry(jprovider, viewerJarEntryName,
-            "chimera", extension);
-
-    Set<Entry<File, StructureData>> fileData = data.getFileData()
-            .entrySet();
-    List<PDBEntry> pdbs = new ArrayList<>();
-    List<SequenceI[]> allseqs = new ArrayList<>();
-    for (Entry<File, StructureData> pdb : fileData)
-    {
-      String filePath = pdb.getValue().getFilePath();
-      String pdbId = pdb.getValue().getPdbId();
-      // pdbs.add(new PDBEntry(filePath, pdbId));
-      pdbs.add(new PDBEntry(pdbId, null, PDBEntry.Type.PDB, filePath));
-      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 use StructureViewer as a factory here, see JAL-1761
-    final PDBEntry[] pdbArray = pdbs.toArray(new PDBEntry[pdbs.size()]);
-    final SequenceI[][] seqsArray = allseqs
-            .toArray(new SequenceI[allseqs.size()][]);
-    String newViewId = viewerData.getKey();
-
-    ChimeraViewFrame cvf = isChimeraX
-            ? new ChimeraXViewFrame(chimeraSessionFile, af.alignPanel,
-                    pdbArray, seqsArray, colourByChimera, colourBySequence,
-                    newViewId)
-            : new ChimeraViewFrame(chimeraSessionFile, af.alignPanel,
-                    pdbArray, seqsArray, colourByChimera, colourBySequence,
-                    newViewId);
-    cvf.setSize(data.getWidth(), data.getHeight());
-    cvf.setLocation(data.getX(), data.getY());
-  }
-
-  /**
-   * 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
-   * @param jprovider
-   */
-  protected void createJmolViewer(
-          final Entry<String, StructureViewerModel> viewerData,
-          AlignFrame af, jarInputStreamProvider jprovider)
-  {
-    final StructureViewerModel svattrib = viewerData.getValue();
-    String state = svattrib.getStateData();
-
-    /*
-     * Pre-2.9: state element value is the Jmol state string
-     * 
-     * 2.9+: @type is "JMOL", state data is in a Jar file member named "viewer_"
-     * + viewId
-     */
-    if (ViewerType.JMOL.toString().equals(svattrib.getType()))
-    {
-      state = readJarEntry(jprovider,
-              getViewerJarEntryName(svattrib.getViewId()));
-    }
-
-    List<String> pdbfilenames = new ArrayList<>();
-    List<SequenceI[]> seqmaps = new ArrayList<>();
-    List<String> pdbids = new ArrayList<>();
-    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));
-        if (filedat == null)
-        {
-          String reformatedOldFilename = oldfilenam.replaceAll("/", "\\\\");
-          filedat = oldFiles.get(new File(reformatedOldFilename));
-        }
-        newFileLoc.append(Platform.escapeBackslashes(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)
-    {
-      return;
-    }
-    int histbug = newFileLoc.indexOf("history = ");
-    if (histbug > -1)
-    {
-      /*
-       * change "history = [true|false];" to "history = [1|0];"
-       */
-      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")) // eh? what can it be?
-        {
-          if (val.trim().equals("true"))
-          {
-            val = "1";
-          }
-          else
-          {
-            val = "0";
-          }
-          newFileLoc.replace(histbug, diff, val);
-        }
-      }
-    }
-
-    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()
-        {
-          JalviewStructureDisplayI sview = null;
-          try
-          {
-            sview = new StructureViewer(
-                    alf.alignPanel.getStructureSelectionManager())
-                            .createView(StructureViewer.ViewerType.JMOL,
-                                    pdbf, id, sq, alf.alignPanel, svattrib,
-                                    fileloc, rect, sviewid);
-            addNewStructureViewer(sview);
-          } catch (OutOfMemoryError ex)
-          {
-            new OOMWarning("restoring structure view for PDB id " + id,
-                    (OutOfMemoryError) ex.getCause());
-            if (sview != null && sview.isVisible())
-            {
-              sview.closeViewer(false);
-              sview.setVisible(false);
-              sview.dispose();
-            }
-          }
-        }
-      });
-    } catch (InvocationTargetException ex)
-    {
-      warn("Unexpected error when opening Jmol view.", ex);
-
-    } catch (InterruptedException e)
-    {
-      // e.printStackTrace();
-    }
-
-  }
-
-  /**
    * Generates a name for the entry in the project jar file to hold state
    * information for a structure viewer
    * 
@@ -5533,7 +5280,7 @@ public class Jalview2XML
         addDatasetRef(vamsasSet.getDatasetId(), ds);
       }
     }
-    Vector dseqs = null;
+    Vector<SequenceI> dseqs = null;
     if (!ignoreUnrefed)
     {
       // recovering an alignment View
@@ -5561,7 +5308,7 @@ public class Jalview2XML
       // try even harder to restore dataset
       AlignmentI xtantDS = checkIfHasDataset(vamsasSet.getSequence());
       // create a list of new dataset sequences
-      dseqs = new Vector();
+      dseqs = new Vector<>();
     }
     for (int i = 0, iSize = vamsasSet.getSequence().size(); i < iSize; i++)
     {
@@ -5649,7 +5396,8 @@ public class Jalview2XML
    *          vamsasSeq array ordering, to preserve ordering of dataset
    */
   private void ensureJalviewDatasetSequence(Sequence vamsasSeq,
-          AlignmentI ds, Vector dseqs, boolean ignoreUnrefed, int vseqpos)
+          AlignmentI ds, Vector<SequenceI> dseqs, boolean ignoreUnrefed,
+          int vseqpos)
   {
     // JBP TODO: Check this is called for AlCodonFrames to support recovery of
     // xRef Codon Maps
@@ -6373,58 +6121,175 @@ public class Jalview2XML
   }
 
   /**
-   * Create a new PyMol viewer
+   * Creates a new structure viewer window
    * 
-   * @param data
+   * @param viewerType
+   * @param viewerData
    * @param af
    * @param jprovider
    */
-  protected void createPymolViewer(
-          Entry<String, StructureViewerModel> viewerData, AlignFrame af,
+  protected void createStructureViewer(
+          ViewerType viewerType, final Entry<String, StructureViewerModel> viewerData,
+          AlignFrame af, jarInputStreamProvider jprovider)
+  {
+    final StructureViewerModel viewerModel = viewerData.getValue();
+    String sessionFilePath = null;
+
+    if (viewerType == ViewerType.JMOL)
+    {
+      sessionFilePath = rewriteJmolSession(viewerModel, jprovider);
+    }
+    else
+    {
+      String viewerJarEntryName = getViewerJarEntryName(
+              viewerModel.getViewId());
+      sessionFilePath = copyJarEntry(jprovider,
+              viewerJarEntryName,
+              "viewerSession", ".tmp");
+    }
+    final String sessionPath = sessionFilePath;
+    final String sviewid = viewerData.getKey();
+    try
+    {
+      SwingUtilities.invokeAndWait(new Runnable()
+      {
+        @Override
+        public void run()
+        {
+          JalviewStructureDisplayI sview = null;
+          try
+          {
+            sview = StructureViewer.createView(viewerType, af.alignPanel,
+                    viewerModel, sessionPath, sviewid);
+            addNewStructureViewer(sview);
+          } catch (OutOfMemoryError ex)
+          {
+            new OOMWarning("Restoring structure view for "
+                    + viewerType,
+                    (OutOfMemoryError) ex.getCause());
+            if (sview != null && sview.isVisible())
+            {
+              sview.closeViewer(false);
+              sview.setVisible(false);
+              sview.dispose();
+            }
+          }
+        }
+      });
+    } catch (InvocationTargetException | InterruptedException ex)
+    {
+      warn("Unexpected error when opening " + viewerType
+              + " structure viewer", ex);
+    }
+  }
+
+  /**
+   * Rewrites a Jmol session script, saves it to a temporary file, and returns
+   * the path of the file. "load file" commands are rewritten to change the
+   * original PDB file names to those created as the Jalview project is loaded.
+   * 
+   * @param svattrib
+   * @param jprovider
+   * @return
+   */
+  private String rewriteJmolSession(StructureViewerModel svattrib,
           jarInputStreamProvider jprovider)
   {
-    StructureViewerModel data = viewerData.getValue();
-    String pymolSessionFile = data.getStateData();
-  
-    /*
-     * Copy PyMol session from jar entry "viewer_"+viewId to a temporary file
-     * 
-     * NB this is the 'saved' viewId as in the project file XML, _not_ the
-     * 'uniquified' sviewid used to reconstruct the viewer here
-     */
-    String viewerJarEntryName = getViewerJarEntryName(data.getViewId());
-    pymolSessionFile = copyJarEntry(jprovider, viewerJarEntryName,
-            "pymol", ".pse");
-  
-    Set<Entry<File, StructureData>> fileData = data.getFileData()
-            .entrySet();
-    List<PDBEntry> pdbs = new ArrayList<>();
-    List<SequenceI[]> allseqs = new ArrayList<>();
-    for (Entry<File, StructureData> pdb : fileData)
-    {
-      String filePath = pdb.getValue().getFilePath();
-      String pdbId = pdb.getValue().getPdbId();
-      // pdbs.add(new PDBEntry(filePath, pdbId));
-      pdbs.add(new PDBEntry(pdbId, null, PDBEntry.Type.PDB, filePath));
-      final List<SequenceI> seqList = pdb.getValue().getSeqList();
-      SequenceI[] seqs = seqList.toArray(new SequenceI[seqList.size()]);
-      allseqs.add(seqs);
+    String state = svattrib.getStateData(); // Jalview < 2.9
+    if (state == null || state.isEmpty()) // Jalview >= 2.9
+    {
+      state = readJarEntry(jprovider,
+              getViewerJarEntryName(svattrib.getViewId()));
     }
-  
-    boolean colourByPymol = data.isColourByViewer();
-    boolean colourBySequence = data.isColourWithAlignPanel();
-  
-    // TODO use StructureViewer as a factory here, see JAL-1761
-    final PDBEntry[] pdbArray = pdbs.toArray(new PDBEntry[pdbs.size()]);
-    final SequenceI[][] seqsArray = allseqs
-            .toArray(new SequenceI[allseqs.size()][]);
-    String newViewId = viewerData.getKey();
-  
-    PymolViewer pv = new PymolViewer(pymolSessionFile,
-            af.alignPanel, pdbArray, seqsArray, colourByPymol,
-            colourBySequence, newViewId);
-    pv.setSize(data.getWidth(), data.getHeight());
-    pv.setLocation(data.getX(), data.getY());
+    // TODO or simpler? for each key in oldFiles,
+    // replace key.getPath() in state with oldFiles.get(key).getFilePath()
+    // (allowing for different path escapings)
+    StringBuilder rewritten = new StringBuilder(state.length());
+    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
+        rewritten.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));
+        if (filedat == null)
+        {
+          String reformatedOldFilename = oldfilenam.replaceAll("/", "\\\\");
+          filedat = oldFiles.get(new File(reformatedOldFilename));
+        }
+        rewritten
+                .append(Platform.escapeBackslashes(filedat.getFilePath()));
+        rewritten.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
+      rewritten.append(state.substring(cp));
+    }
+    else
+    {
+      System.err.print("Ignoring incomplete Jmol state for PDB ids: ");
+      rewritten = new StringBuilder(state);
+      rewritten.append("; load append ");
+      for (File id : oldFiles.keySet())
+      {
+        // add pdb files that should be present in the viewer
+        StructureData filedat = oldFiles.get(id);
+        rewritten.append(filedat.getFilePath()).append(" \"")
+                .append(filedat.getFilePath()).append("\"");
+      }
+      rewritten.append(";");
+    }
+
+    if (rewritten.length() == 0)
+    {
+      return null;
+    }
+    final String history = "history = ";
+    int historyIndex = rewritten.indexOf(history);
+    if (historyIndex > -1)
+    {
+      /*
+       * change "history = [true|false];" to "history = [1|0];"
+       */
+      historyIndex += history.length();
+      String val = rewritten.substring(historyIndex, historyIndex + 5);
+      if (val.startsWith("true"))
+      {
+        rewritten.replace(historyIndex, historyIndex + 4, "1");
+      }
+      else if (val.startsWith("false"))
+      {
+        rewritten.replace(historyIndex, historyIndex + 5, "0");
+      }
+    }
+
+    try
+    {
+      File tmp = File.createTempFile("viewerSession", ".tmp");
+      try (OutputStream os = new FileOutputStream(tmp))
+      {
+        InputStream is = new ByteArrayInputStream(
+                rewritten.toString().getBytes());
+        copyAll(is, os);
+        return tmp.getAbsolutePath();
+      }
+    } catch (IOException e)
+    {
+      Cache.log.error("Error restoring Jmol session: " + e.toString());
+    }
+    return null;
   }
 
   /**
index 871d84b..5a0db0a 100644 (file)
@@ -4,9 +4,6 @@ import java.awt.Color;
 import java.util.List;
 import java.util.Map;
 
-import jalview.api.AlignmentViewPanel;
-import jalview.datamodel.SequenceI;
-
 /**
  * Methods that generate commands that can be sent to a molecular structure
  * viewer program (e.g. Jmol, Chimera, ChimeraX)
@@ -160,4 +157,13 @@ public interface StructureCommandsI
    */
   List<StructureCommandI> setAttributes(
           Map<String, Map<Object, AtomSpecModel>> featureValues);
+
+  /**
+   * Returns command to open a saved structure viewer session file, or null if
+   * not supported
+   * 
+   * @param filepath
+   * @return
+   */
+  StructureCommandI openSession(String filepath);
 }
index 798b07a..6319338 100644 (file)
  */
 package jalview.structure;
 
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
 import jalview.analysis.AlignSeq;
 import jalview.api.StructureSelectionManagerProvider;
+import jalview.bin.Cache;
 import jalview.commands.CommandI;
 import jalview.commands.EditCommand;
 import jalview.commands.OrderCommand;
@@ -45,18 +57,6 @@ import jalview.util.Platform;
 import jalview.ws.sifts.SiftsClient;
 import jalview.ws.sifts.SiftsException;
 import jalview.ws.sifts.SiftsSettings;
-
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.IdentityHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Vector;
-
 import mc_view.Atom;
 import mc_view.PDBChain;
 import mc_view.PDBfile;
@@ -537,15 +537,14 @@ public class StructureSelectionManager
                     pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
             seqToStrucMapping.add(siftsMapping);
             maxChain.makeExactMapping(siftsMapping, seq);
-            maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS");// FIXME: is this
-                                                       // "IEA:SIFTS" ?
+            maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS");
             maxChain.transferResidueAnnotation(siftsMapping, null);
             ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
 
           } catch (SiftsException e)
           {
             // fall back to NW alignment
-            System.err.println(e.getMessage());
+            Cache.log.error(e.getMessage());
             StructureMapping nwMapping = getNWMappings(seq, pdbFile,
                     targetChainId, maxChain, pdb, maxAlignseq);
             seqToStrucMapping.add(nwMapping);
index 8aa1895..870a761 100644 (file)
@@ -1697,6 +1697,25 @@ public abstract class AAStructureBindingModel
   }
 
   /**
+   * Ask the structure viewer to open a session file. Returns true if
+   * successful, else false (or not supported).
+   * 
+   * @param filepath
+   * @return
+   */
+  public boolean openSession(String filepath)
+  {
+    StructureCommandI cmd = getCommandGenerator().openSession(filepath);
+    if (cmd == null)
+    {
+      return false;
+    }
+    executeCommand(cmd, true);
+    // todo: test for failure - how?
+    return true;
+  }
+
+  /**
    * Scans visible features in mapped positions of the CDS/peptide complement, and
    * adds any found to the map of attribute values/structure positions
    *