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.AlignmentI;
25 import jalview.datamodel.HiddenColumns;
26 import jalview.datamodel.PDBEntry;
27 import jalview.datamodel.SequenceI;
28 import jalview.gui.IProgressIndicator;
29 import jalview.gui.StructureViewer.ViewerType;
30 import jalview.io.DataSourceType;
31 import jalview.io.StructureFile;
32 import jalview.structure.AtomSpec;
33 import jalview.structure.StructureCommandsI.SuperposeData;
34 import jalview.structure.StructureSelectionManager;
35 import jalview.structures.models.AAStructureBindingModel;
36 import jalview.util.MessageManager;
38 import java.awt.Container;
39 import java.awt.event.ComponentEvent;
40 import java.awt.event.ComponentListener;
43 import java.util.ArrayList;
44 import java.util.BitSet;
45 import java.util.List;
47 import java.util.StringTokenizer;
48 import java.util.Vector;
50 import org.jmol.adapter.smarter.SmarterJmolAdapter;
51 import org.jmol.api.JmolAppConsoleInterface;
52 import org.jmol.api.JmolSelectionListener;
53 import org.jmol.api.JmolStatusListener;
54 import org.jmol.api.JmolViewer;
55 import org.jmol.c.CBK;
56 import org.jmol.viewer.Viewer;
58 public abstract class JalviewJmolBinding extends AAStructureBindingModel
59 implements JmolStatusListener, JmolSelectionListener,
62 private String lastMessage;
65 * when true, try to search the associated datamodel for sequences that are
66 * associated with any unknown structures in the Jmol view.
68 private boolean associateNewStructs = false;
70 private Vector<String> atomsPicked = new Vector<>();
72 private String lastCommand;
74 private boolean loadedInline;
76 private StringBuffer resetLastRes = new StringBuffer();
78 public Viewer jmolViewer;
80 public JalviewJmolBinding(StructureSelectionManager ssm,
81 PDBEntry[] pdbentry, SequenceI[][] sequenceIs,
82 DataSourceType protocol)
84 super(ssm, pdbentry, sequenceIs, protocol);
85 setStructureCommands(new JmolCommands());
87 * viewer = JmolViewer.allocateViewer(renderPanel, new SmarterJmolAdapter(),
88 * "jalviewJmol", ap.av.applet .getDocumentBase(),
89 * ap.av.applet.getCodeBase(), "", this);
91 * jmolpopup = JmolPopup.newJmolPopup(viewer, true, "Jmol", true);
95 public JalviewJmolBinding(StructureSelectionManager ssm,
96 SequenceI[][] seqs, Viewer theViewer)
100 jmolViewer = theViewer;
101 jmolViewer.setJmolStatusListener(this);
102 jmolViewer.addSelectionListener(this);
103 setStructureCommands(new JmolCommands());
107 * construct a title string for the viewer window based on the data jalview
112 public String getViewerTitle()
114 return getViewerTitle("Jmol", true);
117 public void closeViewer()
119 // remove listeners for all structures in viewer
120 getSsm().removeStructureViewerListener(this, this.getStructureFiles());
121 jmolViewer.dispose();
124 releaseUIResources();
128 public List<String> executeCommand(String command, boolean getReply)
135 if (lastCommand == null || !lastCommand.equals(command))
137 jmolViewer.evalStringQuiet(command + "\n");
140 lastCommand = command;
144 public void createImage(String file, String type, int quality)
146 System.out.println("JMOL CREATE IMAGE");
150 public String createImage(String fileName, String type,
151 Object textOrBytes, int quality)
153 System.out.println("JMOL CREATE IMAGE");
158 public String eval(String strEval)
160 // System.out.println(strEval);
161 // "# 'eval' is implemented only for the applet.";
165 // End StructureListener
166 // //////////////////////////
169 public float[][] functionXY(String functionName, int x, int y)
175 public float[][][] functionXYZ(String functionName, int nx, int ny,
178 // TODO Auto-generated method stub
183 * map between index of model filename returned from getPdbFile and the first
184 * index of models from this file in the viewer. Note - this is not trimmed -
185 * use getPdbFile to get number of unique models.
187 private int _modelFileNameMap[];
190 public synchronized String[] getStructureFiles()
192 List<String> mset = new ArrayList<>();
193 if (jmolViewer == null)
195 return new String[0];
198 if (modelFileNames == null)
200 int modelCount = jmolViewer.ms.mc;
201 String filePath = null;
202 for (int i = 0; i < modelCount; ++i)
204 filePath = jmolViewer.ms.getModelFileName(i);
205 if (!mset.contains(filePath))
210 modelFileNames = mset.toArray(new String[mset.size()]);
213 return modelFileNames;
217 * map from string to applet
220 public Map<String, Object> getRegistryInfo()
222 // TODO Auto-generated method stub
226 // ///////////////////////////////
227 // JmolStatusListener
229 public void handlePopupMenu(int x, int y)
231 // jmolpopup.show(x, y);
232 // jmolpopup.jpiShow(x, y);
236 * Highlight zero, one or more atoms on the structure
239 public void highlightAtoms(List<AtomSpec> atoms)
243 if (resetLastRes.length() > 0)
245 jmolViewer.evalStringQuiet(resetLastRes.toString());
246 resetLastRes.setLength(0);
248 for (AtomSpec atom : atoms)
250 highlightAtom(atom.getAtomIndex(), atom.getPdbResNum(),
251 atom.getChain(), atom.getPdbFile());
257 public void highlightAtom(int atomIndex, int pdbResNum, String chain,
260 if (modelFileNames == null)
265 // look up file model number for this pdbfile
267 // may need to adjust for URLencoding here - we don't worry about that yet.
268 while (mdlNum < modelFileNames.length
269 && !pdbfile.equals(modelFileNames[mdlNum]))
273 if (mdlNum == modelFileNames.length)
280 StringBuilder cmd = new StringBuilder(64);
281 cmd.append("select ").append(String.valueOf(pdbResNum)); // +modelNum
283 resetLastRes.append("select ").append(String.valueOf(pdbResNum)); // +modelNum
286 resetLastRes.append(":");
287 if (!chain.equals(" "))
290 resetLastRes.append(chain);
293 cmd.append(" /").append(String.valueOf(mdlNum + 1));
294 resetLastRes.append("/").append(String.valueOf(mdlNum + 1));
296 cmd.append(";wireframe 100;" + cmd.toString() + " and not hetero;");
298 resetLastRes.append(";wireframe 0;" + resetLastRes.toString()
299 + " and not hetero; spacefill 0;");
301 cmd.append("spacefill 200;select none");
303 jmolViewer.evalStringQuiet(cmd.toString());
308 private boolean debug = true;
310 private void jmolHistory(boolean enable)
312 jmolViewer.evalStringQuiet("History " + ((debug || enable) ? "on" : "off"));
315 public void loadInline(String string)
319 // viewer.loadInline(strModel, isAppend);
321 // construct fake fullPathName and fileName so we can identify the file
323 // Then, construct pass a reader for the string to Jmol.
324 // ((org.jmol.Viewer.Viewer) viewer).loadModelFromFile(fullPathName,
325 // fileName, null, reader, false, null, null, 0);
326 jmolViewer.openStringInline(string);
329 protected void mouseOverStructure(int atomIndex, final String strInfo)
332 int alocsep = strInfo.indexOf("^");
333 int mdlSep = strInfo.indexOf("/");
334 int chainSeparator = strInfo.indexOf(":"), chainSeparator1 = -1;
336 if (chainSeparator == -1)
338 chainSeparator = strInfo.indexOf(".");
339 if (mdlSep > -1 && mdlSep < chainSeparator)
341 chainSeparator1 = chainSeparator;
342 chainSeparator = mdlSep;
345 // handle insertion codes
348 pdbResNum = Integer.parseInt(
349 strInfo.substring(strInfo.indexOf("]") + 1, alocsep));
354 pdbResNum = Integer.parseInt(
355 strInfo.substring(strInfo.indexOf("]") + 1, chainSeparator));
359 if (strInfo.indexOf(":") > -1)
361 chainId = strInfo.substring(strInfo.indexOf(":") + 1,
362 strInfo.indexOf("."));
369 String pdbfilename = modelFileNames[0]; // default is first model
372 if (chainSeparator1 == -1)
374 chainSeparator1 = strInfo.indexOf(".", mdlSep);
376 String mdlId = (chainSeparator1 > -1)
377 ? strInfo.substring(mdlSep + 1, chainSeparator1)
378 : strInfo.substring(mdlSep + 1);
381 // recover PDB filename for the model hovered over.
382 int mnumber = Integer.valueOf(mdlId).intValue() - 1;
383 if (_modelFileNameMap != null)
385 int _mp = _modelFileNameMap.length - 1;
387 while (mnumber < _modelFileNameMap[_mp])
391 pdbfilename = modelFileNames[_mp];
395 if (mnumber >= 0 && mnumber < modelFileNames.length)
397 pdbfilename = modelFileNames[mnumber];
400 if (pdbfilename == null)
402 pdbfilename = new File(jmolViewer.ms.getModelFileName(mnumber))
406 } catch (Exception e)
412 * highlight position on alignment(s); if some text is returned,
413 * show this as a second line on the structure hover tooltip
415 String label = getSsm().mouseOverStructure(pdbResNum, chainId,
419 // change comma to pipe separator (newline token for Jmol)
420 label = label.replace(',', '|');
421 StringTokenizer toks = new StringTokenizer(strInfo, " ");
422 StringBuilder sb = new StringBuilder();
423 sb.append("select ").append(String.valueOf(pdbResNum)).append(":")
424 .append(chainId).append("/1");
425 sb.append(";set hoverLabel \"").append(toks.nextToken()).append(" ")
426 .append(toks.nextToken());
427 sb.append("|").append(label).append("\"");
428 executeCommand(sb.toString(), false);
432 public void notifyAtomHovered(int atomIndex, String strInfo, String data)
434 if (strInfo.equals(lastMessage))
438 lastMessage = strInfo;
441 System.err.println("Ignoring additional hover info: " + data
442 + " (other info: '" + strInfo + "' pos " + atomIndex + ")");
444 mouseOverStructure(atomIndex, strInfo);
448 * { if (history != null && strStatus != null &&
449 * !strStatus.equals("Script completed")) { history.append("\n" + strStatus);
453 public void notifyAtomPicked(int atomIndex, String strInfo,
457 * this implements the toggle label behaviour copied from the original
458 * structure viewer, MCView
462 System.err.println("Ignoring additional pick data string " + strData);
464 int chainSeparator = strInfo.indexOf(":");
466 if (chainSeparator == -1)
468 chainSeparator = strInfo.indexOf(".");
471 String picked = strInfo.substring(strInfo.indexOf("]") + 1,
473 String mdlString = "";
474 if ((p = strInfo.indexOf(":")) > -1)
476 picked += strInfo.substring(p, strInfo.indexOf("."));
479 if ((p = strInfo.indexOf("/")) > -1)
481 mdlString += strInfo.substring(p, strInfo.indexOf(" #"));
483 picked = "((" + picked + ".CA" + mdlString + ")|(" + picked + ".P"
487 if (!atomsPicked.contains(picked))
489 jmolViewer.evalStringQuiet("select " + picked + ";label %n %r:%c");
490 atomsPicked.addElement(picked);
494 jmolViewer.evalString("select " + picked + ";label off");
495 atomsPicked.removeElement(picked);
498 // TODO: in application this happens
500 // if (scriptWindow != null)
502 // scriptWindow.sendConsoleMessage(strInfo);
503 // scriptWindow.sendConsoleMessage("\n");
509 public void notifyCallback(CBK type, Object[] data)
516 notifyFileLoaded((String) data[1], (String) data[2],
517 (String) data[3], (String) data[4],
518 ((Integer) data[5]).intValue());
522 notifyAtomPicked(((Integer) data[2]).intValue(), (String) data[1],
524 // also highlight in alignment
525 // deliberate fall through
527 notifyAtomHovered(((Integer) data[2]).intValue(), (String) data[1],
531 notifyScriptTermination((String) data[2],
532 ((Integer) data[3]).intValue());
535 sendConsoleEcho((String) data[1]);
539 (data == null) ? ((String) null) : (String) data[1]);
542 // System.err.println("Ignoring error callback.");
553 "Unhandled callback " + type + " " + data[1].toString());
556 } catch (Exception e)
558 System.err.println("Squashed Jmol callback handler error:");
564 public boolean notifyEnabled(CBK callbackPick)
566 switch (callbackPick)
582 // incremented every time a load notification is successfully handled -
583 // lightweight mechanism for other threads to detect when they can start
584 // referrring to new structures.
585 private long loadNotifiesHandled = 0;
587 public long getLoadNotifiesHandled()
589 return loadNotifiesHandled;
592 public void notifyFileLoaded(String fullPathName, String fileName2,
593 String modelName, String errorMsg, int modelParts)
595 if (errorMsg != null)
597 fileLoadingError = errorMsg;
601 // TODO: deal sensibly with models loaded inLine:
602 // modelName will be null, as will fullPathName.
604 // the rest of this routine ignores the arguments, and simply interrogates
605 // the Jmol view to find out what structures it contains, and adds them to
606 // the structure selection manager.
607 fileLoadingError = null;
608 String[] oldmodels = modelFileNames;
609 modelFileNames = null;
610 boolean notifyLoaded = false;
611 String[] modelfilenames = getStructureFiles();
612 // first check if we've lost any structures
613 if (oldmodels != null && oldmodels.length > 0)
616 for (int i = 0; i < oldmodels.length; i++)
618 for (int n = 0; n < modelfilenames.length; n++)
620 if (modelfilenames[n] == oldmodels[i])
626 if (oldmodels[i] != null)
633 String[] oldmfn = new String[oldm];
635 for (int i = 0; i < oldmodels.length; i++)
637 if (oldmodels[i] != null)
639 oldmfn[oldm++] = oldmodels[i];
642 // deregister the Jmol instance for these structures - we'll add
643 // ourselves again at the end for the current structure set.
644 getSsm().removeStructureViewerListener(this, oldmfn);
648 for (int modelnum = 0; modelnum < modelfilenames.length; modelnum++)
650 String fileName = modelfilenames[modelnum];
651 boolean foundEntry = false;
652 StructureFile pdb = null;
653 String pdbfile = null;
654 // model was probably loaded inline - so check the pdb file hashcode
657 // calculate essential attributes for the pdb data imported inline.
658 // prolly need to resolve modelnumber properly - for now just use our
660 pdbfile = jmolViewer.getData(
661 "" + (1 + _modelFileNameMap[modelnum]) + ".0", "PDB");
663 // search pdbentries and sequences to find correct pdbentry for this
665 for (int pe = 0; pe < getPdbCount(); pe++)
667 boolean matches = false;
668 addSequence(pe, getSequence()[pe]);
669 if (fileName == null)
672 // see JAL-623 - need method of matching pasted data up
674 pdb = getSsm().setMapping(getSequence()[pe], getChains()[pe],
675 pdbfile, DataSourceType.PASTE,
676 getIProgressIndicator());
677 getPdbEntry(modelnum).setFile("INLINE" + pdb.getId());
684 File fl = new File(getPdbEntry(pe).getFile());
685 matches = fl.equals(new File(fileName));
689 // TODO: Jmol can in principle retrieve from CLASSLOADER but
692 // to be tested. See mantis bug
693 // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
694 DataSourceType protocol = DataSourceType.URL;
699 protocol = DataSourceType.FILE;
701 } catch (Exception e)
706 // Explicitly map to the filename used by Jmol ;
707 pdb = getSsm().setMapping(getSequence()[pe], getChains()[pe],
708 fileName, protocol, getIProgressIndicator());
709 // pdbentry[pe].getFile(), protocol);
715 // add an entry for every chain in the model
716 for (int i = 0; i < pdb.getChains().size(); i++)
718 String chid = pdb.getId() + ":"
719 + pdb.getChains().elementAt(i).id;
720 addChainFile(chid, fileName);
721 getChainNames().add(chid);
727 if (!foundEntry && associateNewStructs)
729 // this is a foreign pdb file that jalview doesn't know about - add
730 // it to the dataset and try to find a home - either on a matching
731 // sequence or as a new sequence.
732 String pdbcontent = jmolViewer.getData("/" + (modelnum + 1) + ".1",
734 // parse pdb file into a chain, etc.
735 // locate best match for pdb in associated views and add mapping to
737 // if properly registered then
743 // so finally, update the jmol bits and pieces
744 // if (jmolpopup != null)
746 // // potential for deadlock here:
747 // // jmolpopup.updateComputedMenus();
749 if (!isLoadingFromArchive())
751 jmolViewer.evalStringQuiet(
752 "model *; select backbone;restrict;cartoon;wireframe off;spacefill off");
754 // register ourselves as a listener and notify the gui that it needs to
756 getSsm().addStructureViewerListener(this);
759 FeatureRenderer fr = getFeatureRenderer(null);
765 loadNotifiesHandled++;
767 setLoadingFromArchive(false);
770 protected IProgressIndicator getIProgressIndicator()
775 public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)
777 notifyAtomPicked(iatom, strMeasure, null);
780 public abstract void notifyScriptTermination(String strStatus,
784 * display a message echoed from the jmol viewer
788 public abstract void sendConsoleEcho(String strEcho); /*
789 * { showConsole(true);
791 * history.append("\n" +
795 // /End JmolStatusListener
796 // /////////////////////////////
800 * status message - usually the response received after a script
803 public abstract void sendConsoleMessage(String strStatus);
806 public void setCallbackFunction(String callbackType,
807 String callbackFunction)
809 System.err.println("Ignoring set-callback request to associate "
810 + callbackType + " with function " + callbackFunction);
814 public void showHelp()
816 showUrl("http://jmol.sourceforge.net/docs/JmolUserGuide/", "jmolHelp");
820 * open the URL somehow
824 public abstract void showUrl(String url, String target);
827 * called to show or hide the associated console window container.
831 public abstract void showConsole(boolean show);
836 * - when true will initialise jmol's file IO system (should be false
839 * @param documentBase
841 * @param commandOptions
843 public void allocateViewer(Container renderPanel, boolean jmolfileio,
844 String htmlName, URL documentBase, URL codeBase,
845 String commandOptions)
847 allocateViewer(renderPanel, jmolfileio, htmlName, documentBase,
848 codeBase, commandOptions, null, null);
855 * - when true will initialise jmol's file IO system (should be false
858 * @param documentBase
860 * @param commandOptions
861 * @param consolePanel
862 * - panel to contain Jmol console
863 * @param buttonsToShow
864 * - buttons to show on the console, in ordr
866 public void allocateViewer(Container renderPanel, boolean jmolfileio,
867 String htmlName, URL documentBase, URL codeBase,
868 String commandOptions, final Container consolePanel,
869 String buttonsToShow)
871 if (commandOptions == null)
875 jmolViewer = (Viewer) JmolViewer.allocateViewer(renderPanel,
876 (jmolfileio ? new SmarterJmolAdapter() : null),
877 htmlName + ((Object) this).toString(), documentBase, codeBase,
878 commandOptions, this);
880 jmolViewer.setJmolStatusListener(this); // extends JmolCallbackListener
882 console = createJmolConsole(consolePanel, buttonsToShow);
883 if (consolePanel != null)
885 consolePanel.addComponentListener(this);
891 protected abstract JmolAppConsoleInterface createJmolConsole(
892 Container consolePanel, String buttonsToShow);
894 protected org.jmol.api.JmolAppConsoleInterface console = null;
897 public int[] resizeInnerPanel(String data)
899 // Jalview doesn't honour resize panel requests
906 protected void closeConsole()
912 console.setVisible(false);
915 } catch (Exception x)
924 * ComponentListener method
927 public void componentMoved(ComponentEvent e)
932 * ComponentListener method
935 public void componentResized(ComponentEvent e)
940 * ComponentListener method
943 public void componentShown(ComponentEvent e)
949 * ComponentListener method
952 public void componentHidden(ComponentEvent e)
958 protected int getModelNoForFile(String pdbFile)
960 if (modelFileNames == null)
964 for (int i = 0; i < modelFileNames.length; i++)
966 if (modelFileNames[i].equalsIgnoreCase(pdbFile))
975 protected ViewerType getViewerType()
977 return ViewerType.JMOL;