X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fext%2Frbvi%2Fchimera%2FJalviewChimeraBinding.java;h=98f2e1a7a7c406ed418b8665e234643a97823ba1;hb=85937698cb3a6d1fb8be1cfa7a1677e632da7be9;hp=f24a976ff4c6bcb296bbf322befce144fb1d8c71;hpb=25aaaa87042b3f507ad4348120df7dd073182759;p=jalview.git diff --git a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java index f24a976..98f2e1a 100644 --- a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java +++ b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java @@ -1,6 +1,6 @@ /* - * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2b1) - * Copyright (C) 2014 The Jalview Authors + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors * * This file is part of Jalview. * @@ -20,6 +20,18 @@ */ package jalview.ext.rbvi.chimera; +import java.awt.Color; +import java.net.BindException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import ext.edu.ucsf.rbvi.strucviz2.ChimeraManager; +import ext.edu.ucsf.rbvi.strucviz2.ChimeraModel; +import ext.edu.ucsf.rbvi.strucviz2.StructureManager; +import ext.edu.ucsf.rbvi.strucviz2.StructureManager.ModelType; + import jalview.api.AlignmentViewPanel; import jalview.api.FeatureRenderer; import jalview.api.SequenceRenderer; @@ -28,8 +40,10 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.ColumnSelection; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; +import jalview.httpserver.AbstractRequestHandler; import jalview.schemes.ColourSchemeI; import jalview.schemes.ResidueProperties; +import jalview.structure.AtomSpec; import jalview.structure.StructureMapping; import jalview.structure.StructureMappingcommandSet; import jalview.structure.StructureSelectionManager; @@ -37,21 +51,10 @@ import jalview.structures.models.AAStructureBindingModel; import jalview.util.Comparison; import jalview.util.MessageManager; -import java.awt.Color; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import ext.edu.ucsf.rbvi.strucviz2.ChimeraManager; -import ext.edu.ucsf.rbvi.strucviz2.ChimeraModel; -import ext.edu.ucsf.rbvi.strucviz2.StructureManager; -import ext.edu.ucsf.rbvi.strucviz2.StructureManager.ModelType; - public abstract class JalviewChimeraBinding extends AAStructureBindingModel { + // Chimera clause to exclude alternate locations in atom selection + private static final String NO_ALTLOCS = "&~@.B-Z&~@.2-9"; private static final boolean debug = false; @@ -61,9 +64,17 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel private StructureManager csm; + /* + * Object through which we talk to Chimera + */ private ChimeraManager viewer; /* + * Object which listens to Chimera notifications + */ + private AbstractRequestHandler chimeraListener; + + /* * set if chimera state is being restored from some source - instructs binding * not to apply default display style when structure set is updated for first * time. @@ -87,8 +98,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel private Map chainFile; - private StringBuffer eval = new StringBuffer(); - public String fileLoadingError; /* @@ -104,11 +113,25 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel private String lastCommand; - private String lastMessage; - private boolean loadedInline; /** + * current set of model filenames loaded + */ + String[] modelFileNames = null; + + String lastMousedOverAtomSpec; + + private List lastReply; + + /* + * incremented every time a load notification is successfully handled - + * lightweight mechanism for other threads to detect when they can start + * referring to new structures. + */ + private long loadNotifiesHandled = 0; + + /** * Open a PDB structure file in Chimera and set up mappings from Jalview. * * We check if the PDB model id is already loaded in Chimera, if so don't @@ -140,13 +163,20 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel /* * If Chimera doesn't yet have this model, ask it to open it, and retrieve - * the model names added by Chimera. + * the model name(s) added by Chimera. */ if (!alreadyOpen) { viewer.openModel(file, pe.getId(), ModelType.PDB_MODEL); - modelsToMap = viewer.getModelList(); - modelsToMap.removeAll(oldList); + List newList = viewer.getModelList(); + // JAL-1728 newList.removeAll(oldList) does not work + for (ChimeraModel cm : newList) + { + if (cm.getModelName().equals(pe.getId())) + { + modelsToMap.add(cm); + } + } } chimeraMaps.put(file, modelsToMap); @@ -173,16 +203,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel } /** - * current set of model filenames loaded - */ - String[] modelFileNames = null; - - - StringBuffer resetLastRes = new StringBuffer(); - - private List lastReply; - - /** * Constructor * * @param ssm @@ -201,6 +221,23 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel } /** + * Start a dedicated HttpServer to listen for Chimera notifications, and tell + * it to start listening + */ + public void startChimeraListener() + { + try + { + chimeraListener = new ChimeraListener(this); + viewer.startListening(chimeraListener.getUri()); + } catch (BindException e) + { + System.err.println("Failed to start Chimera listener: " + + e.getMessage()); + } + } + + /** * Constructor * * @param ssm @@ -259,8 +296,8 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel } /** - * Close down the Jalview viewer, and (optionally) the associated Chimera - * window. + * Close down the Jalview viewer and listener, and (optionally) the associated + * Chimera window. */ public void closeViewer(boolean closeChimera) { @@ -269,8 +306,14 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel { viewer.exitChimera(); } + if (this.chimeraListener != null) + { + chimeraListener.shutdown(); + chimeraListener = null; + } lastCommand = null; viewer = null; + releaseUIResources(); } @@ -584,9 +627,12 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel // TODO: handle sub-models command.append(selcom[pdbfnum]); command.append("@" + atomSpec[pdbfnum]); + // JAL-1757 exclude alternative CA locations + command.append(NO_ALTLOCS); command.append(" #" + refStructure /* +".1" */); command.append(selcom[refStructure]); command.append("@" + atomSpec[refStructure]); + command.append(NO_ALTLOCS); } if (selectioncom.length() > 0) { @@ -693,7 +739,8 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel } AlignmentI alignment = alignmentv.getAlignment(); - for (jalview.structure.StructureMappingcommandSet cpdbbyseq : getColourBySequenceCommands(files, sr, fr, alignment)) + for (jalview.structure.StructureMappingcommandSet cpdbbyseq : getColourBySequenceCommands( + files, sr, fr, alignment)) { for (String command : cpdbbyseq.commands) { @@ -713,10 +760,8 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel String[] files, SequenceRenderer sr, FeatureRenderer fr, AlignmentI alignment) { - return ChimeraCommands - .getColourBySequenceCommand(getSsm(), files, getSequence(), sr, - fr, - alignment); + return ChimeraCommands.getColourBySequenceCommand(getSsm(), files, + getSequence(), sr, fr, alignment); } /** @@ -740,7 +785,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel } } - + // End StructureListener // ////////////////////////// @@ -843,292 +888,130 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel public abstract SequenceRenderer getSequenceRenderer( AlignmentViewPanel alignment); - // jmol/ssm only + /** + * Construct and send a command to highlight an atom. + * + *
+   * Done by generating a command like (to 'highlight' position 44)
+   *   ~show #0:43.C;show #0:44.C
+   * Note this removes the highlight from the previous position.
+   * 
+ */ public void highlightAtom(int atomIndex, int pdbResNum, String chain, String pdbfile) { List cms = chimeraMaps.get(pdbfile); if (cms != null) { - int mdlNum = cms.get(0).getModelNumber(); - - viewerCommandHistory(false); - // viewer.stopListening(); - if (resetLastRes.length() > 0) + StringBuilder sb = new StringBuilder(); + sb.append(" #" + cms.get(0).getModelNumber()); + sb.append(":" + pdbResNum); + if (!chain.equals(" ")) { - eval.setLength(0); - eval.append(resetLastRes.toString() + ";"); + sb.append("." + chain); } + String atomSpec = sb.toString(); - eval.append("display "); // +modelNum - - resetLastRes.setLength(0); - resetLastRes.append("~display "); + StringBuilder command = new StringBuilder(32); + if (lastMousedOverAtomSpec != null) { - eval.append(" #" + (mdlNum)); - resetLastRes.append(" #" + (mdlNum)); + command.append("~show " + lastMousedOverAtomSpec + ";"); } - // complete select string - - eval.append(":" + pdbResNum); - resetLastRes.append(":" + pdbResNum); - if (!chain.equals(" ")) + viewerCommandHistory(false); + command.append("show ").append(atomSpec); + String cmd = command.toString(); + if (cmd.length() > 0) { - eval.append("." + chain); - resetLastRes.append("." + chain); + viewer.stopListening(chimeraListener.getUri()); + viewer.sendChimeraCommand(cmd, false); + viewer.startListening(chimeraListener.getUri()); } - - viewer.sendChimeraCommand(eval.toString(), false); viewerCommandHistory(true); - // viewer.startListening(); + this.lastMousedOverAtomSpec = atomSpec; } } - private void log(String message) - { - System.err.println("## Chimera log: " + message); - } - - private void viewerCommandHistory(boolean enable) - { - // log("(Not yet implemented) History " - // + ((debug || enable) ? "on" : "off")); - } - - public void loadInline(String string) + /** + * Query Chimera for its current selection, and highlight it on the alignment + */ + public void highlightChimeraSelection() { - loadedInline = true; - // TODO: re JAL-623 - // viewer.loadInline(strModel, isAppend); - // could do this: - // construct fake fullPathName and fileName so we can identify the file - // later. - // Then, construct pass a reader for the string to Jmol. - // ((org.jmol.Viewer.Viewer) viewer).loadModelFromFile(fullPathName, - // fileName, null, reader, false, null, null, 0); - // viewer.openStringInline(string); - log("cannot load inline in Chimera, yet"); - } + /* + * Ask Chimera for its current selection + */ + List selection = viewer.getSelectedResidueSpecs(); - public void mouseOverStructure(int atomIndex, String strInfo) - { - // function to parse a mouseOver event from Chimera - // - int pdbResNum; - int alocsep = strInfo.indexOf("^"); - int mdlSep = strInfo.indexOf("/"); - int chainSeparator = strInfo.indexOf(":"), chainSeparator1 = -1; - - if (chainSeparator == -1) + /* + * Parse model number, residue and chain for each selected position, + * formatted as #0:123.A or #1.2:87.B (#model.submodel:residue.chain) + */ + List atomSpecs = new ArrayList(); + for (String atomSpec : selection) { - chainSeparator = strInfo.indexOf("."); - if (mdlSep > -1 && mdlSep < chainSeparator) + int colonPos = atomSpec.indexOf(":"); + if (colonPos == -1) { - chainSeparator1 = chainSeparator; - chainSeparator = mdlSep; + continue; // malformed } - } - // handle insertion codes - if (alocsep != -1) - { - pdbResNum = Integer.parseInt(strInfo.substring( - strInfo.indexOf("]") + 1, alocsep)); - - } - else - { - pdbResNum = Integer.parseInt(strInfo.substring( - strInfo.indexOf("]") + 1, chainSeparator)); - } - String chainId; - - if (strInfo.indexOf(":") > -1) - { - chainId = strInfo.substring(strInfo.indexOf(":") + 1, - strInfo.indexOf(".")); - } - else - { - chainId = " "; - } - - String pdbfilename = modelFileNames[frameNo]; // default is first or current - // model - if (mdlSep > -1) - { - if (chainSeparator1 == -1) - { - chainSeparator1 = strInfo.indexOf(".", mdlSep); + + int hashPos = atomSpec.indexOf("#"); + String modelSubmodel = atomSpec.substring(hashPos + 1, colonPos); + int dotPos = modelSubmodel.indexOf("."); + int modelId = 0; + try { + modelId = Integer.valueOf(dotPos == -1 ? modelSubmodel + : modelSubmodel.substring(0, dotPos)); + } catch (NumberFormatException e) { + // ignore, default to model 0 } - String mdlId = (chainSeparator1 > -1) ? strInfo.substring(mdlSep + 1, - chainSeparator1) : strInfo.substring(mdlSep + 1); - try + + String residueChain = atomSpec.substring(colonPos + 1); + dotPos = residueChain.indexOf("."); + int pdbResNum = Integer.parseInt(dotPos == -1 ? residueChain + : residueChain.substring(0, dotPos)); + + String chainId = dotPos == -1 ? "" : residueChain + .substring(dotPos + 1); + + /* + * Work out the pdbfilename from the model number + */ + String pdbfilename = modelFileNames[frameNo]; + findfileloop: for (String pdbfile : this.chimeraMaps.keySet()) { - // recover PDB filename for the model hovered over. - int _mp = _modelFileNameMap.length - 1, mnumber = new Integer(mdlId) - .intValue() - 1; - while (mnumber < _modelFileNameMap[_mp]) - { - _mp--; - } - pdbfilename = modelFileNames[_mp]; - if (pdbfilename == null) + for (ChimeraModel cm : chimeraMaps.get(pdbfile)) { - // pdbfilename = new File(viewer.getModelFileName(mnumber)) - // .getAbsolutePath(); + if (cm.getModelNumber() == modelId) + { + pdbfilename = pdbfile; + break findfileloop; + } } - - } catch (Exception e) - { } - ; - } - if (lastMessage == null || !lastMessage.equals(strInfo)) - { - getSsm().mouseOverStructure(pdbResNum, chainId, pdbfilename); + atomSpecs.add(new AtomSpec(pdbfilename, chainId, pdbResNum, 0)); } - lastMessage = strInfo; + /* + * Broadcast the selection (which may be empty, if the user just cleared all + * selections) + */ + getSsm().mouseOverStructure(atomSpecs); } - public void notifyAtomPicked(int atomIndex, String strInfo, String strData) + private void log(String message) { - /** - * this implements the toggle label behaviour copied from the original - * structure viewer, MCView - */ - if (strData != null) - { - System.err.println("Ignoring additional pick data string " + strData); - } - // rewrite these selections for chimera (DNA, RNA and protein) - int chainSeparator = strInfo.indexOf(":"); - int p = 0; - if (chainSeparator == -1) - { - chainSeparator = strInfo.indexOf("."); - } - - String picked = strInfo.substring(strInfo.indexOf("]") + 1, - chainSeparator); - String mdlString = ""; - if ((p = strInfo.indexOf(":")) > -1) - { - picked += strInfo.substring(p + 1, strInfo.indexOf(".")); - } - - if ((p = strInfo.indexOf("/")) > -1) - { - mdlString += strInfo.substring(p, strInfo.indexOf(" #")); - } - picked = "((" + picked + ".CA" + mdlString + ")|(" + picked + ".P" - + mdlString + "))"; - viewerCommandHistory(false); - - if (!atomsPicked.contains(picked)) - { - viewer.select(picked); - atomsPicked.add(picked); - } - else - { - viewer.select("not " + picked); - atomsPicked.remove(picked); - } - viewerCommandHistory(true); - // TODO: in application this happens - // - // if (scriptWindow != null) - // { - // scriptWindow.sendConsoleMessage(strInfo); - // scriptWindow.sendConsoleMessage("\n"); - // } - + System.err.println("## Chimera log: " + message); } - // incremented every time a load notification is successfully handled - - // lightweight mechanism for other threads to detect when they can start - // referring to new structures. - private long loadNotifiesHandled = 0; - - public long getLoadNotifiesHandled() + private void viewerCommandHistory(boolean enable) { - return loadNotifiesHandled; + // log("(Not yet implemented) History " + // + ((debug || enable) ? "on" : "off")); } - public void notifyFileLoaded(String fullPathName, String fileName2, - String modelName, String errorMsg, int modelParts) + public long getLoadNotifiesHandled() { - if (errorMsg != null) - { - fileLoadingError = errorMsg; - refreshGUI(); - return; - } - // TODO: deal sensibly with models loaded inLine: - // modelName will be null, as will fullPathName. - - // the rest of this routine ignores the arguments, and simply interrogates - // the Jmol view to find out what structures it contains, and adds them to - // the structure selection manager. - fileLoadingError = null; - String[] oldmodels = modelFileNames; - modelFileNames = null; - chainNames = new ArrayList(); - chainFile = new HashMap(); - boolean notifyLoaded = false; - String[] modelfilenames = getPdbFile(); - // first check if we've lost any structures - if (oldmodels != null && oldmodels.length > 0) - { - int oldm = 0; - for (int i = 0; i < oldmodels.length; i++) - { - for (int n = 0; n < modelfilenames.length; n++) - { - if (modelfilenames[n] == oldmodels[i]) - { - oldmodels[i] = null; - break; - } - } - if (oldmodels[i] != null) - { - oldm++; - } - } - if (oldm > 0) - { - String[] oldmfn = new String[oldm]; - oldm = 0; - for (int i = 0; i < oldmodels.length; i++) - { - if (oldmodels[i] != null) - { - oldmfn[oldm++] = oldmodels[i]; - } - } - // deregister the Jmol instance for these structures - we'll add - // ourselves again at the end for the current structure set. - getSsm().removeStructureViewerListener(this, oldmfn); - } - } - - // register ourselves as a listener and notify the gui that it needs to - // update itself. - getSsm().addStructureViewerListener(this); - - if (notifyLoaded) - { - FeatureRenderer fr = getFeatureRenderer(null); - if (fr != null) - { - fr.featuresAdded(); - } - refreshGUI(); - loadNotifiesHandled++; - } - setLoadingFromArchive(false); + return loadNotifiesHandled; } public void setJalviewColourScheme(ColourSchemeI cs) @@ -1140,26 +1023,16 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel return; } - String res; - int index; - Color col; // Chimera expects RBG values in the range 0-1 final double normalise = 255D; viewerCommandHistory(false); - // TODO: Switch between nucleotide or aa selection expressions - Enumeration en = ResidueProperties.aa3Hash.keys(); StringBuilder command = new StringBuilder(128); - command.append("color white;"); - while (en.hasMoreElements()) - { - res = en.nextElement().toString(); - index = ((Integer) ResidueProperties.aa3Hash.get(res)).intValue(); - if (index > 20) - { - continue; - } - col = cs.findColour(ResidueProperties.aa[index].charAt(0)); + List residueSet = ResidueProperties.getResidues(isNucleotide(), + false); + for (String res : residueSet) + { + Color col = cs.findColour(res.charAt(0)); command.append("color " + col.getRed() / normalise + "," + col.getGreen() / normalise + "," + col.getBlue() / normalise + " ::" + res + ";");