From 7720585dc8cc56cdad2486b330ed37ddd0133531 Mon Sep 17 00:00:00 2001 From: gmungoc Date: Tue, 26 May 2020 12:51:17 +0100 Subject: [PATCH] JAL-1761 pattern for structure viewer construction from project file --- src/jalview/ext/jmol/JalviewJmolBinding.java | 5 + src/jalview/ext/jmol/JmolCommands.java | 11 +- src/jalview/ext/pymol/PymolCommands.java | 7 + src/jalview/ext/rbvi/chimera/ChimeraCommands.java | 8 + src/jalview/ext/rbvi/chimera/ChimeraXCommands.java | 14 +- .../ext/rbvi/chimera/JalviewChimeraBinding.java | 19 - src/jalview/gui/AppJmol.java | 48 +- src/jalview/gui/ChimeraViewFrame.java | 31 +- src/jalview/gui/ChimeraXViewFrame.java | 19 +- src/jalview/gui/PymolBindingModel.java | 8 - src/jalview/gui/PymolViewer.java | 32 +- src/jalview/gui/StructureViewer.java | 52 +-- src/jalview/jbgui/GStructureViewer.java | 1 - src/jalview/project/Jalview2XML.java | 483 +++++++------------- src/jalview/structure/StructureCommandsI.java | 12 +- .../structure/StructureSelectionManager.java | 29 +- .../structures/models/AAStructureBindingModel.java | 19 + 17 files changed, 366 insertions(+), 432 deletions(-) diff --git a/src/jalview/ext/jmol/JalviewJmolBinding.java b/src/jalview/ext/jmol/JalviewJmolBinding.java index 0b37ceb..63c1fd3 100644 --- a/src/jalview/ext/jmol/JalviewJmolBinding.java +++ b/src/jalview/ext/jmol/JalviewJmolBinding.java @@ -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) { diff --git a/src/jalview/ext/jmol/JmolCommands.java b/src/jalview/ext/jmol/JmolCommands.java index 99130b4..085fbd5 100644 --- a/src/jalview/ext/jmol/JmolCommands.java +++ b/src/jalview/ext/jmol/JmolCommands.java @@ -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); + } } diff --git a/src/jalview/ext/pymol/PymolCommands.java b/src/jalview/ext/pymol/PymolCommands.java index 53b1ec5..bba11a1 100644 --- a/src/jalview/ext/pymol/PymolCommands.java +++ b/src/jalview/ext/pymol/PymolCommands.java @@ -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"); + } + } diff --git a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java index ac10b0b..8e72255 100644 --- a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java +++ b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java @@ -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); + } + } diff --git a/src/jalview/ext/rbvi/chimera/ChimeraXCommands.java b/src/jalview/ext/rbvi/chimera/ChimeraXCommands.java index b7d9ce3..90bdb6b 100644 --- a/src/jalview/ext/rbvi/chimera/ChimeraXCommands.java +++ b/src/jalview/ext/rbvi/chimera/ChimeraXCommands.java @@ -20,15 +20,15 @@ */ 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); + } } diff --git a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java index d1b8583..460b156 100644 --- a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java +++ b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java @@ -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 diff --git a/src/jalview/gui/AppJmol.java b/src/jalview/gui/AppJmol.java index 87fb1b4..7cf10e7 100644 --- a/src/jalview/gui/AppJmol.java +++ b/src/jalview/gui/AppJmol.java @@ -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 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 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 diff --git a/src/jalview/gui/ChimeraViewFrame.java b/src/jalview/gui/ChimeraViewFrame.java index 65b002b..0e5675c 100644 --- a/src/jalview/gui/ChimeraViewFrame.java +++ b/src/jalview/gui/ChimeraViewFrame.java @@ -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 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 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); diff --git a/src/jalview/gui/ChimeraXViewFrame.java b/src/jalview/gui/ChimeraXViewFrame.java index a823235..517eb4f 100644 --- a/src/jalview/gui/ChimeraXViewFrame.java +++ b/src/jalview/gui/ChimeraXViewFrame.java @@ -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 diff --git a/src/jalview/gui/PymolBindingModel.java b/src/jalview/gui/PymolBindingModel.java index 21ba95c..264a49c 100644 --- a/src/jalview/gui/PymolBindingModel.java +++ b/src/jalview/gui/PymolBindingModel.java @@ -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()) diff --git a/src/jalview/gui/PymolViewer.java b/src/jalview/gui/PymolViewer.java index 4e0ac95..c5a4c9a 100644 --- a/src/jalview/gui/PymolViewer.java +++ b/src/jalview/gui/PymolViewer.java @@ -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 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 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); } } diff --git a/src/jalview/gui/StructureViewer.java b/src/jalview/gui/StructureViewer.java index 360ddf2..617706a 100644 --- a/src/jalview/gui/StructureViewer.java +++ b/src/jalview/gui/StructureViewer.java @@ -20,14 +20,6 @@ */ 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() diff --git a/src/jalview/jbgui/GStructureViewer.java b/src/jalview/jbgui/GStructureViewer.java index 6c0beda..73180ee 100644 --- a/src/jalview/jbgui/GStructureViewer.java +++ b/src/jalview/jbgui/GStructureViewer.java @@ -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 diff --git a/src/jalview/project/Jalview2XML.java b/src/jalview/project/Jalview2XML.java index 4cfac7b..8bbe20c 100644 --- a/src/jalview/project/Jalview2XML.java +++ b/src/jalview/project/Jalview2XML.java @@ -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 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> fileData = data.getFileData() - .entrySet(); - List pdbs = new ArrayList<>(); - List allseqs = new ArrayList<>(); - for (Entry 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 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 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 pdbfilenames = new ArrayList<>(); - List seqmaps = new ArrayList<>(); - List pdbids = new ArrayList<>(); - StringBuilder newFileLoc = new StringBuilder(64); - int cp = 0, ncp, ecp; - Map 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 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 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 viewerData, AlignFrame af, + protected void createStructureViewer( + ViewerType viewerType, final Entry 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> fileData = data.getFileData() - .entrySet(); - List pdbs = new ArrayList<>(); - List allseqs = new ArrayList<>(); - for (Entry 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 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 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; } /** diff --git a/src/jalview/structure/StructureCommandsI.java b/src/jalview/structure/StructureCommandsI.java index 871d84b..5a0db0a 100644 --- a/src/jalview/structure/StructureCommandsI.java +++ b/src/jalview/structure/StructureCommandsI.java @@ -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 setAttributes( Map> featureValues); + + /** + * Returns command to open a saved structure viewer session file, or null if + * not supported + * + * @param filepath + * @return + */ + StructureCommandI openSession(String filepath); } diff --git a/src/jalview/structure/StructureSelectionManager.java b/src/jalview/structure/StructureSelectionManager.java index 798b07a..6319338 100644 --- a/src/jalview/structure/StructureSelectionManager.java +++ b/src/jalview/structure/StructureSelectionManager.java @@ -20,8 +20,20 @@ */ 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); diff --git a/src/jalview/structures/models/AAStructureBindingModel.java b/src/jalview/structures/models/AAStructureBindingModel.java index 8aa1895..870a761 100644 --- a/src/jalview/structures/models/AAStructureBindingModel.java +++ b/src/jalview/structures/models/AAStructureBindingModel.java @@ -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 * -- 1.7.10.2