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.StructureListener;
36 import jalview.structure.StructureMapping;
37 import jalview.structure.StructureSelectionManager;
39 import java.awt.Color;
40 import java.awt.Container;
41 import java.awt.event.ComponentEvent;
42 import java.awt.event.ComponentListener;
45 import java.security.AccessControlException;
46 import java.util.Enumeration;
47 import java.util.Hashtable;
49 import java.util.Vector;
51 import org.jmol.adapter.smarter.SmarterJmolAdapter;
52 import org.jmol.api.JmolAppConsoleInterface;
53 import org.jmol.api.JmolSelectionListener;
54 import org.jmol.api.JmolStatusListener;
55 import org.jmol.api.JmolViewer;
56 import org.jmol.constant.EnumCallback;
57 import org.jmol.popup.JmolPopup;
59 public abstract class JalviewJmolBinding implements StructureListener,
60 JmolStatusListener, SequenceStructureBinding,
61 JmolSelectionListener, ComponentListener,
62 StructureSelectionManagerProvider
66 * set if Jmol state is being restored from some source - instructs binding
67 * not to apply default display style when structure set is updated for first
70 private boolean loadingFromArchive = false;
73 * second flag to indicate if the jmol viewer should ignore sequence colouring
74 * events from the structure manager because the GUI is still setting up
76 private boolean loadingFinished = true;
79 * state flag used to check if the Jmol viewer's paint method can be called
81 private boolean finishedInit = false;
83 public boolean isFinishedInit()
88 public void setFinishedInit(boolean finishedInit)
90 this.finishedInit = finishedInit;
93 boolean allChainsSelected = false;
96 * when true, try to search the associated datamodel for sequences that are
97 * associated with any unknown structures in the Jmol view.
99 private boolean associateNewStructs = false;
101 Vector atomsPicked = new Vector();
103 public Vector chainNames;
108 * array of target chains for seuqences - tied to pdbentry and sequence[]
110 protected String[][] chains;
112 boolean colourBySequence = true;
114 StringBuffer eval = new StringBuffer();
116 public String fileLoadingError;
119 * the default or current model displayed if the model cannot be identified
120 * from the selection message
124 protected JmolPopup jmolpopup;
130 boolean loadedInline;
133 * current set of model filenames loaded in the Jmol instance
135 String[] modelFileNames = null;
137 public PDBEntry[] pdbentry;
140 * datasource protocol for access to PDBEntrylatest
142 String protocol = null;
144 StringBuffer resetLastRes = new StringBuffer();
147 * sequences mapped to each pdbentry
149 public SequenceI[][] sequence;
151 public StructureSelectionManager ssm;
153 public JmolViewer viewer;
155 public JalviewJmolBinding(StructureSelectionManager ssm,
156 PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String[][] chains,
160 this.sequence = sequenceIs;
161 this.chains = chains;
162 this.pdbentry = pdbentry;
163 this.protocol = protocol;
166 this.chains = new String[pdbentry.length][];
169 * viewer = JmolViewer.allocateViewer(renderPanel, new SmarterJmolAdapter(),
170 * "jalviewJmol", ap.av.applet .getDocumentBase(),
171 * ap.av.applet.getCodeBase(), "", this);
173 * jmolpopup = JmolPopup.newJmolPopup(viewer, true, "Jmol", true);
177 public JalviewJmolBinding(StructureSelectionManager ssm,
182 viewer.setJmolStatusListener(this);
183 viewer.addSelectionListener(this);
187 * construct a title string for the viewer window based on the data jalview
192 public String getViewerTitle()
194 if (sequence == null || pdbentry == null || sequence.length < 1
195 || pdbentry.length < 1 || sequence[0].length < 1)
197 return ("Jalview Jmol Window");
199 // TODO: give a more informative title when multiple structures are
201 StringBuffer title = new StringBuffer(sequence[0][0].getName() + ":"
202 + pdbentry[0].getId());
204 if (pdbentry[0].getProperty() != null)
206 if (pdbentry[0].getProperty().get("method") != null)
208 title.append(" Method: ");
209 title.append(pdbentry[0].getProperty().get("method"));
211 if (pdbentry[0].getProperty().get("chains") != null)
213 title.append(" Chain:");
214 title.append(pdbentry[0].getProperty().get("chains"));
217 return title.toString();
221 * prepare the view for a given set of models/chains. chainList contains
222 * strings of the form 'pdbfilename:Chaincode'
225 * list of chains to make visible
227 public void centerViewer(Vector chainList)
229 StringBuffer cmd = new StringBuffer();
232 for (int i = 0, iSize = chainList.size(); i < iSize; i++)
235 lbl = (String) chainList.elementAt(i);
239 mlength = lbl.indexOf(":", p);
240 } while (p < mlength && mlength < (lbl.length() - 2));
241 // TODO: lookup each pdb id and recover proper model number for it.
242 cmd.append(":" + lbl.substring(mlength + 1) + " /"
243 + (1 + getModelNum((String) chainFile.get(lbl))) + " or ");
245 if (cmd.length() > 0)
246 cmd.setLength(cmd.length() - 4);
247 evalStateCommand("select *;restrict " + cmd + ";cartoon;center " + cmd);
250 public void closeViewer()
252 viewer.setModeMouse(org.jmol.viewer.JmolConstants.MOUSE_NONE);
253 // remove listeners for all structures in viewer
254 ssm.removeStructureViewerListener(this, this.getPdbFile());
255 // and shut down jmol
256 viewer.evalStringQuiet("zap");
257 viewer.setJmolStatusListener(null);
260 releaseUIResources();
264 * called by JalviewJmolbinding after closeViewer is called - release any
265 * resources and references so they can be garbage collected.
267 protected abstract void releaseUIResources();
269 public void colourByChain()
271 colourBySequence = false;
272 // TODO: colour by chain should colour each chain distinctly across all
274 // TODO: http://issues.jalview.org/browse/JAL-628
275 evalStateCommand("select *;color chain");
278 public void colourByCharge()
280 colourBySequence = false;
281 evalStateCommand("select *;color white;select ASP,GLU;color red;"
282 + "select LYS,ARG;color blue;select CYS;color yellow");
286 * superpose the structures associated with sequences in the alignment
287 * according to their corresponding positions.
289 public void superposeStructures(AlignmentI alignment)
291 superposeStructures(alignment, -1, null);
295 * superpose the structures associated with sequences in the alignment
296 * according to their corresponding positions. ded)
298 * @param refStructure
299 * - select which pdb file to use as reference (default is -1 - the
300 * first structure in the alignment)
302 public void superposeStructures(AlignmentI alignment, int refStructure)
304 superposeStructures(alignment, refStructure, null);
308 * superpose the structures associated with sequences in the alignment
309 * according to their corresponding positions. ded)
311 * @param refStructure
312 * - select which pdb file to use as reference (default is -1 - the
313 * first structure in the alignment)
317 public void superposeStructures(AlignmentI alignment, int refStructure,
318 ColumnSelection hiddenCols)
320 superposeStructures(new AlignmentI[]
321 { alignment }, new int[]
322 { refStructure }, new ColumnSelection[]
326 public void superposeStructures(AlignmentI[] _alignment,
327 int[] _refStructure, ColumnSelection[] _hiddenCols)
329 assert (_alignment.length == _refStructure.length && _alignment.length != _hiddenCols.length);
331 String[] files = getPdbFile();
332 // check to see if we are still waiting for Jmol files
333 long starttime=System.currentTimeMillis();
334 boolean waiting=true;
337 for (String file:files)
340 // HACK - in Jalview 2.8 this call may not be threadsafe so we catch
341 // every possible exception
342 StructureMapping[] sm = ssm.getMapping(file);
343 if (sm == null || sm.length == 0)
347 } catch (Exception x)
355 // we wait around for a reasonable time before we give up
356 } while (waiting && System.currentTimeMillis()<(10000+1000*files.length+starttime));
359 System.err.println("RUNTIME PROBLEM: Jmol seems to be taking a long time to process all the structures.");
362 StringBuffer selectioncom = new StringBuffer();
363 // In principle - nSeconds specifies the speed of animation for each
364 // superposition - but is seems to behave weirdly, so we don't specify it.
365 String nSeconds = " ";
366 if (files.length > 10)
368 nSeconds = " 0.00001 ";
372 nSeconds = " " + (2.0 / files.length) + " ";
373 // if (nSeconds).substring(0,5)+" ";
375 // see JAL-1345 - should really automatically turn off the animation for
376 // large numbers of structures, but Jmol doesn't seem to allow that.
378 // union of all aligned positions are collected together.
379 for (int a = 0; a < _alignment.length; a++)
381 int refStructure = _refStructure[a];
382 AlignmentI alignment = _alignment[a];
383 ColumnSelection hiddenCols = _hiddenCols[a];
385 && selectioncom.length() > 0
386 && !selectioncom.substring(selectioncom.length() - 1).equals(
389 selectioncom.append("|");
391 // process this alignment
392 if (refStructure >= files.length)
394 System.err.println("Invalid reference structure value "
398 if (refStructure < -1)
402 StringBuffer command = new StringBuffer();
404 boolean matched[] = new boolean[alignment.getWidth()];
405 for (int m = 0; m < matched.length; m++)
408 matched[m] = (hiddenCols != null) ? hiddenCols.isVisible(m) : true;
411 int commonrpositions[][] = new int[files.length][alignment.getWidth()];
412 String isel[] = new String[files.length];
413 // reference structure - all others are superposed in it
414 String[] targetC = new String[files.length];
415 String[] chainNames = new String[files.length];
416 for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
418 StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
419 // RACE CONDITION - getMapping only returns Jmol loaded filenames once
420 // Jmol callback has completed.
421 if (mapping == null || mapping.length < 1)
423 throw new Error("Implementation error - Jmol seems to be still working on getting its data - report at http://issues.jalview.org/browse/JAL-1016");
426 for (int s = 0; s < sequence[pdbfnum].length; s++)
428 for (int sp, m = 0; m < mapping.length; m++)
430 if (mapping[m].getSequence() == sequence[pdbfnum][s]
431 && (sp = alignment.findIndex(sequence[pdbfnum][s])) > -1)
433 if (refStructure == -1)
435 refStructure = pdbfnum;
437 SequenceI asp = alignment.getSequenceAt(sp);
438 for (int r = 0; r < matched.length; r++)
444 matched[r] = false; // assume this is not a good site
445 if (r >= asp.getLength())
450 if (jalview.util.Comparison.isGap(asp.getCharAt(r)))
452 // no mapping to gaps in sequence
455 int t = asp.findPosition(r); // sequence position
456 int apos = mapping[m].getAtomNum(t);
457 int pos = mapping[m].getPDBResNum(t);
459 if (pos < 1 || pos == lastPos)
461 // can't align unmapped sequence
464 matched[r] = true; // this is a good ite
466 // just record this residue position
467 commonrpositions[pdbfnum][r] = pos;
469 // create model selection suffix
470 isel[pdbfnum] = "/" + (pdbfnum + 1) + ".1";
471 if (mapping[m].getChain() == null
472 || mapping[m].getChain().trim().length() == 0)
474 targetC[pdbfnum] = "";
478 targetC[pdbfnum] = ":" + mapping[m].getChain();
480 chainNames[pdbfnum] = mapping[m].getPdbId()
482 // move on to next pdb file
483 s = sequence[pdbfnum].length;
490 // TODO: consider bailing if nmatched less than 4 because superposition
493 // TODO: refactor superposable position search (above) from jmol selection
494 // construction (below)
496 String[] selcom = new String[files.length];
498 // generate select statements to select regions to superimpose structures
500 for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
502 String chainCd = targetC[pdbfnum];
505 StringBuffer molsel = new StringBuffer();
507 for (int r = 0; r < matched.length; r++)
515 if (lpos != commonrpositions[pdbfnum][r] - 1)
521 molsel.append(chainCd);
522 // molsel.append("} {");
528 // continuous run - and lpos >-1
531 // at the beginning, so add dash
537 lpos = commonrpositions[pdbfnum][r];
538 // molsel.append(lpos);
541 // add final selection phrase
545 molsel.append(chainCd);
548 if (molsel.length() > 1)
550 selcom[pdbfnum] = molsel.toString();
551 selectioncom.append("((");
552 selectioncom.append(selcom[pdbfnum].substring(1,
553 selcom[pdbfnum].length() - 1));
554 selectioncom.append(" )& ");
555 selectioncom.append(pdbfnum + 1);
556 selectioncom.append(".1)");
557 if (pdbfnum < files.length - 1)
559 selectioncom.append("|");
562 selcom[pdbfnum] = null;
566 for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
568 if (pdbfnum == refStructure || selcom[pdbfnum]==null || selcom[refStructure]==null)
572 command.append("echo ");
573 command.append("\"Superposing (");
574 command.append(chainNames[pdbfnum]);
575 command.append(") against reference (");
576 command.append(chainNames[refStructure]);
577 command.append(")\";\ncompare "+nSeconds);
579 command.append(1 + pdbfnum);
580 command.append(".1} {");
581 command.append(1 + refStructure);
582 command.append(".1} SUBSET {*.CA | *.P} ATOMS ");
584 // form the matched pair strings
586 for (int s = 0; s < 2; s++)
588 command.append(selcom[(s == 0 ? pdbfnum : refStructure)]);
590 command.append(" ROTATE TRANSLATE;\n");
592 if (selectioncom.length() > 0)
594 System.out.println("Select regions:\n" + selectioncom.toString());
595 evalStateCommand("select *; cartoons off; backbone; select ("
596 + selectioncom.toString() + "); cartoons; ");
597 // selcom.append("; ribbons; ");
599 .println("Superimpose command(s):\n" + command.toString());
601 evalStateCommand(command.toString());
604 if (selectioncom.length() > 0)
605 {// finally, mark all regions that were superposed.
606 if (selectioncom.substring(selectioncom.length() - 1).equals("|"))
608 selectioncom.setLength(selectioncom.length() - 1);
610 System.out.println("Select regions:\n" + selectioncom.toString());
611 evalStateCommand("select *; cartoons off; backbone; select ("
612 + selectioncom.toString() + "); cartoons; ");
613 // evalStateCommand("select *; backbone; select "+selcom.toString()+"; cartoons; center "+selcom.toString());
617 public void evalStateCommand(String command)
620 if (lastCommand == null || !lastCommand.equals(command))
622 viewer.evalStringQuiet(command + "\n");
625 lastCommand = command;
629 * colour any structures associated with sequences in the given alignment
630 * using the getFeatureRenderer() and getSequenceRenderer() renderers but only
631 * if colourBySequence is enabled.
633 public void colourBySequence(boolean showFeatures,
634 jalview.api.AlignmentViewPanel alignmentv)
636 if (!colourBySequence || !loadingFinished)
642 String[] files = getPdbFile();
644 SequenceRenderer sr = getSequenceRenderer(alignmentv);
646 FeatureRenderer fr = null;
649 fr = getFeatureRenderer(alignmentv);
651 AlignmentI alignment = alignmentv.getAlignment();
653 for (jalview.structure.StructureMappingcommandSet cpdbbyseq : JmolCommands
654 .getColourBySequenceCommand(ssm, files, sequence, sr, fr,
656 for (String cbyseq : cpdbbyseq.commands)
658 evalStateCommand(cbyseq);
662 public boolean isColourBySequence()
664 return colourBySequence;
667 public void setColourBySequence(boolean colourBySequence)
669 this.colourBySequence = colourBySequence;
672 public void createImage(String file, String type, int quality)
674 System.out.println("JMOL CREATE IMAGE");
677 public String createImage(String fileName, String type,
678 Object textOrBytes, int quality)
680 System.out.println("JMOL CREATE IMAGE");
684 public String eval(String strEval)
686 // System.out.println(strEval);
687 // "# 'eval' is implemented only for the applet.";
691 // End StructureListener
692 // //////////////////////////
694 public float[][] functionXY(String functionName, int x, int y)
699 public float[][][] functionXYZ(String functionName, int nx, int ny, int nz)
701 // TODO Auto-generated method stub
705 public Color getColour(int atomIndex, int pdbResNum, String chain,
708 if (getModelNum(pdbfile) < 0)
710 // TODO: verify atomIndex is selecting correct model.
711 return new Color(viewer.getAtomArgb(atomIndex));
715 * returns the current featureRenderer that should be used to colour the
722 public abstract FeatureRenderer getFeatureRenderer(
723 AlignmentViewPanel alignment);
726 * instruct the Jalview binding to update the pdbentries vector if necessary
727 * prior to matching the jmol view's contents to the list of structure files
728 * Jalview knows about.
730 public abstract void refreshPdbEntries();
732 private int getModelNum(String modelFileName)
734 String[] mfn = getPdbFile();
739 for (int i = 0; i < mfn.length; i++)
741 if (mfn[i].equalsIgnoreCase(modelFileName))
748 * map between index of model filename returned from getPdbFile and the first
749 * index of models from this file in the viewer. Note - this is not trimmed -
750 * use getPdbFile to get number of unique models.
752 private int _modelFileNameMap[];
754 // ////////////////////////////////
755 // /StructureListener
756 public synchronized String[] getPdbFile()
760 return new String[0];
762 if (modelFileNames == null)
765 String mset[] = new String[viewer.getModelCount()];
766 _modelFileNameMap = new int[mset.length];
768 String m = viewer.getModelFileName(0);
773 mset[0] = new File(m).getAbsolutePath();
774 } catch (AccessControlException x)
776 // usually not allowed to do this in applet, so keep raw handle
778 // System.err.println("jmolBinding: Using local file string from Jmol: "+m);
781 for (int i = 1; i < mset.length; i++)
783 m = viewer.getModelFileName(i);
788 mset[j] = new File(m).getAbsolutePath();
789 } catch (AccessControlException x)
791 // usually not allowed to do this in applet, so keep raw handle
793 // System.err.println("jmolBinding: Using local file string from Jmol: "+m);
796 _modelFileNameMap[j] = i; // record the model index for the filename
797 // skip any additional models in the same file (NMR structures)
798 if ((mset[j] == null ? mset[j] != mset[j - 1]
799 : (mset[j - 1] == null || !mset[j].equals(mset[j - 1]))))
804 modelFileNames = new String[j];
805 System.arraycopy(mset, 0, modelFileNames, 0, j);
807 return modelFileNames;
811 * map from string to applet
813 public Map getRegistryInfo()
815 // TODO Auto-generated method stub
820 * returns the current sequenceRenderer that should be used to colour the
827 public abstract SequenceRenderer getSequenceRenderer(
828 AlignmentViewPanel alignment);
830 // ///////////////////////////////
831 // JmolStatusListener
833 public void handlePopupMenu(int x, int y)
835 jmolpopup.show(x, y);
839 public void highlightAtom(int atomIndex, int pdbResNum, String chain,
842 if (modelFileNames == null)
847 // look up file model number for this pdbfile
850 // may need to adjust for URLencoding here - we don't worry about that yet.
851 while (mdlNum < modelFileNames.length
852 && !pdbfile.equals(modelFileNames[mdlNum]))
854 // System.out.println("nomatch:"+pdbfile+"\nmodelfn:"+fn);
857 if (mdlNum == modelFileNames.length)
863 // if (!pdbfile.equals(pdbentry.getFile()))
865 if (resetLastRes.length() > 0)
867 viewer.evalStringQuiet(resetLastRes.toString());
871 eval.append("select " + pdbResNum); // +modelNum
873 resetLastRes.setLength(0);
874 resetLastRes.append("select " + pdbResNum); // +modelNum
877 resetLastRes.append(":");
878 if (!chain.equals(" "))
881 resetLastRes.append(chain);
884 eval.append(" /" + (mdlNum + 1));
885 resetLastRes.append("/" + (mdlNum + 1));
887 eval.append(";wireframe 100;" + eval.toString() + " and not hetero;");
889 resetLastRes.append(";wireframe 0;" + resetLastRes.toString()
890 + " and not hetero; spacefill 0;");
892 eval.append("spacefill 200;select none");
894 viewer.evalStringQuiet(eval.toString());
899 boolean debug = true;
901 private void jmolHistory(boolean enable)
903 viewer.evalStringQuiet("History " + ((debug || enable) ? "on" : "off"));
906 public void loadInline(String string)
910 // viewer.loadInline(strModel, isAppend);
912 // construct fake fullPathName and fileName so we can identify the file
914 // Then, construct pass a reader for the string to Jmol.
915 // ((org.jmol.Viewer.Viewer) viewer).loadModelFromFile(fullPathName,
916 // fileName, null, reader, false, null, null, 0);
917 viewer.openStringInline(string);
920 public void mouseOverStructure(int atomIndex, String strInfo)
923 int alocsep = strInfo.indexOf("^");
924 int mdlSep = strInfo.indexOf("/");
925 int chainSeparator = strInfo.indexOf(":"), chainSeparator1 = -1;
927 if (chainSeparator == -1)
929 chainSeparator = strInfo.indexOf(".");
930 if (mdlSep > -1 && mdlSep < chainSeparator)
932 chainSeparator1 = chainSeparator;
933 chainSeparator = mdlSep;
936 // handle insertion codes
939 pdbResNum = Integer.parseInt(strInfo.substring(
940 strInfo.indexOf("]") + 1, alocsep));
945 pdbResNum = Integer.parseInt(strInfo.substring(
946 strInfo.indexOf("]") + 1, chainSeparator));
950 if (strInfo.indexOf(":") > -1)
951 chainId = strInfo.substring(strInfo.indexOf(":") + 1,
952 strInfo.indexOf("."));
958 String pdbfilename = modelFileNames[frameNo]; // default is first or current
962 if (chainSeparator1 == -1)
964 chainSeparator1 = strInfo.indexOf(".", mdlSep);
966 String mdlId = (chainSeparator1 > -1) ? strInfo.substring(mdlSep + 1,
967 chainSeparator1) : strInfo.substring(mdlSep + 1);
970 // recover PDB filename for the model hovered over.
971 int _mp = _modelFileNameMap.length - 1, mnumber = new Integer(mdlId)
973 while (mnumber < _modelFileNameMap[_mp])
977 pdbfilename = modelFileNames[_mp];
978 if (pdbfilename == null)
980 pdbfilename = new File(viewer.getModelFileName(mnumber))
984 } catch (Exception e)
989 if (lastMessage == null || !lastMessage.equals(strInfo))
990 ssm.mouseOverStructure(pdbResNum, chainId, pdbfilename);
992 lastMessage = strInfo;
995 public void notifyAtomHovered(int atomIndex, String strInfo, String data)
999 System.err.println("Ignoring additional hover info: " + data
1000 + " (other info: '" + strInfo + "' pos " + atomIndex + ")");
1002 mouseOverStructure(atomIndex, strInfo);
1006 * { if (history != null && strStatus != null &&
1007 * !strStatus.equals("Script completed")) { history.append("\n" + strStatus);
1011 public void notifyAtomPicked(int atomIndex, String strInfo, String strData)
1014 * this implements the toggle label behaviour copied from the original
1015 * structure viewer, MCView
1017 if (strData != null)
1019 System.err.println("Ignoring additional pick data string " + strData);
1021 int chainSeparator = strInfo.indexOf(":");
1023 if (chainSeparator == -1)
1024 chainSeparator = strInfo.indexOf(".");
1026 String picked = strInfo.substring(strInfo.indexOf("]") + 1,
1028 String mdlString = "";
1029 if ((p = strInfo.indexOf(":")) > -1)
1030 picked += strInfo.substring(p + 1, strInfo.indexOf("."));
1032 if ((p = strInfo.indexOf("/")) > -1)
1034 mdlString += strInfo.substring(p, strInfo.indexOf(" #"));
1036 picked = "((" + picked + ".CA" + mdlString + ")|(" + picked + ".P"
1040 if (!atomsPicked.contains(picked))
1042 viewer.evalStringQuiet("select " + picked + ";label %n %r:%c");
1043 atomsPicked.addElement(picked);
1047 viewer.evalString("select " + picked + ";label off");
1048 atomsPicked.removeElement(picked);
1051 // TODO: in application this happens
1053 // if (scriptWindow != null)
1055 // scriptWindow.sendConsoleMessage(strInfo);
1056 // scriptWindow.sendConsoleMessage("\n");
1062 public void notifyCallback(EnumCallback type, Object[] data)
1069 notifyFileLoaded((String) data[1], (String) data[2],
1070 (String) data[3], (String) data[4],
1071 ((Integer) data[5]).intValue());
1075 notifyAtomPicked(((Integer) data[2]).intValue(), (String) data[1],
1077 // also highlight in alignment
1079 notifyAtomHovered(((Integer) data[2]).intValue(), (String) data[1],
1083 notifyScriptTermination((String) data[2],
1084 ((Integer) data[3]).intValue());
1087 sendConsoleEcho((String) data[1]);
1090 sendConsoleMessage((data == null) ? ((String) null)
1091 : (String) data[1]);
1094 // System.err.println("Ignoring error callback.");
1104 System.err.println("Unhandled callback " + type + " "
1105 + data[1].toString());
1108 } catch (Exception e)
1110 System.err.println("Squashed Jmol callback handler error:");
1111 e.printStackTrace();
1116 public boolean notifyEnabled(EnumCallback callbackPick)
1118 switch (callbackPick)
1138 // incremented every time a load notification is successfully handled -
1139 // lightweight mechanism for other threads to detect when they can start
1140 // referrring to new structures.
1141 private long loadNotifiesHandled = 0;
1143 public long getLoadNotifiesHandled()
1145 return loadNotifiesHandled;
1148 public void notifyFileLoaded(String fullPathName, String fileName2,
1149 String modelName, String errorMsg, int modelParts)
1151 if (errorMsg != null)
1153 fileLoadingError = errorMsg;
1157 // TODO: deal sensibly with models loaded inLine:
1158 // modelName will be null, as will fullPathName.
1160 // the rest of this routine ignores the arguments, and simply interrogates
1161 // the Jmol view to find out what structures it contains, and adds them to
1162 // the structure selection manager.
1163 fileLoadingError = null;
1164 String[] oldmodels = modelFileNames;
1165 modelFileNames = null;
1166 chainNames = new Vector();
1167 chainFile = new Hashtable();
1168 boolean notifyLoaded = false;
1169 String[] modelfilenames = getPdbFile();
1170 // first check if we've lost any structures
1171 if (oldmodels != null && oldmodels.length > 0)
1174 for (int i = 0; i < oldmodels.length; i++)
1176 for (int n = 0; n < modelfilenames.length; n++)
1178 if (modelfilenames[n] == oldmodels[i])
1180 oldmodels[i] = null;
1184 if (oldmodels[i] != null)
1191 String[] oldmfn = new String[oldm];
1193 for (int i = 0; i < oldmodels.length; i++)
1195 if (oldmodels[i] != null)
1197 oldmfn[oldm++] = oldmodels[i];
1200 // deregister the Jmol instance for these structures - we'll add
1201 // ourselves again at the end for the current structure set.
1202 ssm.removeStructureViewerListener(this, oldmfn);
1205 refreshPdbEntries();
1206 for (int modelnum = 0; modelnum < modelfilenames.length; modelnum++)
1208 String fileName = modelfilenames[modelnum];
1209 boolean foundEntry = false;
1210 MCview.PDBfile pdb = null;
1211 String pdbfile = null, pdbfhash = null;
1212 // model was probably loaded inline - so check the pdb file hashcode
1215 // calculate essential attributes for the pdb data imported inline.
1216 // prolly need to resolve modelnumber properly - for now just use our
1218 pdbfile = viewer.getData("" + (1 + _modelFileNameMap[modelnum])
1220 pdbfhash = "" + pdbfile.hashCode();
1222 if (pdbentry != null)
1224 // search pdbentries and sequences to find correct pdbentry for this
1226 for (int pe = 0; pe < pdbentry.length; pe++)
1228 boolean matches = false;
1229 if (fileName == null)
1232 // see JAL-623 - need method of matching pasted data up
1234 pdb = ssm.setMapping(sequence[pe], chains[pe], pdbfile,
1235 AppletFormatAdapter.PASTE);
1236 pdbentry[modelnum].setFile("INLINE" + pdb.id);
1244 if (matches = (fl = new File(pdbentry[pe].getFile()))
1245 .equals(new File(fileName)))
1248 // TODO: Jmol can in principle retrieve from CLASSLOADER but
1251 // to be tested. See mantis bug
1252 // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
1253 String protocol = AppletFormatAdapter.URL;
1258 protocol = AppletFormatAdapter.FILE;
1260 } catch (Exception e)
1265 // Explicitly map to the filename used by Jmol ;
1266 pdb = ssm.setMapping(sequence[pe], chains[pe], fileName,
1268 // pdbentry[pe].getFile(), protocol);
1274 // add an entry for every chain in the model
1275 for (int i = 0; i < pdb.chains.size(); i++)
1277 String chid = new String(pdb.id + ":"
1278 + ((MCview.PDBChain) pdb.chains.elementAt(i)).id);
1279 chainFile.put(chid, fileName);
1280 chainNames.addElement(chid);
1282 notifyLoaded = true;
1286 if (!foundEntry && associateNewStructs)
1288 // this is a foreign pdb file that jalview doesn't know about - add
1289 // it to the dataset and try to find a home - either on a matching
1290 // sequence or as a new sequence.
1291 String pdbcontent = viewer.getData("/" + (modelnum + 1) + ".1",
1293 // parse pdb file into a chain, etc.
1294 // locate best match for pdb in associated views and add mapping to
1296 // if properly registered then
1297 notifyLoaded = true;
1302 // so finally, update the jmol bits and pieces
1303 if (jmolpopup != null)
1305 // potential for deadlock here:
1306 // jmolpopup.updateComputedMenus();
1308 if (!isLoadingFromArchive())
1310 viewer.evalStringQuiet("model 0; select backbone;restrict;cartoon;wireframe off;spacefill off");
1312 // register ourselves as a listener and notify the gui that it needs to
1314 ssm.addStructureViewerListener(this);
1317 FeatureRenderer fr = getFeatureRenderer(null);
1323 loadNotifiesHandled++;
1325 setLoadingFromArchive(false);
1328 public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)
1330 notifyAtomPicked(iatom, strMeasure, null);
1333 public abstract void notifyScriptTermination(String strStatus,
1337 * display a message echoed from the jmol viewer
1341 public abstract void sendConsoleEcho(String strEcho); /*
1342 * { showConsole(true);
1344 * history.append("\n" +
1348 // /End JmolStatusListener
1349 // /////////////////////////////
1353 * status message - usually the response received after a script
1356 public abstract void sendConsoleMessage(String strStatus);
1358 public void setCallbackFunction(String callbackType,
1359 String callbackFunction)
1361 System.err.println("Ignoring set-callback request to associate "
1362 + callbackType + " with function " + callbackFunction);
1366 public void setJalviewColourScheme(ColourSchemeI cs)
1368 colourBySequence = false;
1377 // TODO: Switch between nucleotide or aa selection expressions
1378 Enumeration en = ResidueProperties.aa3Hash.keys();
1379 StringBuffer command = new StringBuffer("select *;color white;");
1380 while (en.hasMoreElements())
1382 res = en.nextElement().toString();
1383 index = ((Integer) ResidueProperties.aa3Hash.get(res)).intValue();
1387 col = cs.findColour(ResidueProperties.aa[index].charAt(0));
1389 command.append("select " + res + ";color[" + col.getRed() + ","
1390 + col.getGreen() + "," + col.getBlue() + "];");
1393 evalStateCommand(command.toString());
1397 public void showHelp()
1399 showUrl("http://jmol.sourceforge.net/docs/JmolUserGuide/", "jmolHelp");
1403 * open the URL somehow
1407 public abstract void showUrl(String url, String target);
1410 * called when the binding thinks the UI needs to be refreshed after a Jmol
1411 * state change. this could be because structures were loaded, or because an
1412 * error has occured.
1414 public abstract void refreshGUI();
1417 * called to show or hide the associated console window container.
1421 public abstract void showConsole(boolean show);
1424 * @param renderPanel
1426 * - when true will initialise jmol's file IO system (should be false
1427 * in applet context)
1429 * @param documentBase
1431 * @param commandOptions
1433 public void allocateViewer(Container renderPanel, boolean jmolfileio,
1434 String htmlName, URL documentBase, URL codeBase,
1435 String commandOptions)
1437 allocateViewer(renderPanel, jmolfileio, htmlName, documentBase,
1438 codeBase, commandOptions, null, null);
1443 * @param renderPanel
1445 * - when true will initialise jmol's file IO system (should be false
1446 * in applet context)
1448 * @param documentBase
1450 * @param commandOptions
1451 * @param consolePanel
1452 * - panel to contain Jmol console
1453 * @param buttonsToShow
1454 * - buttons to show on the console, in ordr
1456 public void allocateViewer(Container renderPanel, boolean jmolfileio,
1457 String htmlName, URL documentBase, URL codeBase,
1458 String commandOptions, final Container consolePanel,
1459 String buttonsToShow)
1461 if (commandOptions == null)
1463 commandOptions = "";
1465 viewer = JmolViewer.allocateViewer(renderPanel,
1466 (jmolfileio ? new SmarterJmolAdapter() : null), htmlName
1467 + ((Object) this).toString(), documentBase, codeBase,
1468 commandOptions, this);
1470 console = createJmolConsole(viewer, consolePanel, buttonsToShow);
1471 if (consolePanel != null)
1473 consolePanel.addComponentListener(this);
1479 protected abstract JmolAppConsoleInterface createJmolConsole(
1480 JmolViewer viewer2, Container consolePanel, String buttonsToShow);
1482 protected org.jmol.api.JmolAppConsoleInterface console = null;
1484 public void componentResized(ComponentEvent e)
1489 public void componentMoved(ComponentEvent e)
1494 public void componentShown(ComponentEvent e)
1499 public void componentHidden(ComponentEvent e)
1504 public void setLoadingFromArchive(boolean loadingFromArchive)
1506 this.loadingFromArchive = loadingFromArchive;
1511 * @return true if Jmol is still restoring state or loading is still going on (see setFinsihedLoadingFromArchive)
1513 public boolean isLoadingFromArchive()
1515 return loadingFromArchive && !loadingFinished;
1519 * modify flag which controls if sequence colouring events are honoured by the binding.
1520 * Should be true for normal operation
1521 * @param finishedLoading
1523 public void setFinishedLoadingFromArchive(boolean finishedLoading)
1525 loadingFinished = finishedLoading;
1528 public void setBackgroundColour(java.awt.Color col)
1531 viewer.evalStringQuiet("background [" + col.getRed() + ","
1532 + col.getGreen() + "," + col.getBlue() + "];");
1537 * add structures and any known sequence associations
1539 * @returns the pdb entries added to the current set.
1541 public synchronized PDBEntry[] addSequenceAndChain(PDBEntry[] pdbe,
1542 SequenceI[][] seq, String[][] chns)
1545 Vector v = new Vector();
1546 Vector rtn = new Vector();
1547 for (int i = 0; i < pdbentry.length; i++)
1549 v.addElement(pdbentry[i]);
1551 for (int i = 0; i < pdbe.length; i++)
1553 int r = v.indexOf(pdbe[i]);
1554 if (r == -1 || r >= pdbentry.length)
1556 rtn.addElement(new int[]
1558 v.addElement(pdbe[i]);
1562 // just make sure the sequence/chain entries are all up to date
1563 addSequenceAndChain(r, seq[i], chns[i]);
1566 pdbe = new PDBEntry[v.size()];
1571 // expand the tied seuqence[] and string[] arrays
1572 SequenceI[][] sqs = new SequenceI[pdbentry.length][];
1573 String[][] sch = new String[pdbentry.length][];
1574 System.arraycopy(sequence, 0, sqs, 0, sequence.length);
1575 System.arraycopy(chains, 0, sch, 0, this.chains.length);
1578 pdbe = new PDBEntry[rtn.size()];
1579 for (int r = 0; r < pdbe.length; r++)
1581 int[] stri = ((int[]) rtn.elementAt(r));
1582 // record the pdb file as a new addition
1583 pdbe[r] = pdbentry[stri[0]];
1584 // and add the new sequence/chain entries
1585 addSequenceAndChain(stri[0], seq[stri[1]], chns[stri[1]]);
1595 public void addSequence(int pe, SequenceI[] seq)
1597 // add sequences to the pe'th pdbentry's seuqence set.
1598 addSequenceAndChain(pe, seq, null);
1601 private void addSequenceAndChain(int pe, SequenceI[] seq, String[] tchain)
1603 if (pe < 0 || pe >= pdbentry.length)
1606 "Implementation error - no corresponding pdbentry (for index "
1607 + pe + ") to add sequences mappings to");
1609 final String nullChain = "TheNullChain";
1610 Vector s = new Vector();
1611 Vector c = new Vector();
1614 chains = new String[pdbentry.length][];
1616 if (sequence[pe] != null)
1618 for (int i = 0; i < sequence[pe].length; i++)
1620 s.addElement(sequence[pe][i]);
1621 if (chains[pe] != null)
1623 if (i < chains[pe].length)
1625 c.addElement(chains[pe][i]);
1629 c.addElement(nullChain);
1634 if (tchain != null && tchain.length > 0)
1636 c.addElement(nullChain);
1641 for (int i = 0; i < seq.length; i++)
1643 if (!s.contains(seq[i]))
1645 s.addElement(seq[i]);
1646 if (tchain != null && i < tchain.length)
1648 c.addElement(tchain[i] == null ? nullChain : tchain[i]);
1652 SequenceI[] tmp = new SequenceI[s.size()];
1657 String[] tch = new String[c.size()];
1659 for (int i = 0; i < tch.length; i++)
1661 if (tch[i] == nullChain)
1677 * @return text report of alignment between pdbfile and any associated
1678 * alignment sequences
1680 public String printMapping(String pdbfile)
1682 return ssm.printMapping(pdbfile);
1686 public void resizeInnerPanel(String data)
1688 // Jalview doesn't honour resize panel requests