2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3 * Copyright (C) 2014 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.AlignmentViewPanel;
24 import jalview.api.FeatureRenderer;
25 import jalview.api.SequenceRenderer;
26 import jalview.api.SequenceStructureBinding;
27 import jalview.api.StructureSelectionManagerProvider;
28 import jalview.datamodel.AlignmentI;
29 import jalview.datamodel.ColumnSelection;
30 import jalview.datamodel.PDBEntry;
31 import jalview.datamodel.SequenceI;
32 import jalview.io.AppletFormatAdapter;
33 import jalview.schemes.ColourSchemeI;
34 import jalview.schemes.ResidueProperties;
35 import jalview.structure.AtomSpec;
36 import jalview.structure.StructureListener;
37 import jalview.structure.StructureMapping;
38 import jalview.structure.StructureSelectionManager;
39 import jalview.structures.models.SequenceStructureBindingModel;
40 import jalview.util.MessageManager;
42 import java.awt.Color;
43 import java.awt.Container;
44 import java.awt.event.ComponentEvent;
45 import java.awt.event.ComponentListener;
48 import java.security.AccessControlException;
49 import java.util.Hashtable;
50 import java.util.List;
52 import java.util.Vector;
54 import org.jmol.adapter.smarter.SmarterJmolAdapter;
55 import org.jmol.api.JmolAppConsoleInterface;
56 import org.jmol.api.JmolSelectionListener;
57 import org.jmol.api.JmolStatusListener;
58 import org.jmol.api.JmolViewer;
59 import org.jmol.constant.EnumCallback;
60 import org.jmol.popup.JmolPopup;
62 public abstract class JalviewJmolBinding extends SequenceStructureBindingModel implements StructureListener,
63 JmolStatusListener, SequenceStructureBinding,
64 JmolSelectionListener, ComponentListener,
65 StructureSelectionManagerProvider
69 * state flag used to check if the Jmol viewer's paint method can be called
71 private boolean finishedInit = false;
73 public boolean isFinishedInit()
78 public void setFinishedInit(boolean finishedInit)
80 this.finishedInit = finishedInit;
83 boolean allChainsSelected = false;
86 * when true, try to search the associated datamodel for sequences that are
87 * associated with any unknown structures in the Jmol view.
89 private boolean associateNewStructs = false;
91 Vector atomsPicked = new Vector();
93 public Vector chainNames;
98 * array of target chains for seuqences - tied to pdbentry and sequence[]
100 protected String[][] chains;
102 boolean colourBySequence = true;
104 StringBuffer eval = new StringBuffer();
106 public String fileLoadingError;
109 * the default or current model displayed if the model cannot be identified
110 * from the selection message
114 protected JmolPopup jmolpopup;
120 boolean loadedInline;
123 * current set of model filenames loaded in the Jmol instance
125 String[] modelFileNames = null;
127 public PDBEntry[] pdbentry;
130 * datasource protocol for access to PDBEntrylatest
132 String protocol = null;
134 StringBuffer resetLastRes = new StringBuffer();
137 * sequences mapped to each pdbentry
139 public SequenceI[][] sequence;
141 public StructureSelectionManager ssm;
143 public JmolViewer viewer;
145 public JalviewJmolBinding(StructureSelectionManager ssm,
146 PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String[][] chains,
150 this.sequence = sequenceIs;
151 this.chains = chains;
152 this.pdbentry = pdbentry;
153 this.protocol = protocol;
156 this.chains = new String[pdbentry.length][];
159 * viewer = JmolViewer.allocateViewer(renderPanel, new SmarterJmolAdapter(),
160 * "jalviewJmol", ap.av.applet .getDocumentBase(),
161 * ap.av.applet.getCodeBase(), "", this);
163 * jmolpopup = JmolPopup.newJmolPopup(viewer, true, "Jmol", true);
167 public JalviewJmolBinding(StructureSelectionManager ssm,
172 viewer.setJmolStatusListener(this);
173 viewer.addSelectionListener(this);
177 * construct a title string for the viewer window based on the data jalview
182 public String getViewerTitle()
184 if (sequence == null || pdbentry == null || sequence.length < 1
185 || pdbentry.length < 1 || sequence[0].length < 1)
187 return ("Jalview Jmol Window");
189 // TODO: give a more informative title when multiple structures are
191 StringBuffer title = new StringBuffer(sequence[0][0].getName() + ":"
192 + pdbentry[0].getId());
194 if (pdbentry[0].getProperty() != null)
196 if (pdbentry[0].getProperty().get("method") != null)
198 title.append(" Method: ");
199 title.append(pdbentry[0].getProperty().get("method"));
201 if (pdbentry[0].getProperty().get("chains") != null)
203 title.append(" Chain:");
204 title.append(pdbentry[0].getProperty().get("chains"));
207 return title.toString();
211 * prepare the view for a given set of models/chains. chainList contains
212 * strings of the form 'pdbfilename:Chaincode'
215 * list of chains to make visible
217 public void centerViewer(Vector chainList)
219 StringBuffer cmd = new StringBuffer();
222 for (int i = 0, iSize = chainList.size(); i < iSize; i++)
225 lbl = (String) chainList.elementAt(i);
229 mlength = lbl.indexOf(":", p);
230 } while (p < mlength && mlength < (lbl.length() - 2));
231 // TODO: lookup each pdb id and recover proper model number for it.
232 cmd.append(":" + lbl.substring(mlength + 1) + " /"
233 + (1 + getModelNum((String) chainFile.get(lbl))) + " or ");
235 if (cmd.length() > 0)
237 cmd.setLength(cmd.length() - 4);
239 evalStateCommand("select *;restrict " + cmd + ";cartoon;center " + cmd);
242 public void closeViewer()
244 viewer.setModeMouse(org.jmol.viewer.JmolConstants.MOUSE_NONE);
245 // remove listeners for all structures in viewer
246 ssm.removeStructureViewerListener(this, this.getPdbFile());
247 // and shut down jmol
248 viewer.evalStringQuiet("zap");
249 viewer.setJmolStatusListener(null);
252 releaseUIResources();
256 * called by JalviewJmolbinding after closeViewer is called - release any
257 * resources and references so they can be garbage collected.
259 protected abstract void releaseUIResources();
261 public void colourByChain()
263 colourBySequence = false;
264 // TODO: colour by chain should colour each chain distinctly across all
266 // TODO: http://issues.jalview.org/browse/JAL-628
267 evalStateCommand("select *;color chain");
270 public void colourByCharge()
272 colourBySequence = false;
273 evalStateCommand("select *;color white;select ASP,GLU;color red;"
274 + "select LYS,ARG;color blue;select CYS;color yellow");
278 * superpose the structures associated with sequences in the alignment
279 * according to their corresponding positions.
281 public void superposeStructures(AlignmentI alignment)
283 superposeStructures(alignment, -1, null);
287 * superpose the structures associated with sequences in the alignment
288 * according to their corresponding positions. ded)
290 * @param refStructure
291 * - select which pdb file to use as reference (default is -1 - the
292 * first structure in the alignment)
294 public void superposeStructures(AlignmentI alignment, int refStructure)
296 superposeStructures(alignment, refStructure, null);
300 * superpose the structures associated with sequences in the alignment
301 * according to their corresponding positions. ded)
303 * @param refStructure
304 * - select which pdb file to use as reference (default is -1 - the
305 * first structure in the alignment)
309 public void superposeStructures(AlignmentI alignment, int refStructure,
310 ColumnSelection hiddenCols)
312 superposeStructures(new AlignmentI[]
313 { alignment }, new int[]
314 { refStructure }, new ColumnSelection[]
318 public void superposeStructures(AlignmentI[] _alignment,
319 int[] _refStructure, ColumnSelection[] _hiddenCols)
321 assert (_alignment.length == _refStructure.length && _alignment.length != _hiddenCols.length);
323 String[] files = getPdbFile();
324 // check to see if we are still waiting for Jmol files
325 long starttime = System.currentTimeMillis();
326 boolean waiting = true;
330 for (String file : files)
334 // HACK - in Jalview 2.8 this call may not be threadsafe so we catch
335 // every possible exception
336 StructureMapping[] sm = ssm.getMapping(file);
337 if (sm == null || sm.length == 0)
341 } catch (Exception x)
349 // we wait around for a reasonable time before we give up
351 && System.currentTimeMillis() < (10000 + 1000 * files.length + starttime));
355 .println("RUNTIME PROBLEM: Jmol seems to be taking a long time to process all the structures.");
358 StringBuffer selectioncom = new StringBuffer();
359 // In principle - nSeconds specifies the speed of animation for each
360 // superposition - but is seems to behave weirdly, so we don't specify it.
361 String nSeconds = " ";
362 if (files.length > 10)
364 nSeconds = " 0.00001 ";
368 nSeconds = " " + (2.0 / files.length) + " ";
369 // if (nSeconds).substring(0,5)+" ";
371 // see JAL-1345 - should really automatically turn off the animation for
372 // large numbers of structures, but Jmol doesn't seem to allow that.
374 // union of all aligned positions are collected together.
375 for (int a = 0; a < _alignment.length; a++)
377 int refStructure = _refStructure[a];
378 AlignmentI alignment = _alignment[a];
379 ColumnSelection hiddenCols = _hiddenCols[a];
381 && selectioncom.length() > 0
382 && !selectioncom.substring(selectioncom.length() - 1).equals(
385 selectioncom.append("|");
387 // process this alignment
388 if (refStructure >= files.length)
390 System.err.println("Invalid reference structure value "
394 if (refStructure < -1)
398 StringBuffer command = new StringBuffer();
400 boolean matched[] = new boolean[alignment.getWidth()];
401 for (int m = 0; m < matched.length; m++)
404 matched[m] = (hiddenCols != null) ? hiddenCols.isVisible(m) : true;
407 int commonrpositions[][] = new int[files.length][alignment.getWidth()];
408 String isel[] = new String[files.length];
409 // reference structure - all others are superposed in it
410 String[] targetC = new String[files.length];
411 String[] chainNames = new String[files.length];
412 for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
414 StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
415 // RACE CONDITION - getMapping only returns Jmol loaded filenames once
416 // Jmol callback has completed.
417 if (mapping == null || mapping.length < 1)
419 throw new Error(MessageManager.getString("error.implementation_error_jmol_getting_data"));
422 for (int s = 0; s < sequence[pdbfnum].length; s++)
424 for (int sp, m = 0; m < mapping.length; m++)
426 if (mapping[m].getSequence() == sequence[pdbfnum][s]
427 && (sp = alignment.findIndex(sequence[pdbfnum][s])) > -1)
429 if (refStructure == -1)
431 refStructure = pdbfnum;
433 SequenceI asp = alignment.getSequenceAt(sp);
434 for (int r = 0; r < matched.length; r++)
440 matched[r] = false; // assume this is not a good site
441 if (r >= asp.getLength())
446 if (jalview.util.Comparison.isGap(asp.getCharAt(r)))
448 // no mapping to gaps in sequence
451 int t = asp.findPosition(r); // sequence position
452 int apos = mapping[m].getAtomNum(t);
453 int pos = mapping[m].getPDBResNum(t);
455 if (pos < 1 || pos == lastPos)
457 // can't align unmapped sequence
460 matched[r] = true; // this is a good ite
462 // just record this residue position
463 commonrpositions[pdbfnum][r] = pos;
465 // create model selection suffix
466 isel[pdbfnum] = "/" + (pdbfnum + 1) + ".1";
467 if (mapping[m].getChain() == null
468 || mapping[m].getChain().trim().length() == 0)
470 targetC[pdbfnum] = "";
474 targetC[pdbfnum] = ":" + mapping[m].getChain();
476 chainNames[pdbfnum] = mapping[m].getPdbId()
478 // move on to next pdb file
479 s = sequence[pdbfnum].length;
486 // TODO: consider bailing if nmatched less than 4 because superposition
489 // TODO: refactor superposable position search (above) from jmol selection
490 // construction (below)
492 String[] selcom = new String[files.length];
494 // generate select statements to select regions to superimpose structures
496 for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
498 String chainCd = targetC[pdbfnum];
501 StringBuffer molsel = new StringBuffer();
503 for (int r = 0; r < matched.length; r++)
511 if (lpos != commonrpositions[pdbfnum][r] - 1)
517 molsel.append(chainCd);
518 // molsel.append("} {");
524 // continuous run - and lpos >-1
527 // at the beginning, so add dash
533 lpos = commonrpositions[pdbfnum][r];
534 // molsel.append(lpos);
537 // add final selection phrase
541 molsel.append(chainCd);
544 if (molsel.length() > 1)
546 selcom[pdbfnum] = molsel.toString();
547 selectioncom.append("((");
548 selectioncom.append(selcom[pdbfnum].substring(1,
549 selcom[pdbfnum].length() - 1));
550 selectioncom.append(" )& ");
551 selectioncom.append(pdbfnum + 1);
552 selectioncom.append(".1)");
553 if (pdbfnum < files.length - 1)
555 selectioncom.append("|");
560 selcom[pdbfnum] = null;
564 for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
566 if (pdbfnum == refStructure || selcom[pdbfnum] == null
567 || selcom[refStructure] == null)
571 command.append("echo ");
572 command.append("\"Superposing (");
573 command.append(chainNames[pdbfnum]);
574 command.append(") against reference (");
575 command.append(chainNames[refStructure]);
576 command.append(")\";\ncompare " + nSeconds);
578 command.append(1 + pdbfnum);
579 command.append(".1} {");
580 command.append(1 + refStructure);
581 command.append(".1} SUBSET {*.CA | *.P} ATOMS ");
583 // form the matched pair strings
585 for (int s = 0; s < 2; s++)
587 command.append(selcom[(s == 0 ? pdbfnum : refStructure)]);
589 command.append(" ROTATE TRANSLATE;\n");
591 if (selectioncom.length() > 0)
593 System.out.println("Select regions:\n" + selectioncom.toString());
594 evalStateCommand("select *; cartoons off; backbone; select ("
595 + selectioncom.toString() + "); cartoons; ");
596 // selcom.append("; ribbons; ");
598 .println("Superimpose command(s):\n" + command.toString());
600 evalStateCommand(command.toString());
603 if (selectioncom.length() > 0)
604 {// finally, mark all regions that were superposed.
605 if (selectioncom.substring(selectioncom.length() - 1).equals("|"))
607 selectioncom.setLength(selectioncom.length() - 1);
609 System.out.println("Select regions:\n" + selectioncom.toString());
610 evalStateCommand("select *; cartoons off; backbone; select ("
611 + selectioncom.toString() + "); cartoons; ");
612 // evalStateCommand("select *; backbone; select "+selcom.toString()+"; cartoons; center "+selcom.toString());
616 public void evalStateCommand(String command)
619 if (lastCommand == null || !lastCommand.equals(command))
621 viewer.evalStringQuiet(command + "\n");
624 lastCommand = command;
628 * colour any structures associated with sequences in the given alignment
629 * using the getFeatureRenderer() and getSequenceRenderer() renderers but only
630 * if colourBySequence is enabled.
632 public void colourBySequence(boolean showFeatures,
633 jalview.api.AlignmentViewPanel alignmentv)
635 if (!colourBySequence || !isLoadingFinished())
643 String[] files = getPdbFile();
645 SequenceRenderer sr = getSequenceRenderer(alignmentv);
647 FeatureRenderer fr = null;
650 fr = getFeatureRenderer(alignmentv);
652 AlignmentI alignment = alignmentv.getAlignment();
654 for (jalview.structure.StructureMappingcommandSet cpdbbyseq : JmolCommands
655 .getColourBySequenceCommand(ssm, files, sequence, sr, fr,
658 for (String cbyseq : cpdbbyseq.commands)
660 evalStateCommand(cbyseq);
665 public boolean isColourBySequence()
667 return colourBySequence;
670 public void setColourBySequence(boolean colourBySequence)
672 this.colourBySequence = colourBySequence;
675 public void createImage(String file, String type, int quality)
677 System.out.println("JMOL CREATE IMAGE");
680 public String createImage(String fileName, String type,
681 Object textOrBytes, int quality)
683 System.out.println("JMOL CREATE IMAGE");
687 public String eval(String strEval)
689 // System.out.println(strEval);
690 // "# 'eval' is implemented only for the applet.";
694 // End StructureListener
695 // //////////////////////////
697 public float[][] functionXY(String functionName, int x, int y)
702 public float[][][] functionXYZ(String functionName, int nx, int ny, int nz)
704 // TODO Auto-generated method stub
708 public Color getColour(int atomIndex, int pdbResNum, String chain,
711 if (getModelNum(pdbfile) < 0)
715 // TODO: verify atomIndex is selecting correct model.
716 return new Color(viewer.getAtomArgb(atomIndex));
720 * returns the current featureRenderer that should be used to colour the
727 public abstract FeatureRenderer getFeatureRenderer(
728 AlignmentViewPanel alignment);
731 * instruct the Jalview binding to update the pdbentries vector if necessary
732 * prior to matching the jmol view's contents to the list of structure files
733 * Jalview knows about.
735 public abstract void refreshPdbEntries();
737 private int getModelNum(String modelFileName)
739 String[] mfn = getPdbFile();
744 for (int i = 0; i < mfn.length; i++)
746 if (mfn[i].equalsIgnoreCase(modelFileName))
755 * map between index of model filename returned from getPdbFile and the first
756 * index of models from this file in the viewer. Note - this is not trimmed -
757 * use getPdbFile to get number of unique models.
759 private int _modelFileNameMap[];
761 // ////////////////////////////////
762 // /StructureListener
763 public synchronized String[] getPdbFile()
767 return new String[0];
769 if (modelFileNames == null)
772 String mset[] = new String[viewer.getModelCount()];
773 _modelFileNameMap = new int[mset.length];
775 String m = viewer.getModelFileName(0);
780 mset[0] = new File(m).getAbsolutePath();
781 } catch (AccessControlException x)
783 // usually not allowed to do this in applet, so keep raw handle
785 // System.err.println("jmolBinding: Using local file string from Jmol: "+m);
788 for (int i = 1; i < mset.length; i++)
790 m = viewer.getModelFileName(i);
795 mset[j] = new File(m).getAbsolutePath();
796 } catch (AccessControlException x)
798 // usually not allowed to do this in applet, so keep raw handle
800 // System.err.println("jmolBinding: Using local file string from Jmol: "+m);
803 _modelFileNameMap[j] = i; // record the model index for the filename
804 // skip any additional models in the same file (NMR structures)
805 if ((mset[j] == null ? mset[j] != mset[j - 1]
806 : (mset[j - 1] == null || !mset[j].equals(mset[j - 1]))))
811 modelFileNames = new String[j];
812 System.arraycopy(mset, 0, modelFileNames, 0, j);
814 return modelFileNames;
818 * map from string to applet
820 public Map getRegistryInfo()
822 // TODO Auto-generated method stub
827 * returns the current sequenceRenderer that should be used to colour the
834 public abstract SequenceRenderer getSequenceRenderer(
835 AlignmentViewPanel alignment);
837 // ///////////////////////////////
838 // JmolStatusListener
840 public void handlePopupMenu(int x, int y)
842 jmolpopup.show(x, y);
846 * Highlight the specified atoms in the structure.
851 public void highlightAtoms(List<AtomSpec> atoms)
853 if (modelFileNames == null)
858 for (AtomSpec atom : atoms)
860 String pdbfile = atom.getPdbId();
861 int pdbResNum = atom.getPdbResNum();
862 String chain = atom.getChain();
864 // look up file model number for this pdbfile
867 // may need to adjust for URLencoding here - we don't worry about that
869 while (mdlNum < modelFileNames.length
870 && !pdbfile.equals(modelFileNames[mdlNum]))
872 // System.out.println("nomatch:"+pdbfile+"\nmodelfn:"+fn);
875 if (mdlNum == modelFileNames.length)
881 // if (!pdbfile.equals(pdbentry.getFile()))
883 if (resetLastRes.length() > 0)
885 viewer.evalStringQuiet(resetLastRes.toString());
889 eval.append("select " + pdbResNum); // +modelNum
891 resetLastRes.setLength(0);
892 resetLastRes.append("select " + pdbResNum); // +modelNum
895 resetLastRes.append(":");
896 if (!chain.equals(" "))
899 resetLastRes.append(chain);
902 eval.append(" /" + (mdlNum + 1));
903 resetLastRes.append("/" + (mdlNum + 1));
905 eval.append(";wireframe 100;" + eval.toString() + " and not hetero;");
907 resetLastRes.append(";wireframe 0;" + resetLastRes.toString()
908 + " and not hetero; spacefill 0;");
910 eval.append("spacefill 200;select none");
912 viewer.evalStringQuiet(eval.toString());
918 boolean debug = true;
920 private void jmolHistory(boolean enable)
922 viewer.evalStringQuiet("History " + ((debug || enable) ? "on" : "off"));
925 public void loadInline(String string)
929 // viewer.loadInline(strModel, isAppend);
931 // construct fake fullPathName and fileName so we can identify the file
933 // Then, construct pass a reader for the string to Jmol.
934 // ((org.jmol.Viewer.Viewer) viewer).loadModelFromFile(fullPathName,
935 // fileName, null, reader, false, null, null, 0);
936 viewer.openStringInline(string);
939 public void mouseOverStructure(int atomIndex, String strInfo)
942 int alocsep = strInfo.indexOf("^");
943 int mdlSep = strInfo.indexOf("/");
944 int chainSeparator = strInfo.indexOf(":"), chainSeparator1 = -1;
946 if (chainSeparator == -1)
948 chainSeparator = strInfo.indexOf(".");
949 if (mdlSep > -1 && mdlSep < chainSeparator)
951 chainSeparator1 = chainSeparator;
952 chainSeparator = mdlSep;
955 // handle insertion codes
958 pdbResNum = Integer.parseInt(strInfo.substring(
959 strInfo.indexOf("]") + 1, alocsep));
964 pdbResNum = Integer.parseInt(strInfo.substring(
965 strInfo.indexOf("]") + 1, chainSeparator));
969 if (strInfo.indexOf(":") > -1)
971 chainId = strInfo.substring(strInfo.indexOf(":") + 1,
972 strInfo.indexOf("."));
979 String pdbfilename = modelFileNames[frameNo]; // default is first or current
983 if (chainSeparator1 == -1)
985 chainSeparator1 = strInfo.indexOf(".", mdlSep);
987 String mdlId = (chainSeparator1 > -1) ? strInfo.substring(mdlSep + 1,
988 chainSeparator1) : strInfo.substring(mdlSep + 1);
991 // recover PDB filename for the model hovered over.
992 int _mp = _modelFileNameMap.length - 1, mnumber = new Integer(mdlId)
994 while (mnumber < _modelFileNameMap[_mp])
998 pdbfilename = modelFileNames[_mp];
999 if (pdbfilename == null)
1001 pdbfilename = new File(viewer.getModelFileName(mnumber))
1005 } catch (Exception e)
1010 if (lastMessage == null || !lastMessage.equals(strInfo))
1012 ssm.mouseOverStructure(pdbResNum, chainId, pdbfilename);
1015 lastMessage = strInfo;
1018 public void notifyAtomHovered(int atomIndex, String strInfo, String data)
1022 System.err.println("Ignoring additional hover info: " + data
1023 + " (other info: '" + strInfo + "' pos " + atomIndex + ")");
1025 mouseOverStructure(atomIndex, strInfo);
1029 * { if (history != null && strStatus != null &&
1030 * !strStatus.equals("Script completed")) { history.append("\n" + strStatus);
1034 public void notifyAtomPicked(int atomIndex, String strInfo, String strData)
1037 * this implements the toggle label behaviour copied from the original
1038 * structure viewer, MCView
1040 if (strData != null)
1042 System.err.println("Ignoring additional pick data string " + strData);
1044 int chainSeparator = strInfo.indexOf(":");
1046 if (chainSeparator == -1)
1048 chainSeparator = strInfo.indexOf(".");
1051 String picked = strInfo.substring(strInfo.indexOf("]") + 1,
1053 String mdlString = "";
1054 if ((p = strInfo.indexOf(":")) > -1)
1056 picked += strInfo.substring(p + 1, strInfo.indexOf("."));
1059 if ((p = strInfo.indexOf("/")) > -1)
1061 mdlString += strInfo.substring(p, strInfo.indexOf(" #"));
1063 picked = "((" + picked + ".CA" + mdlString + ")|(" + picked + ".P"
1067 if (!atomsPicked.contains(picked))
1069 viewer.evalStringQuiet("select " + picked + ";label %n %r:%c");
1070 atomsPicked.addElement(picked);
1074 viewer.evalString("select " + picked + ";label off");
1075 atomsPicked.removeElement(picked);
1078 // TODO: in application this happens
1080 // if (scriptWindow != null)
1082 // scriptWindow.sendConsoleMessage(strInfo);
1083 // scriptWindow.sendConsoleMessage("\n");
1089 public void notifyCallback(EnumCallback type, Object[] data)
1096 notifyFileLoaded((String) data[1], (String) data[2],
1097 (String) data[3], (String) data[4],
1098 ((Integer) data[5]).intValue());
1102 notifyAtomPicked(((Integer) data[2]).intValue(), (String) data[1],
1104 // also highlight in alignment
1106 notifyAtomHovered(((Integer) data[2]).intValue(), (String) data[1],
1110 notifyScriptTermination((String) data[2],
1111 ((Integer) data[3]).intValue());
1114 sendConsoleEcho((String) data[1]);
1117 sendConsoleMessage((data == null) ? ((String) null)
1118 : (String) data[1]);
1121 // System.err.println("Ignoring error callback.");
1131 System.err.println("Unhandled callback " + type + " "
1132 + data[1].toString());
1135 } catch (Exception e)
1137 System.err.println("Squashed Jmol callback handler error:");
1138 e.printStackTrace();
1143 public boolean notifyEnabled(EnumCallback callbackPick)
1145 switch (callbackPick)
1165 // incremented every time a load notification is successfully handled -
1166 // lightweight mechanism for other threads to detect when they can start
1167 // referrring to new structures.
1168 private long loadNotifiesHandled = 0;
1170 public long getLoadNotifiesHandled()
1172 return loadNotifiesHandled;
1175 public void notifyFileLoaded(String fullPathName, String fileName2,
1176 String modelName, String errorMsg, int modelParts)
1178 if (errorMsg != null)
1180 fileLoadingError = errorMsg;
1184 // TODO: deal sensibly with models loaded inLine:
1185 // modelName will be null, as will fullPathName.
1187 // the rest of this routine ignores the arguments, and simply interrogates
1188 // the Jmol view to find out what structures it contains, and adds them to
1189 // the structure selection manager.
1190 fileLoadingError = null;
1191 String[] oldmodels = modelFileNames;
1192 modelFileNames = null;
1193 chainNames = new Vector();
1194 chainFile = new Hashtable();
1195 boolean notifyLoaded = false;
1196 String[] modelfilenames = getPdbFile();
1197 // first check if we've lost any structures
1198 if (oldmodels != null && oldmodels.length > 0)
1201 for (int i = 0; i < oldmodels.length; i++)
1203 for (int n = 0; n < modelfilenames.length; n++)
1205 if (modelfilenames[n] == oldmodels[i])
1207 oldmodels[i] = null;
1211 if (oldmodels[i] != null)
1218 String[] oldmfn = new String[oldm];
1220 for (int i = 0; i < oldmodels.length; i++)
1222 if (oldmodels[i] != null)
1224 oldmfn[oldm++] = oldmodels[i];
1227 // deregister the Jmol instance for these structures - we'll add
1228 // ourselves again at the end for the current structure set.
1229 ssm.removeStructureViewerListener(this, oldmfn);
1232 refreshPdbEntries();
1233 for (int modelnum = 0; modelnum < modelfilenames.length; modelnum++)
1235 String fileName = modelfilenames[modelnum];
1236 boolean foundEntry = false;
1237 MCview.PDBfile pdb = null;
1238 String pdbfile = null, pdbfhash = null;
1239 // model was probably loaded inline - so check the pdb file hashcode
1242 // calculate essential attributes for the pdb data imported inline.
1243 // prolly need to resolve modelnumber properly - for now just use our
1245 pdbfile = viewer.getData("" + (1 + _modelFileNameMap[modelnum])
1247 pdbfhash = "" + pdbfile.hashCode();
1249 if (pdbentry != null)
1251 // search pdbentries and sequences to find correct pdbentry for this
1253 for (int pe = 0; pe < pdbentry.length; pe++)
1255 boolean matches = false;
1256 if (fileName == null)
1259 // see JAL-623 - need method of matching pasted data up
1261 pdb = ssm.setMapping(sequence[pe], chains[pe], pdbfile,
1262 AppletFormatAdapter.PASTE);
1263 pdbentry[modelnum].setFile("INLINE" + pdb.id);
1271 if (matches = (fl = new File(pdbentry[pe].getFile()))
1272 .equals(new File(fileName)))
1275 // TODO: Jmol can in principle retrieve from CLASSLOADER but
1278 // to be tested. See mantis bug
1279 // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
1280 String protocol = AppletFormatAdapter.URL;
1285 protocol = AppletFormatAdapter.FILE;
1287 } catch (Exception e)
1292 // Explicitly map to the filename used by Jmol ;
1293 pdb = ssm.setMapping(sequence[pe], chains[pe], fileName,
1295 // pdbentry[pe].getFile(), protocol);
1301 // add an entry for every chain in the model
1302 for (int i = 0; i < pdb.chains.size(); i++)
1304 String chid = new String(pdb.id + ":"
1305 + pdb.chains.elementAt(i).id);
1306 chainFile.put(chid, fileName);
1307 chainNames.addElement(chid);
1309 notifyLoaded = true;
1313 if (!foundEntry && associateNewStructs)
1315 // this is a foreign pdb file that jalview doesn't know about - add
1316 // it to the dataset and try to find a home - either on a matching
1317 // sequence or as a new sequence.
1318 String pdbcontent = viewer.getData("/" + (modelnum + 1) + ".1",
1320 // parse pdb file into a chain, etc.
1321 // locate best match for pdb in associated views and add mapping to
1323 // if properly registered then
1324 notifyLoaded = true;
1329 // so finally, update the jmol bits and pieces
1330 if (jmolpopup != null)
1332 // potential for deadlock here:
1333 // jmolpopup.updateComputedMenus();
1335 if (!isLoadingFromArchive())
1337 viewer.evalStringQuiet("model 0; select backbone;restrict;cartoon;wireframe off;spacefill off");
1339 // register ourselves as a listener and notify the gui that it needs to
1341 ssm.addStructureViewerListener(this);
1344 FeatureRenderer fr = getFeatureRenderer(null);
1350 loadNotifiesHandled++;
1352 setLoadingFromArchive(false);
1355 public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)
1357 notifyAtomPicked(iatom, strMeasure, null);
1360 public abstract void notifyScriptTermination(String strStatus,
1364 * display a message echoed from the jmol viewer
1368 public abstract void sendConsoleEcho(String strEcho); /*
1369 * { showConsole(true);
1371 * history.append("\n" +
1375 // /End JmolStatusListener
1376 // /////////////////////////////
1380 * status message - usually the response received after a script
1383 public abstract void sendConsoleMessage(String strStatus);
1385 public void setCallbackFunction(String callbackType,
1386 String callbackFunction)
1388 System.err.println("Ignoring set-callback request to associate "
1389 + callbackType + " with function " + callbackFunction);
1393 public void setJalviewColourScheme(ColourSchemeI cs)
1395 colourBySequence = false;
1405 // TODO: Switch between nucleotide or aa selection expressions
1406 StringBuilder command = new StringBuilder(128);
1407 command.append("select *;color white;");
1408 for (String res : ResidueProperties.aa3Hash.keySet())
1410 index = ResidueProperties.aa3Hash.get(res).intValue();
1416 col = cs.findColour(ResidueProperties.aa[index].charAt(0));
1418 command.append("select " + res + ";color[" + col.getRed() + ","
1419 + col.getGreen() + "," + col.getBlue() + "];");
1422 evalStateCommand(command.toString());
1426 public void showHelp()
1428 showUrl("http://jmol.sourceforge.net/docs/JmolUserGuide/", "jmolHelp");
1432 * open the URL somehow
1436 public abstract void showUrl(String url, String target);
1439 * called when the binding thinks the UI needs to be refreshed after a Jmol
1440 * state change. this could be because structures were loaded, or because an
1441 * error has occured.
1443 public abstract void refreshGUI();
1446 * called to show or hide the associated console window container.
1450 public abstract void showConsole(boolean show);
1453 * @param renderPanel
1455 * - when true will initialise jmol's file IO system (should be false
1456 * in applet context)
1458 * @param documentBase
1460 * @param commandOptions
1462 public void allocateViewer(Container renderPanel, boolean jmolfileio,
1463 String htmlName, URL documentBase, URL codeBase,
1464 String commandOptions)
1466 allocateViewer(renderPanel, jmolfileio, htmlName, documentBase,
1467 codeBase, commandOptions, null, null);
1472 * @param renderPanel
1474 * - when true will initialise jmol's file IO system (should be false
1475 * in applet context)
1477 * @param documentBase
1479 * @param commandOptions
1480 * @param consolePanel
1481 * - panel to contain Jmol console
1482 * @param buttonsToShow
1483 * - buttons to show on the console, in ordr
1485 public void allocateViewer(Container renderPanel, boolean jmolfileio,
1486 String htmlName, URL documentBase, URL codeBase,
1487 String commandOptions, final Container consolePanel,
1488 String buttonsToShow)
1490 if (commandOptions == null)
1492 commandOptions = "";
1494 viewer = JmolViewer.allocateViewer(renderPanel,
1495 (jmolfileio ? new SmarterJmolAdapter() : null), htmlName
1496 + ((Object) this).toString(), documentBase, codeBase,
1497 commandOptions, this);
1499 console = createJmolConsole(viewer, consolePanel, buttonsToShow);
1500 if (consolePanel != null)
1502 consolePanel.addComponentListener(this);
1508 protected abstract JmolAppConsoleInterface createJmolConsole(
1509 JmolViewer viewer2, Container consolePanel, String buttonsToShow);
1511 protected org.jmol.api.JmolAppConsoleInterface console = null;
1513 public void componentResized(ComponentEvent e)
1518 public void componentMoved(ComponentEvent e)
1523 public void componentShown(ComponentEvent e)
1528 public void componentHidden(ComponentEvent e)
1533 public void setBackgroundColour(java.awt.Color col)
1536 viewer.evalStringQuiet("background [" + col.getRed() + ","
1537 + col.getGreen() + "," + col.getBlue() + "];");
1542 * add structures and any known sequence associations
1544 * @returns the pdb entries added to the current set.
1546 public synchronized PDBEntry[] addSequenceAndChain(PDBEntry[] pdbe,
1547 SequenceI[][] seq, String[][] chns)
1550 Vector v = new Vector();
1551 Vector rtn = new Vector();
1552 for (int i = 0; i < pdbentry.length; i++)
1554 v.addElement(pdbentry[i]);
1556 for (int i = 0; i < pdbe.length; i++)
1558 int r = v.indexOf(pdbe[i]);
1559 if (r == -1 || r >= pdbentry.length)
1561 rtn.addElement(new int[]
1563 v.addElement(pdbe[i]);
1567 // just make sure the sequence/chain entries are all up to date
1568 addSequenceAndChain(r, seq[i], chns[i]);
1571 pdbe = new PDBEntry[v.size()];
1576 // expand the tied seuqence[] and string[] arrays
1577 SequenceI[][] sqs = new SequenceI[pdbentry.length][];
1578 String[][] sch = new String[pdbentry.length][];
1579 System.arraycopy(sequence, 0, sqs, 0, sequence.length);
1580 System.arraycopy(chains, 0, sch, 0, this.chains.length);
1583 pdbe = new PDBEntry[rtn.size()];
1584 for (int r = 0; r < pdbe.length; r++)
1586 int[] stri = ((int[]) rtn.elementAt(r));
1587 // record the pdb file as a new addition
1588 pdbe[r] = pdbentry[stri[0]];
1589 // and add the new sequence/chain entries
1590 addSequenceAndChain(stri[0], seq[stri[1]], chns[stri[1]]);
1600 public void addSequence(int pe, SequenceI[] seq)
1602 // add sequences to the pe'th pdbentry's seuqence set.
1603 addSequenceAndChain(pe, seq, null);
1606 private void addSequenceAndChain(int pe, SequenceI[] seq, String[] tchain)
1608 if (pe < 0 || pe >= pdbentry.length)
1610 throw new Error(MessageManager.formatMessage("error.implementation_error_no_pdbentry_from_index", new String[]{Integer.valueOf(pe).toString()}));
1612 final String nullChain = "TheNullChain";
1613 Vector s = new Vector();
1614 Vector c = new Vector();
1617 chains = new String[pdbentry.length][];
1619 if (sequence[pe] != null)
1621 for (int i = 0; i < sequence[pe].length; i++)
1623 s.addElement(sequence[pe][i]);
1624 if (chains[pe] != null)
1626 if (i < chains[pe].length)
1628 c.addElement(chains[pe][i]);
1632 c.addElement(nullChain);
1637 if (tchain != null && tchain.length > 0)
1639 c.addElement(nullChain);
1644 for (int i = 0; i < seq.length; i++)
1646 if (!s.contains(seq[i]))
1648 s.addElement(seq[i]);
1649 if (tchain != null && i < tchain.length)
1651 c.addElement(tchain[i] == null ? nullChain : tchain[i]);
1655 SequenceI[] tmp = new SequenceI[s.size()];
1660 String[] tch = new String[c.size()];
1662 for (int i = 0; i < tch.length; i++)
1664 if (tch[i] == nullChain)
1680 * @return text report of alignment between pdbfile and any associated
1681 * alignment sequences
1683 public String printMapping(String pdbfile)
1685 return ssm.printMapping(pdbfile);
1689 public void resizeInnerPanel(String data)
1691 // Jalview doesn't honour resize panel requests