2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.ext.jmol;
23 import jalview.api.FeatureRenderer;
24 import jalview.datamodel.PDBEntry;
25 import jalview.datamodel.SequenceI;
26 import jalview.gui.IProgressIndicator;
27 import jalview.gui.StructureViewer.ViewerType;
28 import jalview.io.DataSourceType;
29 import jalview.io.StructureFile;
30 import jalview.structure.AtomSpec;
31 import jalview.structure.StructureCommand;
32 import jalview.structure.StructureCommandI;
33 import jalview.structure.StructureSelectionManager;
34 import jalview.structures.models.AAStructureBindingModel;
36 import java.awt.Container;
37 import java.awt.event.ComponentEvent;
38 import java.awt.event.ComponentListener;
41 import java.util.ArrayList;
42 import java.util.List;
44 import java.util.StringTokenizer;
45 import java.util.Vector;
47 import org.jmol.adapter.smarter.SmarterJmolAdapter;
48 import org.jmol.api.JmolAppConsoleInterface;
49 import org.jmol.api.JmolSelectionListener;
50 import org.jmol.api.JmolStatusListener;
51 import org.jmol.api.JmolViewer;
52 import org.jmol.c.CBK;
53 import org.jmol.viewer.Viewer;
55 public abstract class JalviewJmolBinding extends AAStructureBindingModel
56 implements JmolStatusListener, JmolSelectionListener,
59 private String lastMessage;
62 * when true, try to search the associated datamodel for sequences that are
63 * associated with any unknown structures in the Jmol view.
65 private boolean associateNewStructs = false;
67 private Vector<String> atomsPicked = new Vector<>();
69 private String lastCommand;
71 private boolean loadedInline;
73 private StringBuffer resetLastRes = new StringBuffer();
75 public Viewer jmolViewer;
77 public JalviewJmolBinding(StructureSelectionManager ssm,
78 PDBEntry[] pdbentry, SequenceI[][] sequenceIs,
79 DataSourceType protocol)
81 super(ssm, pdbentry, sequenceIs, protocol);
82 setStructureCommands(new JmolCommands());
84 * viewer = JmolViewer.allocateViewer(renderPanel, new SmarterJmolAdapter(),
85 * "jalviewJmol", ap.av.applet .getDocumentBase(),
86 * ap.av.applet.getCodeBase(), "", this);
88 * jmolpopup = JmolPopup.newJmolPopup(viewer, true, "Jmol", true);
92 public JalviewJmolBinding(StructureSelectionManager ssm,
93 SequenceI[][] seqs, Viewer theViewer)
97 jmolViewer = theViewer;
98 jmolViewer.setJmolStatusListener(this);
99 jmolViewer.addSelectionListener(this);
100 setStructureCommands(new JmolCommands());
104 * construct a title string for the viewer window based on the data jalview
109 public String getViewerTitle()
111 return getViewerTitle("Jmol", true);
114 public void closeViewer()
116 // remove listeners for all structures in viewer
117 getSsm().removeStructureViewerListener(this, this.getStructureFiles());
118 jmolViewer.dispose();
121 releaseUIResources();
125 public List<String> executeCommand(StructureCommandI command,
132 String cmd = command.getCommand();
134 if (lastCommand == null || !lastCommand.equals(cmd))
136 jmolViewer.evalStringQuiet(cmd + "\n");
143 public void createImage(String file, String type, int quality)
145 System.out.println("JMOL CREATE IMAGE");
149 public String createImage(String fileName, String type,
150 Object textOrBytes, int quality)
152 System.out.println("JMOL CREATE IMAGE");
157 public String eval(String strEval)
159 // System.out.println(strEval);
160 // "# 'eval' is implemented only for the applet.";
164 // End StructureListener
165 // //////////////////////////
168 public float[][] functionXY(String functionName, int x, int y)
174 public float[][][] functionXYZ(String functionName, int nx, int ny,
177 // TODO Auto-generated method stub
182 * map between index of model filename returned from getPdbFile and the first
183 * index of models from this file in the viewer. Note - this is not trimmed -
184 * use getPdbFile to get number of unique models.
186 private int _modelFileNameMap[];
189 public synchronized String[] getStructureFiles()
191 List<String> mset = new ArrayList<>();
192 if (jmolViewer == null)
194 return new String[0];
197 if (modelFileNames == null)
199 int modelCount = jmolViewer.ms.mc;
200 String filePath = null;
201 for (int i = 0; i < modelCount; ++i)
203 filePath = jmolViewer.ms.getModelFileName(i);
204 if (!mset.contains(filePath))
209 modelFileNames = mset.toArray(new String[mset.size()]);
212 return modelFileNames;
216 * map from string to applet
219 public Map<String, Object> getRegistryInfo()
221 // TODO Auto-generated method stub
225 // ///////////////////////////////
226 // JmolStatusListener
228 public void handlePopupMenu(int x, int y)
230 // jmolpopup.show(x, y);
231 // jmolpopup.jpiShow(x, y);
235 * Highlight zero, one or more atoms on the structure
238 public void highlightAtoms(List<AtomSpec> atoms)
242 if (resetLastRes.length() > 0)
244 jmolViewer.evalStringQuiet(resetLastRes.toString());
245 resetLastRes.setLength(0);
247 for (AtomSpec atom : atoms)
249 highlightAtom(atom.getAtomIndex(), atom.getPdbResNum(),
250 atom.getChain(), atom.getPdbFile());
256 public void highlightAtom(int atomIndex, int pdbResNum, String chain,
259 if (modelFileNames == null)
264 // look up file model number for this pdbfile
266 // may need to adjust for URLencoding here - we don't worry about that yet.
267 while (mdlNum < modelFileNames.length
268 && !pdbfile.equals(modelFileNames[mdlNum]))
272 if (mdlNum == modelFileNames.length)
279 StringBuilder cmd = new StringBuilder(64);
280 cmd.append("select ").append(String.valueOf(pdbResNum)); // +modelNum
282 resetLastRes.append("select ").append(String.valueOf(pdbResNum)); // +modelNum
285 resetLastRes.append(":");
286 if (!chain.equals(" "))
289 resetLastRes.append(chain);
292 cmd.append(" /").append(String.valueOf(mdlNum + 1));
293 resetLastRes.append("/").append(String.valueOf(mdlNum + 1));
295 cmd.append(";wireframe 100;" + cmd.toString() + " and not hetero;");
297 resetLastRes.append(";wireframe 0;" + resetLastRes.toString()
298 + " and not hetero; spacefill 0;");
300 cmd.append("spacefill 200;select none");
302 jmolViewer.evalStringQuiet(cmd.toString());
307 private boolean debug = true;
309 private void jmolHistory(boolean enable)
311 jmolViewer.evalStringQuiet("History " + ((debug || enable) ? "on" : "off"));
314 public void loadInline(String string)
318 // viewer.loadInline(strModel, isAppend);
320 // construct fake fullPathName and fileName so we can identify the file
322 // Then, construct pass a reader for the string to Jmol.
323 // ((org.jmol.Viewer.Viewer) viewer).loadModelFromFile(fullPathName,
324 // fileName, null, reader, false, null, null, 0);
325 jmolViewer.openStringInline(string);
328 protected void mouseOverStructure(int atomIndex, final String strInfo)
331 int alocsep = strInfo.indexOf("^");
332 int mdlSep = strInfo.indexOf("/");
333 int chainSeparator = strInfo.indexOf(":"), chainSeparator1 = -1;
335 if (chainSeparator == -1)
337 chainSeparator = strInfo.indexOf(".");
338 if (mdlSep > -1 && mdlSep < chainSeparator)
340 chainSeparator1 = chainSeparator;
341 chainSeparator = mdlSep;
344 // handle insertion codes
347 pdbResNum = Integer.parseInt(
348 strInfo.substring(strInfo.indexOf("]") + 1, alocsep));
353 pdbResNum = Integer.parseInt(
354 strInfo.substring(strInfo.indexOf("]") + 1, chainSeparator));
358 if (strInfo.indexOf(":") > -1)
360 chainId = strInfo.substring(strInfo.indexOf(":") + 1,
361 strInfo.indexOf("."));
368 String pdbfilename = modelFileNames[0]; // default is first model
371 if (chainSeparator1 == -1)
373 chainSeparator1 = strInfo.indexOf(".", mdlSep);
375 String mdlId = (chainSeparator1 > -1)
376 ? strInfo.substring(mdlSep + 1, chainSeparator1)
377 : strInfo.substring(mdlSep + 1);
380 // recover PDB filename for the model hovered over.
381 int mnumber = Integer.valueOf(mdlId).intValue() - 1;
382 if (_modelFileNameMap != null)
384 int _mp = _modelFileNameMap.length - 1;
386 while (mnumber < _modelFileNameMap[_mp])
390 pdbfilename = modelFileNames[_mp];
394 if (mnumber >= 0 && mnumber < modelFileNames.length)
396 pdbfilename = modelFileNames[mnumber];
399 if (pdbfilename == null)
401 pdbfilename = new File(jmolViewer.ms.getModelFileName(mnumber))
405 } catch (Exception e)
411 * highlight position on alignment(s); if some text is returned,
412 * show this as a second line on the structure hover tooltip
414 String label = getSsm().mouseOverStructure(pdbResNum, chainId,
418 // change comma to pipe separator (newline token for Jmol)
419 label = label.replace(',', '|');
420 StringTokenizer toks = new StringTokenizer(strInfo, " ");
421 StringBuilder sb = new StringBuilder();
422 sb.append("select ").append(String.valueOf(pdbResNum)).append(":")
423 .append(chainId).append("/1");
424 sb.append(";set hoverLabel \"").append(toks.nextToken()).append(" ")
425 .append(toks.nextToken());
426 sb.append("|").append(label).append("\"");
427 executeCommand(new StructureCommand(sb.toString()), false);
431 public void notifyAtomHovered(int atomIndex, String strInfo, String data)
433 if (strInfo.equals(lastMessage))
437 lastMessage = strInfo;
440 System.err.println("Ignoring additional hover info: " + data
441 + " (other info: '" + strInfo + "' pos " + atomIndex + ")");
443 mouseOverStructure(atomIndex, strInfo);
447 * { if (history != null && strStatus != null &&
448 * !strStatus.equals("Script completed")) { history.append("\n" + strStatus);
452 public void notifyAtomPicked(int atomIndex, String strInfo,
456 * this implements the toggle label behaviour copied from the original
457 * structure viewer, MCView
461 System.err.println("Ignoring additional pick data string " + strData);
463 int chainSeparator = strInfo.indexOf(":");
465 if (chainSeparator == -1)
467 chainSeparator = strInfo.indexOf(".");
470 String picked = strInfo.substring(strInfo.indexOf("]") + 1,
472 String mdlString = "";
473 if ((p = strInfo.indexOf(":")) > -1)
475 picked += strInfo.substring(p, strInfo.indexOf("."));
478 if ((p = strInfo.indexOf("/")) > -1)
480 mdlString += strInfo.substring(p, strInfo.indexOf(" #"));
482 picked = "((" + picked + ".CA" + mdlString + ")|(" + picked + ".P"
486 if (!atomsPicked.contains(picked))
488 jmolViewer.evalStringQuiet("select " + picked + ";label %n %r:%c");
489 atomsPicked.addElement(picked);
493 jmolViewer.evalString("select " + picked + ";label off");
494 atomsPicked.removeElement(picked);
497 // TODO: in application this happens
499 // if (scriptWindow != null)
501 // scriptWindow.sendConsoleMessage(strInfo);
502 // scriptWindow.sendConsoleMessage("\n");
508 public void notifyCallback(CBK type, Object[] data)
515 notifyFileLoaded((String) data[1], (String) data[2],
516 (String) data[3], (String) data[4],
517 ((Integer) data[5]).intValue());
521 notifyAtomPicked(((Integer) data[2]).intValue(), (String) data[1],
523 // also highlight in alignment
524 // deliberate fall through
526 notifyAtomHovered(((Integer) data[2]).intValue(), (String) data[1],
530 notifyScriptTermination((String) data[2],
531 ((Integer) data[3]).intValue());
534 sendConsoleEcho((String) data[1]);
538 (data == null) ? ((String) null) : (String) data[1]);
541 // System.err.println("Ignoring error callback.");
552 "Unhandled callback " + type + " " + data[1].toString());
555 } catch (Exception e)
557 System.err.println("Squashed Jmol callback handler error:");
563 public boolean notifyEnabled(CBK callbackPick)
565 switch (callbackPick)
581 // incremented every time a load notification is successfully handled -
582 // lightweight mechanism for other threads to detect when they can start
583 // referrring to new structures.
584 private long loadNotifiesHandled = 0;
586 public long getLoadNotifiesHandled()
588 return loadNotifiesHandled;
591 public void notifyFileLoaded(String fullPathName, String fileName2,
592 String modelName, String errorMsg, int modelParts)
594 if (errorMsg != null)
596 fileLoadingError = errorMsg;
600 // TODO: deal sensibly with models loaded inLine:
601 // modelName will be null, as will fullPathName.
603 // the rest of this routine ignores the arguments, and simply interrogates
604 // the Jmol view to find out what structures it contains, and adds them to
605 // the structure selection manager.
606 fileLoadingError = null;
607 String[] oldmodels = modelFileNames;
608 modelFileNames = null;
609 boolean notifyLoaded = false;
610 String[] modelfilenames = getStructureFiles();
611 // first check if we've lost any structures
612 if (oldmodels != null && oldmodels.length > 0)
615 for (int i = 0; i < oldmodels.length; i++)
617 for (int n = 0; n < modelfilenames.length; n++)
619 if (modelfilenames[n] == oldmodels[i])
625 if (oldmodels[i] != null)
632 String[] oldmfn = new String[oldm];
634 for (int i = 0; i < oldmodels.length; i++)
636 if (oldmodels[i] != null)
638 oldmfn[oldm++] = oldmodels[i];
641 // deregister the Jmol instance for these structures - we'll add
642 // ourselves again at the end for the current structure set.
643 getSsm().removeStructureViewerListener(this, oldmfn);
647 for (int modelnum = 0; modelnum < modelfilenames.length; modelnum++)
649 String fileName = modelfilenames[modelnum];
650 boolean foundEntry = false;
651 StructureFile pdb = null;
652 String pdbfile = null;
653 // model was probably loaded inline - so check the pdb file hashcode
656 // calculate essential attributes for the pdb data imported inline.
657 // prolly need to resolve modelnumber properly - for now just use our
659 pdbfile = jmolViewer.getData(
660 "" + (1 + _modelFileNameMap[modelnum]) + ".0", "PDB");
662 // search pdbentries and sequences to find correct pdbentry for this
664 for (int pe = 0; pe < getPdbCount(); pe++)
666 boolean matches = false;
667 addSequence(pe, getSequence()[pe]);
668 if (fileName == null)
671 // see JAL-623 - need method of matching pasted data up
673 pdb = getSsm().setMapping(getSequence()[pe], getChains()[pe],
674 pdbfile, DataSourceType.PASTE,
675 getIProgressIndicator());
676 getPdbEntry(modelnum).setFile("INLINE" + pdb.getId());
683 File fl = new File(getPdbEntry(pe).getFile());
684 matches = fl.equals(new File(fileName));
688 // TODO: Jmol can in principle retrieve from CLASSLOADER but
691 // to be tested. See mantis bug
692 // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
693 DataSourceType protocol = DataSourceType.URL;
698 protocol = DataSourceType.FILE;
700 } catch (Exception e)
705 // Explicitly map to the filename used by Jmol ;
706 pdb = getSsm().setMapping(getSequence()[pe], getChains()[pe],
707 fileName, protocol, getIProgressIndicator());
708 // pdbentry[pe].getFile(), protocol);
714 stashFoundChains(pdb, fileName);
719 if (!foundEntry && associateNewStructs)
721 // this is a foreign pdb file that jalview doesn't know about - add
722 // it to the dataset and try to find a home - either on a matching
723 // sequence or as a new sequence.
724 String pdbcontent = jmolViewer.getData("/" + (modelnum + 1) + ".1",
726 // parse pdb file into a chain, etc.
727 // locate best match for pdb in associated views and add mapping to
729 // if properly registered then
735 // so finally, update the jmol bits and pieces
736 // if (jmolpopup != null)
738 // // potential for deadlock here:
739 // // jmolpopup.updateComputedMenus();
741 if (!isLoadingFromArchive())
743 jmolViewer.evalStringQuiet(
744 "model *; select backbone;restrict;cartoon;wireframe off;spacefill off");
746 // register ourselves as a listener and notify the gui that it needs to
748 getSsm().addStructureViewerListener(this);
751 FeatureRenderer fr = getFeatureRenderer(null);
757 loadNotifiesHandled++;
759 setLoadingFromArchive(false);
762 protected IProgressIndicator getIProgressIndicator()
767 public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)
769 notifyAtomPicked(iatom, strMeasure, null);
772 public abstract void notifyScriptTermination(String strStatus,
776 * display a message echoed from the jmol viewer
780 public abstract void sendConsoleEcho(String strEcho); /*
781 * { showConsole(true);
783 * history.append("\n" +
787 // /End JmolStatusListener
788 // /////////////////////////////
792 * status message - usually the response received after a script
795 public abstract void sendConsoleMessage(String strStatus);
798 public void setCallbackFunction(String callbackType,
799 String callbackFunction)
801 System.err.println("Ignoring set-callback request to associate "
802 + callbackType + " with function " + callbackFunction);
806 public void showHelp()
808 showUrl("http://jmol.sourceforge.net/docs/JmolUserGuide/", "jmolHelp");
812 * open the URL somehow
816 public abstract void showUrl(String url, String target);
819 * called to show or hide the associated console window container.
823 public abstract void showConsole(boolean show);
828 * - when true will initialise jmol's file IO system (should be false
831 * @param documentBase
833 * @param commandOptions
835 public void allocateViewer(Container renderPanel, boolean jmolfileio,
836 String htmlName, URL documentBase, URL codeBase,
837 String commandOptions)
839 allocateViewer(renderPanel, jmolfileio, htmlName, documentBase,
840 codeBase, commandOptions, null, null);
847 * - when true will initialise jmol's file IO system (should be false
850 * @param documentBase
852 * @param commandOptions
853 * @param consolePanel
854 * - panel to contain Jmol console
855 * @param buttonsToShow
856 * - buttons to show on the console, in ordr
858 public void allocateViewer(Container renderPanel, boolean jmolfileio,
859 String htmlName, URL documentBase, URL codeBase,
860 String commandOptions, final Container consolePanel,
861 String buttonsToShow)
863 if (commandOptions == null)
867 jmolViewer = (Viewer) JmolViewer.allocateViewer(renderPanel,
868 (jmolfileio ? new SmarterJmolAdapter() : null),
869 htmlName + ((Object) this).toString(), documentBase, codeBase,
870 commandOptions, this);
872 jmolViewer.setJmolStatusListener(this); // extends JmolCallbackListener
874 console = createJmolConsole(consolePanel, buttonsToShow);
875 if (consolePanel != null)
877 consolePanel.addComponentListener(this);
883 protected abstract JmolAppConsoleInterface createJmolConsole(
884 Container consolePanel, String buttonsToShow);
886 protected org.jmol.api.JmolAppConsoleInterface console = null;
889 public int[] resizeInnerPanel(String data)
891 // Jalview doesn't honour resize panel requests
898 protected void closeConsole()
904 console.setVisible(false);
907 } catch (Exception x)
916 * ComponentListener method
919 public void componentMoved(ComponentEvent e)
924 * ComponentListener method
927 public void componentResized(ComponentEvent e)
932 * ComponentListener method
935 public void componentShown(ComponentEvent e)
941 * ComponentListener method
944 public void componentHidden(ComponentEvent e)
950 protected String getModelIdForFile(String pdbFile)
952 if (modelFileNames == null)
956 for (int i = 0; i < modelFileNames.length; i++)
958 if (modelFileNames[i].equalsIgnoreCase(pdbFile))
960 return String.valueOf(i);
967 protected ViewerType getViewerType()
969 return ViewerType.JMOL;
973 protected String getModelId(int pdbfnum, String file)
975 return String.valueOf(pdbfnum + 1);