2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
3 * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, G Barton, M Clamp, S Searle
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 of the License, or (at your option) any later version.
11 * Jalview is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along with Jalview. If not, see <http://www.gnu.org/licenses/>.
18 package jalview.ext.jmol;
20 import jalview.api.AlignmentViewPanel;
21 import jalview.api.FeatureRenderer;
22 import jalview.api.SequenceRenderer;
23 import jalview.api.SequenceStructureBinding;
24 import jalview.api.StructureSelectionManagerProvider;
25 import jalview.datamodel.AlignmentI;
26 import jalview.datamodel.ColumnSelection;
27 import jalview.datamodel.PDBEntry;
28 import jalview.datamodel.SequenceI;
29 import jalview.io.AppletFormatAdapter;
30 import jalview.schemes.ColourSchemeI;
31 import jalview.schemes.ResidueProperties;
32 import jalview.structure.StructureListener;
33 import jalview.structure.StructureMapping;
34 import jalview.structure.StructureSelectionManager;
36 import java.awt.Color;
37 import java.awt.Container;
38 import java.awt.event.ComponentEvent;
39 import java.awt.event.ComponentListener;
42 import java.security.AccessControlException;
43 import java.util.Enumeration;
44 import java.util.Hashtable;
46 import java.util.Vector;
48 import org.jmol.adapter.smarter.SmarterJmolAdapter;
49 import org.jmol.api.JmolAppConsoleInterface;
50 import org.jmol.api.JmolSelectionListener;
51 import org.jmol.api.JmolStatusListener;
52 import org.jmol.api.JmolViewer;
53 import org.jmol.constant.EnumCallback;
54 import org.jmol.popup.JmolPopup;
56 public abstract class JalviewJmolBinding implements StructureListener,
57 JmolStatusListener, SequenceStructureBinding,
58 JmolSelectionListener, ComponentListener,
59 StructureSelectionManagerProvider
63 * set if Jmol state is being restored from some source - instructs binding
64 * not to apply default display style when structure set is updated for first
67 private boolean loadingFromArchive = false;
70 * second flag to indicate if the jmol viewer should ignore sequence colouring
71 * events from the structure manager because the GUI is still setting up
73 private boolean loadingFinished = true;
76 * state flag used to check if the Jmol viewer's paint method can be called
78 private boolean finishedInit = false;
80 public boolean isFinishedInit()
85 public void setFinishedInit(boolean finishedInit)
87 this.finishedInit = finishedInit;
90 boolean allChainsSelected = false;
93 * when true, try to search the associated datamodel for sequences that are
94 * associated with any unknown structures in the Jmol view.
96 private boolean associateNewStructs = false;
98 Vector atomsPicked = new Vector();
100 public Vector chainNames;
105 * array of target chains for seuqences - tied to pdbentry and sequence[]
107 protected String[][] chains;
109 boolean colourBySequence = true;
111 StringBuffer eval = new StringBuffer();
113 public String fileLoadingError;
116 * the default or current model displayed if the model cannot be identified
117 * from the selection message
121 protected JmolPopup jmolpopup;
127 boolean loadedInline;
130 * current set of model filenames loaded in the Jmol instance
132 String[] modelFileNames = null;
134 public PDBEntry[] pdbentry;
137 * datasource protocol for access to PDBEntrylatest
139 String protocol = null;
141 StringBuffer resetLastRes = new StringBuffer();
144 * sequences mapped to each pdbentry
146 public SequenceI[][] sequence;
148 public StructureSelectionManager ssm;
150 public JmolViewer viewer;
152 public JalviewJmolBinding(StructureSelectionManager ssm,
153 PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String[][] chains,
157 this.sequence = sequenceIs;
158 this.chains = chains;
159 this.pdbentry = pdbentry;
160 this.protocol = protocol;
163 this.chains = new String[pdbentry.length][];
166 * viewer = JmolViewer.allocateViewer(renderPanel, new SmarterJmolAdapter(),
167 * "jalviewJmol", ap.av.applet .getDocumentBase(),
168 * ap.av.applet.getCodeBase(), "", this);
170 * jmolpopup = JmolPopup.newJmolPopup(viewer, true, "Jmol", true);
174 public JalviewJmolBinding(StructureSelectionManager ssm,
179 viewer.setJmolStatusListener(this);
180 viewer.addSelectionListener(this);
184 * construct a title string for the viewer window based on the data jalview
189 public String getViewerTitle()
191 if (sequence == null || pdbentry == null || sequence.length < 1
192 || pdbentry.length < 1 || sequence[0].length < 1)
194 return ("Jalview Jmol Window");
196 // TODO: give a more informative title when multiple structures are
198 StringBuffer title = new StringBuffer(sequence[0][0].getName() + ":"
199 + pdbentry[0].getId());
201 if (pdbentry[0].getProperty() != null)
203 if (pdbentry[0].getProperty().get("method") != null)
205 title.append(" Method: ");
206 title.append(pdbentry[0].getProperty().get("method"));
208 if (pdbentry[0].getProperty().get("chains") != null)
210 title.append(" Chain:");
211 title.append(pdbentry[0].getProperty().get("chains"));
214 return title.toString();
218 * prepare the view for a given set of models/chains. chainList contains
219 * strings of the form 'pdbfilename:Chaincode'
222 * list of chains to make visible
224 public void centerViewer(Vector chainList)
226 StringBuffer cmd = new StringBuffer();
229 for (int i = 0, iSize = chainList.size(); i < iSize; i++)
232 lbl = (String) chainList.elementAt(i);
236 mlength = lbl.indexOf(":", p);
237 } while (p < mlength && mlength < (lbl.length() - 2));
238 // TODO: lookup each pdb id and recover proper model number for it.
239 cmd.append(":" + lbl.substring(mlength + 1) + " /"
240 + (1 + getModelNum((String) chainFile.get(lbl))) + " or ");
242 if (cmd.length() > 0)
243 cmd.setLength(cmd.length() - 4);
244 evalStateCommand("select *;restrict " + cmd + ";cartoon;center " + cmd);
247 public void closeViewer()
249 viewer.setModeMouse(org.jmol.viewer.JmolConstants.MOUSE_NONE);
250 // remove listeners for all structures in viewer
251 ssm.removeStructureViewerListener(this, this.getPdbFile());
252 // and shut down jmol
253 viewer.evalStringQuiet("zap");
254 viewer.setJmolStatusListener(null);
257 releaseUIResources();
261 * called by JalviewJmolbinding after closeViewer is called - release any
262 * resources and references so they can be garbage collected.
264 protected abstract void releaseUIResources();
266 public void colourByChain()
268 colourBySequence = false;
269 // TODO: colour by chain should colour each chain distinctly across all
271 // TODO: http://issues.jalview.org/browse/JAL-628
272 evalStateCommand("select *;color chain");
275 public void colourByCharge()
277 colourBySequence = false;
278 evalStateCommand("select *;color white;select ASP,GLU;color red;"
279 + "select LYS,ARG;color blue;select CYS;color yellow");
283 * superpose the structures associated with sequences in the alignment
284 * according to their corresponding positions.
286 public void superposeStructures(AlignmentI alignment)
288 superposeStructures(alignment, -1, null);
292 * superpose the structures associated with sequences in the alignment
293 * according to their corresponding positions. ded)
295 * @param refStructure
296 * - select which pdb file to use as reference (default is -1 - the
297 * first structure in the alignment)
299 public void superposeStructures(AlignmentI alignment, int refStructure)
301 superposeStructures(alignment, refStructure, null);
305 * superpose the structures associated with sequences in the alignment
306 * according to their corresponding positions. ded)
308 * @param refStructure
309 * - select which pdb file to use as reference (default is -1 - the
310 * first structure in the alignment)
314 public void superposeStructures(AlignmentI alignment, int refStructure,
315 ColumnSelection hiddenCols)
317 superposeStructures(new AlignmentI[]
318 { alignment }, new int[]
319 { refStructure }, new ColumnSelection[]
323 public void superposeStructures(AlignmentI[] _alignment,
324 int[] _refStructure, ColumnSelection[] _hiddenCols)
326 assert (_alignment.length == _refStructure.length && _alignment.length != _hiddenCols.length);
328 String[] files = getPdbFile();
329 // check to see if we are still waiting for Jmol files
330 long starttime=System.currentTimeMillis();
331 boolean waiting=true;
334 for (String file:files)
337 // HACK - in Jalview 2.8 this call may not be threadsafe so we catch
338 // every possible exception
339 StructureMapping[] sm = ssm.getMapping(file);
340 if (sm == null || sm.length == 0)
344 } catch (Exception x)
352 // we wait around for a reasonable time before we give up
353 } while (waiting && System.currentTimeMillis()<(10000+1000*files.length+starttime));
356 System.err.println("RUNTIME PROBLEM: Jmol seems to be taking a long time to process all the structures.");
359 StringBuffer selectioncom = new StringBuffer();
360 // In principle - nSeconds specifies the speed of animation for each
361 // superposition - but is seems to behave weirdly, so we don't specify it.
362 String nSeconds = " ";
363 if (files.length > 10)
365 nSeconds = " 0.00001 ";
369 nSeconds = " " + (2.0 / files.length) + " ";
370 // if (nSeconds).substring(0,5)+" ";
372 // see JAL-1345 - should really automatically turn off the animation for
373 // large numbers of structures, but Jmol doesn't seem to allow that.
375 // union of all aligned positions are collected together.
376 for (int a = 0; a < _alignment.length; a++)
378 int refStructure = _refStructure[a];
379 AlignmentI alignment = _alignment[a];
380 ColumnSelection hiddenCols = _hiddenCols[a];
382 && selectioncom.length() > 0
383 && !selectioncom.substring(selectioncom.length() - 1).equals(
386 selectioncom.append("|");
388 // process this alignment
389 if (refStructure >= files.length)
391 System.err.println("Invalid reference structure value "
395 if (refStructure < -1)
399 StringBuffer command = new StringBuffer();
401 boolean matched[] = new boolean[alignment.getWidth()];
402 for (int m = 0; m < matched.length; m++)
405 matched[m] = (hiddenCols != null) ? hiddenCols.isVisible(m) : true;
408 int commonrpositions[][] = new int[files.length][alignment.getWidth()];
409 String isel[] = new String[files.length];
410 // reference structure - all others are superposed in it
411 String[] targetC = new String[files.length];
412 String[] chainNames = new String[files.length];
413 for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
415 StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
416 // RACE CONDITION - getMapping only returns Jmol loaded filenames once
417 // Jmol callback has completed.
418 if (mapping == null || mapping.length < 1)
420 throw new Error("Implementation error - Jmol seems to be still working on getting its data - report at http://issues.jalview.org/browse/JAL-1016");
423 for (int s = 0; s < sequence[pdbfnum].length; s++)
425 for (int sp, m = 0; m < mapping.length; m++)
427 if (mapping[m].getSequence() == sequence[pdbfnum][s]
428 && (sp = alignment.findIndex(sequence[pdbfnum][s])) > -1)
430 if (refStructure == -1)
432 refStructure = pdbfnum;
434 SequenceI asp = alignment.getSequenceAt(sp);
435 for (int r = 0; r < matched.length; r++)
441 matched[r] = false; // assume this is not a good site
442 if (r >= asp.getLength())
447 if (jalview.util.Comparison.isGap(asp.getCharAt(r)))
449 // no mapping to gaps in sequence
452 int t = asp.findPosition(r); // sequence position
453 int apos = mapping[m].getAtomNum(t);
454 int pos = mapping[m].getPDBResNum(t);
456 if (pos < 1 || pos == lastPos)
458 // can't align unmapped sequence
461 matched[r] = true; // this is a good ite
463 // just record this residue position
464 commonrpositions[pdbfnum][r] = pos;
466 // create model selection suffix
467 isel[pdbfnum] = "/" + (pdbfnum + 1) + ".1";
468 if (mapping[m].getChain() == null
469 || mapping[m].getChain().trim().length() == 0)
471 targetC[pdbfnum] = "";
475 targetC[pdbfnum] = ":" + mapping[m].getChain();
477 chainNames[pdbfnum] = mapping[m].getPdbId()
479 // move on to next pdb file
480 s = sequence[pdbfnum].length;
487 // TODO: consider bailing if nmatched less than 4 because superposition
490 // TODO: refactor superposable position search (above) from jmol selection
491 // construction (below)
493 String[] selcom = new String[files.length];
495 // generate select statements to select regions to superimpose structures
497 for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
499 String chainCd = targetC[pdbfnum];
502 StringBuffer molsel = new StringBuffer();
504 for (int r = 0; r < matched.length; r++)
512 if (lpos != commonrpositions[pdbfnum][r] - 1)
518 molsel.append(chainCd);
519 // molsel.append("} {");
525 // continuous run - and lpos >-1
528 // at the beginning, so add dash
534 lpos = commonrpositions[pdbfnum][r];
535 // molsel.append(lpos);
538 // add final selection phrase
542 molsel.append(chainCd);
545 selcom[pdbfnum] = molsel.toString();
546 selectioncom.append("((");
547 selectioncom.append(selcom[pdbfnum].substring(1,
548 selcom[pdbfnum].length() - 1));
549 selectioncom.append(" )& ");
550 selectioncom.append(pdbfnum + 1);
551 selectioncom.append(".1)");
552 if (pdbfnum < files.length - 1)
554 selectioncom.append("|");
558 for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
560 if (pdbfnum == refStructure)
564 command.append("echo ");
565 command.append("\"Superposing (");
566 command.append(chainNames[pdbfnum]);
567 command.append(") against reference (");
568 command.append(chainNames[refStructure]);
569 command.append(")\";\ncompare "+nSeconds);
571 command.append(1 + pdbfnum);
572 command.append(".1} {");
573 command.append(1 + refStructure);
574 command.append(".1} SUBSET {*.CA | *.P} ATOMS ");
576 // form the matched pair strings
578 for (int s = 0; s < 2; s++)
580 command.append(selcom[(s == 0 ? pdbfnum : refStructure)]);
582 command.append(" ROTATE TRANSLATE;\n");
584 System.out.println("Select regions:\n" + selectioncom.toString());
585 evalStateCommand("select *; cartoons off; backbone; select ("
586 + selectioncom.toString() + "); cartoons; ");
587 // selcom.append("; ribbons; ");
588 System.out.println("Superimpose command(s):\n" + command.toString());
590 evalStateCommand(command.toString());
592 if (selectioncom.length() > 0)
593 {// finally, mark all regions that were superposed.
594 if (selectioncom.substring(selectioncom.length() - 1).equals("|"))
596 selectioncom.setLength(selectioncom.length() - 1);
598 System.out.println("Select regions:\n" + selectioncom.toString());
599 evalStateCommand("select *; cartoons off; backbone; select ("
600 + selectioncom.toString() + "); cartoons; ");
601 // evalStateCommand("select *; backbone; select "+selcom.toString()+"; cartoons; center "+selcom.toString());
605 public void evalStateCommand(String command)
608 if (lastCommand == null || !lastCommand.equals(command))
610 viewer.evalStringQuiet(command + "\n");
613 lastCommand = command;
617 * colour any structures associated with sequences in the given alignment
618 * using the getFeatureRenderer() and getSequenceRenderer() renderers but only
619 * if colourBySequence is enabled.
621 public void colourBySequence(boolean showFeatures,
622 jalview.api.AlignmentViewPanel alignmentv)
624 if (!colourBySequence || !loadingFinished)
630 String[] files = getPdbFile();
632 SequenceRenderer sr = getSequenceRenderer(alignmentv);
634 FeatureRenderer fr = null;
637 fr = getFeatureRenderer(alignmentv);
639 AlignmentI alignment = alignmentv.getAlignment();
641 for (jalview.structure.StructureMappingcommandSet cpdbbyseq : JmolCommands
642 .getColourBySequenceCommand(ssm, files, sequence, sr, fr,
644 for (String cbyseq : cpdbbyseq.commands)
646 evalStateCommand(cbyseq);
650 public boolean isColourBySequence()
652 return colourBySequence;
655 public void setColourBySequence(boolean colourBySequence)
657 this.colourBySequence = colourBySequence;
660 public void createImage(String file, String type, int quality)
662 System.out.println("JMOL CREATE IMAGE");
665 public String createImage(String fileName, String type,
666 Object textOrBytes, int quality)
668 System.out.println("JMOL CREATE IMAGE");
672 public String eval(String strEval)
674 // System.out.println(strEval);
675 // "# 'eval' is implemented only for the applet.";
679 // End StructureListener
680 // //////////////////////////
682 public float[][] functionXY(String functionName, int x, int y)
687 public float[][][] functionXYZ(String functionName, int nx, int ny, int nz)
689 // TODO Auto-generated method stub
693 public Color getColour(int atomIndex, int pdbResNum, String chain,
696 if (getModelNum(pdbfile) < 0)
698 // TODO: verify atomIndex is selecting correct model.
699 return new Color(viewer.getAtomArgb(atomIndex));
703 * returns the current featureRenderer that should be used to colour the
710 public abstract FeatureRenderer getFeatureRenderer(
711 AlignmentViewPanel alignment);
714 * instruct the Jalview binding to update the pdbentries vector if necessary
715 * prior to matching the jmol view's contents to the list of structure files
716 * Jalview knows about.
718 public abstract void refreshPdbEntries();
720 private int getModelNum(String modelFileName)
722 String[] mfn = getPdbFile();
727 for (int i = 0; i < mfn.length; i++)
729 if (mfn[i].equalsIgnoreCase(modelFileName))
736 * map between index of model filename returned from getPdbFile and the first
737 * index of models from this file in the viewer. Note - this is not trimmed -
738 * use getPdbFile to get number of unique models.
740 private int _modelFileNameMap[];
742 // ////////////////////////////////
743 // /StructureListener
744 public synchronized String[] getPdbFile()
748 return new String[0];
750 if (modelFileNames == null)
753 String mset[] = new String[viewer.getModelCount()];
754 _modelFileNameMap = new int[mset.length];
756 String m = viewer.getModelFileName(0);
761 mset[0] = new File(m).getAbsolutePath();
762 } catch (AccessControlException x)
764 // usually not allowed to do this in applet, so keep raw handle
766 // System.err.println("jmolBinding: Using local file string from Jmol: "+m);
769 for (int i = 1; i < mset.length; i++)
771 m = viewer.getModelFileName(i);
776 mset[j] = new File(m).getAbsolutePath();
777 } catch (AccessControlException x)
779 // usually not allowed to do this in applet, so keep raw handle
781 // System.err.println("jmolBinding: Using local file string from Jmol: "+m);
784 _modelFileNameMap[j] = i; // record the model index for the filename
785 // skip any additional models in the same file (NMR structures)
786 if ((mset[j] == null ? mset[j] != mset[j - 1]
787 : (mset[j - 1] == null || !mset[j].equals(mset[j - 1]))))
792 modelFileNames = new String[j];
793 System.arraycopy(mset, 0, modelFileNames, 0, j);
795 return modelFileNames;
799 * map from string to applet
801 public Map getRegistryInfo()
803 // TODO Auto-generated method stub
808 * returns the current sequenceRenderer that should be used to colour the
815 public abstract SequenceRenderer getSequenceRenderer(
816 AlignmentViewPanel alignment);
818 // ///////////////////////////////
819 // JmolStatusListener
821 public void handlePopupMenu(int x, int y)
823 jmolpopup.show(x, y);
827 public void highlightAtom(int atomIndex, int pdbResNum, String chain,
830 if (modelFileNames == null)
835 // look up file model number for this pdbfile
838 // may need to adjust for URLencoding here - we don't worry about that yet.
839 while (mdlNum < modelFileNames.length
840 && !pdbfile.equals(modelFileNames[mdlNum]))
842 // System.out.println("nomatch:"+pdbfile+"\nmodelfn:"+fn);
845 if (mdlNum == modelFileNames.length)
851 // if (!pdbfile.equals(pdbentry.getFile()))
853 if (resetLastRes.length() > 0)
855 viewer.evalStringQuiet(resetLastRes.toString());
859 eval.append("select " + pdbResNum); // +modelNum
861 resetLastRes.setLength(0);
862 resetLastRes.append("select " + pdbResNum); // +modelNum
865 resetLastRes.append(":");
866 if (!chain.equals(" "))
869 resetLastRes.append(chain);
872 eval.append(" /" + (mdlNum + 1));
873 resetLastRes.append("/" + (mdlNum + 1));
875 eval.append(";wireframe 100;" + eval.toString() + " and not hetero;");
877 resetLastRes.append(";wireframe 0;" + resetLastRes.toString()
878 + " and not hetero; spacefill 0;");
880 eval.append("spacefill 200;select none");
882 viewer.evalStringQuiet(eval.toString());
887 boolean debug = true;
889 private void jmolHistory(boolean enable)
891 viewer.evalStringQuiet("History " + ((debug || enable) ? "on" : "off"));
894 public void loadInline(String string)
898 // viewer.loadInline(strModel, isAppend);
900 // construct fake fullPathName and fileName so we can identify the file
902 // Then, construct pass a reader for the string to Jmol.
903 // ((org.jmol.Viewer.Viewer) viewer).loadModelFromFile(fullPathName,
904 // fileName, null, reader, false, null, null, 0);
905 viewer.openStringInline(string);
908 public void mouseOverStructure(int atomIndex, String strInfo)
911 int alocsep = strInfo.indexOf("^");
912 int mdlSep = strInfo.indexOf("/");
913 int chainSeparator = strInfo.indexOf(":"), chainSeparator1 = -1;
915 if (chainSeparator == -1)
917 chainSeparator = strInfo.indexOf(".");
918 if (mdlSep > -1 && mdlSep < chainSeparator)
920 chainSeparator1 = chainSeparator;
921 chainSeparator = mdlSep;
924 // handle insertion codes
927 pdbResNum = Integer.parseInt(strInfo.substring(
928 strInfo.indexOf("]") + 1, alocsep));
933 pdbResNum = Integer.parseInt(strInfo.substring(
934 strInfo.indexOf("]") + 1, chainSeparator));
938 if (strInfo.indexOf(":") > -1)
939 chainId = strInfo.substring(strInfo.indexOf(":") + 1,
940 strInfo.indexOf("."));
946 String pdbfilename = modelFileNames[frameNo]; // default is first or current
950 if (chainSeparator1 == -1)
952 chainSeparator1 = strInfo.indexOf(".", mdlSep);
954 String mdlId = (chainSeparator1 > -1) ? strInfo.substring(mdlSep + 1,
955 chainSeparator1) : strInfo.substring(mdlSep + 1);
958 // recover PDB filename for the model hovered over.
959 int _mp = _modelFileNameMap.length - 1, mnumber = new Integer(mdlId)
961 while (mnumber < _modelFileNameMap[_mp])
965 pdbfilename = modelFileNames[_mp];
966 if (pdbfilename == null)
968 pdbfilename = new File(viewer.getModelFileName(mnumber))
972 } catch (Exception e)
977 if (lastMessage == null || !lastMessage.equals(strInfo))
978 ssm.mouseOverStructure(pdbResNum, chainId, pdbfilename);
980 lastMessage = strInfo;
983 public void notifyAtomHovered(int atomIndex, String strInfo, String data)
987 System.err.println("Ignoring additional hover info: " + data
988 + " (other info: '" + strInfo + "' pos " + atomIndex + ")");
990 mouseOverStructure(atomIndex, strInfo);
994 * { if (history != null && strStatus != null &&
995 * !strStatus.equals("Script completed")) { history.append("\n" + strStatus);
999 public void notifyAtomPicked(int atomIndex, String strInfo, String strData)
1002 * this implements the toggle label behaviour copied from the original
1003 * structure viewer, MCView
1005 if (strData != null)
1007 System.err.println("Ignoring additional pick data string " + strData);
1009 int chainSeparator = strInfo.indexOf(":");
1011 if (chainSeparator == -1)
1012 chainSeparator = strInfo.indexOf(".");
1014 String picked = strInfo.substring(strInfo.indexOf("]") + 1,
1016 String mdlString = "";
1017 if ((p = strInfo.indexOf(":")) > -1)
1018 picked += strInfo.substring(p + 1, strInfo.indexOf("."));
1020 if ((p = strInfo.indexOf("/")) > -1)
1022 mdlString += strInfo.substring(p, strInfo.indexOf(" #"));
1024 picked = "((" + picked + ".CA" + mdlString + ")|(" + picked + ".P"
1028 if (!atomsPicked.contains(picked))
1030 viewer.evalStringQuiet("select " + picked + ";label %n %r:%c");
1031 atomsPicked.addElement(picked);
1035 viewer.evalString("select " + picked + ";label off");
1036 atomsPicked.removeElement(picked);
1039 // TODO: in application this happens
1041 // if (scriptWindow != null)
1043 // scriptWindow.sendConsoleMessage(strInfo);
1044 // scriptWindow.sendConsoleMessage("\n");
1050 public void notifyCallback(EnumCallback type, Object[] data)
1057 notifyFileLoaded((String) data[1], (String) data[2],
1058 (String) data[3], (String) data[4],
1059 ((Integer) data[5]).intValue());
1063 notifyAtomPicked(((Integer) data[2]).intValue(), (String) data[1],
1065 // also highlight in alignment
1067 notifyAtomHovered(((Integer) data[2]).intValue(), (String) data[1],
1071 notifyScriptTermination((String) data[2],
1072 ((Integer) data[3]).intValue());
1075 sendConsoleEcho((String) data[1]);
1078 sendConsoleMessage((data == null) ? ((String) null)
1079 : (String) data[1]);
1082 // System.err.println("Ignoring error callback.");
1092 System.err.println("Unhandled callback " + type + " "
1093 + data[1].toString());
1096 } catch (Exception e)
1098 System.err.println("Squashed Jmol callback handler error:");
1099 e.printStackTrace();
1104 public boolean notifyEnabled(EnumCallback callbackPick)
1106 switch (callbackPick)
1126 // incremented every time a load notification is successfully handled -
1127 // lightweight mechanism for other threads to detect when they can start
1128 // referrring to new structures.
1129 private long loadNotifiesHandled = 0;
1131 public long getLoadNotifiesHandled()
1133 return loadNotifiesHandled;
1136 public void notifyFileLoaded(String fullPathName, String fileName2,
1137 String modelName, String errorMsg, int modelParts)
1139 if (errorMsg != null)
1141 fileLoadingError = errorMsg;
1145 // TODO: deal sensibly with models loaded inLine:
1146 // modelName will be null, as will fullPathName.
1148 // the rest of this routine ignores the arguments, and simply interrogates
1149 // the Jmol view to find out what structures it contains, and adds them to
1150 // the structure selection manager.
1151 fileLoadingError = null;
1152 String[] oldmodels = modelFileNames;
1153 modelFileNames = null;
1154 chainNames = new Vector();
1155 chainFile = new Hashtable();
1156 boolean notifyLoaded = false;
1157 String[] modelfilenames = getPdbFile();
1158 // first check if we've lost any structures
1159 if (oldmodels != null && oldmodels.length > 0)
1162 for (int i = 0; i < oldmodels.length; i++)
1164 for (int n = 0; n < modelfilenames.length; n++)
1166 if (modelfilenames[n] == oldmodels[i])
1168 oldmodels[i] = null;
1172 if (oldmodels[i] != null)
1179 String[] oldmfn = new String[oldm];
1181 for (int i = 0; i < oldmodels.length; i++)
1183 if (oldmodels[i] != null)
1185 oldmfn[oldm++] = oldmodels[i];
1188 // deregister the Jmol instance for these structures - we'll add
1189 // ourselves again at the end for the current structure set.
1190 ssm.removeStructureViewerListener(this, oldmfn);
1193 refreshPdbEntries();
1194 for (int modelnum = 0; modelnum < modelfilenames.length; modelnum++)
1196 String fileName = modelfilenames[modelnum];
1197 boolean foundEntry = false;
1198 MCview.PDBfile pdb = null;
1199 String pdbfile = null, pdbfhash = null;
1200 // model was probably loaded inline - so check the pdb file hashcode
1203 // calculate essential attributes for the pdb data imported inline.
1204 // prolly need to resolve modelnumber properly - for now just use our
1206 pdbfile = viewer.getData("" + (1 + _modelFileNameMap[modelnum])
1208 pdbfhash = "" + pdbfile.hashCode();
1210 if (pdbentry != null)
1212 // search pdbentries and sequences to find correct pdbentry for this
1214 for (int pe = 0; pe < pdbentry.length; pe++)
1216 boolean matches = false;
1217 if (fileName == null)
1220 // see JAL-623 - need method of matching pasted data up
1222 pdb = ssm.setMapping(sequence[pe], chains[pe], pdbfile,
1223 AppletFormatAdapter.PASTE);
1224 pdbentry[modelnum].setFile("INLINE" + pdb.id);
1232 if (matches = (fl = new File(pdbentry[pe].getFile()))
1233 .equals(new File(fileName)))
1236 // TODO: Jmol can in principle retrieve from CLASSLOADER but
1239 // to be tested. See mantis bug
1240 // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
1241 String protocol = AppletFormatAdapter.URL;
1246 protocol = AppletFormatAdapter.FILE;
1248 } catch (Exception e)
1253 // Explicitly map to the filename used by Jmol ;
1254 pdb = ssm.setMapping(sequence[pe], chains[pe], fileName,
1256 // pdbentry[pe].getFile(), protocol);
1262 // add an entry for every chain in the model
1263 for (int i = 0; i < pdb.chains.size(); i++)
1265 String chid = new String(pdb.id + ":"
1266 + ((MCview.PDBChain) pdb.chains.elementAt(i)).id);
1267 chainFile.put(chid, fileName);
1268 chainNames.addElement(chid);
1270 notifyLoaded = true;
1274 if (!foundEntry && associateNewStructs)
1276 // this is a foreign pdb file that jalview doesn't know about - add
1277 // it to the dataset and try to find a home - either on a matching
1278 // sequence or as a new sequence.
1279 String pdbcontent = viewer.getData("/" + (modelnum + 1) + ".1",
1281 // parse pdb file into a chain, etc.
1282 // locate best match for pdb in associated views and add mapping to
1284 // if properly registered then
1285 notifyLoaded = true;
1290 // so finally, update the jmol bits and pieces
1291 if (jmolpopup != null)
1293 // potential for deadlock here:
1294 // jmolpopup.updateComputedMenus();
1296 if (!isLoadingFromArchive())
1298 viewer.evalStringQuiet("model 0; select backbone;restrict;cartoon;wireframe off;spacefill off");
1300 // register ourselves as a listener and notify the gui that it needs to
1302 ssm.addStructureViewerListener(this);
1305 FeatureRenderer fr = getFeatureRenderer(null);
1311 loadNotifiesHandled++;
1313 setLoadingFromArchive(false);
1316 public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)
1318 notifyAtomPicked(iatom, strMeasure, null);
1321 public abstract void notifyScriptTermination(String strStatus,
1325 * display a message echoed from the jmol viewer
1329 public abstract void sendConsoleEcho(String strEcho); /*
1330 * { showConsole(true);
1332 * history.append("\n" +
1336 // /End JmolStatusListener
1337 // /////////////////////////////
1341 * status message - usually the response received after a script
1344 public abstract void sendConsoleMessage(String strStatus);
1346 public void setCallbackFunction(String callbackType,
1347 String callbackFunction)
1349 System.err.println("Ignoring set-callback request to associate "
1350 + callbackType + " with function " + callbackFunction);
1354 public void setJalviewColourScheme(ColourSchemeI cs)
1356 colourBySequence = false;
1365 // TODO: Switch between nucleotide or aa selection expressions
1366 Enumeration en = ResidueProperties.aa3Hash.keys();
1367 StringBuffer command = new StringBuffer("select *;color white;");
1368 while (en.hasMoreElements())
1370 res = en.nextElement().toString();
1371 index = ((Integer) ResidueProperties.aa3Hash.get(res)).intValue();
1375 col = cs.findColour(ResidueProperties.aa[index].charAt(0));
1377 command.append("select " + res + ";color[" + col.getRed() + ","
1378 + col.getGreen() + "," + col.getBlue() + "];");
1381 evalStateCommand(command.toString());
1385 public void showHelp()
1387 showUrl("http://jmol.sourceforge.net/docs/JmolUserGuide/", "jmolHelp");
1391 * open the URL somehow
1395 public abstract void showUrl(String url, String target);
1398 * called when the binding thinks the UI needs to be refreshed after a Jmol
1399 * state change. this could be because structures were loaded, or because an
1400 * error has occured.
1402 public abstract void refreshGUI();
1405 * called to show or hide the associated console window container.
1409 public abstract void showConsole(boolean show);
1412 * @param renderPanel
1414 * - when true will initialise jmol's file IO system (should be false
1415 * in applet context)
1417 * @param documentBase
1419 * @param commandOptions
1421 public void allocateViewer(Container renderPanel, boolean jmolfileio,
1422 String htmlName, URL documentBase, URL codeBase,
1423 String commandOptions)
1425 allocateViewer(renderPanel, jmolfileio, htmlName, documentBase,
1426 codeBase, commandOptions, null, null);
1431 * @param renderPanel
1433 * - when true will initialise jmol's file IO system (should be false
1434 * in applet context)
1436 * @param documentBase
1438 * @param commandOptions
1439 * @param consolePanel
1440 * - panel to contain Jmol console
1441 * @param buttonsToShow
1442 * - buttons to show on the console, in ordr
1444 public void allocateViewer(Container renderPanel, boolean jmolfileio,
1445 String htmlName, URL documentBase, URL codeBase,
1446 String commandOptions, final Container consolePanel,
1447 String buttonsToShow)
1449 if (commandOptions == null)
1451 commandOptions = "";
1453 viewer = JmolViewer.allocateViewer(renderPanel,
1454 (jmolfileio ? new SmarterJmolAdapter() : null), htmlName
1455 + ((Object) this).toString(), documentBase, codeBase,
1456 commandOptions, this);
1458 console = createJmolConsole(viewer, consolePanel, buttonsToShow);
1459 if (consolePanel != null)
1461 consolePanel.addComponentListener(this);
1467 protected abstract JmolAppConsoleInterface createJmolConsole(
1468 JmolViewer viewer2, Container consolePanel, String buttonsToShow);
1470 protected org.jmol.api.JmolAppConsoleInterface console = null;
1472 public void componentResized(ComponentEvent e)
1477 public void componentMoved(ComponentEvent e)
1482 public void componentShown(ComponentEvent e)
1487 public void componentHidden(ComponentEvent e)
1492 public void setLoadingFromArchive(boolean loadingFromArchive)
1494 this.loadingFromArchive = loadingFromArchive;
1499 * @return true if Jmol is still restoring state or loading is still going on (see setFinsihedLoadingFromArchive)
1501 public boolean isLoadingFromArchive()
1503 return loadingFromArchive && !loadingFinished;
1507 * modify flag which controls if sequence colouring events are honoured by the binding.
1508 * Should be true for normal operation
1509 * @param finishedLoading
1511 public void setFinishedLoadingFromArchive(boolean finishedLoading)
1513 loadingFinished = finishedLoading;
1516 public void setBackgroundColour(java.awt.Color col)
1519 viewer.evalStringQuiet("background [" + col.getRed() + ","
1520 + col.getGreen() + "," + col.getBlue() + "];");
1525 * add structures and any known sequence associations
1527 * @returns the pdb entries added to the current set.
1529 public synchronized PDBEntry[] addSequenceAndChain(PDBEntry[] pdbe,
1530 SequenceI[][] seq, String[][] chns)
1533 Vector v = new Vector();
1534 Vector rtn = new Vector();
1535 for (int i = 0; i < pdbentry.length; i++)
1537 v.addElement(pdbentry[i]);
1539 for (int i = 0; i < pdbe.length; i++)
1541 int r = v.indexOf(pdbe[i]);
1542 if (r == -1 || r >= pdbentry.length)
1544 rtn.addElement(new int[]
1546 v.addElement(pdbe[i]);
1550 // just make sure the sequence/chain entries are all up to date
1551 addSequenceAndChain(r, seq[i], chns[i]);
1554 pdbe = new PDBEntry[v.size()];
1559 // expand the tied seuqence[] and string[] arrays
1560 SequenceI[][] sqs = new SequenceI[pdbentry.length][];
1561 String[][] sch = new String[pdbentry.length][];
1562 System.arraycopy(sequence, 0, sqs, 0, sequence.length);
1563 System.arraycopy(chains, 0, sch, 0, this.chains.length);
1566 pdbe = new PDBEntry[rtn.size()];
1567 for (int r = 0; r < pdbe.length; r++)
1569 int[] stri = ((int[]) rtn.elementAt(r));
1570 // record the pdb file as a new addition
1571 pdbe[r] = pdbentry[stri[0]];
1572 // and add the new sequence/chain entries
1573 addSequenceAndChain(stri[0], seq[stri[1]], chns[stri[1]]);
1583 public void addSequence(int pe, SequenceI[] seq)
1585 // add sequences to the pe'th pdbentry's seuqence set.
1586 addSequenceAndChain(pe, seq, null);
1589 private void addSequenceAndChain(int pe, SequenceI[] seq, String[] tchain)
1591 if (pe < 0 || pe >= pdbentry.length)
1594 "Implementation error - no corresponding pdbentry (for index "
1595 + pe + ") to add sequences mappings to");
1597 final String nullChain = "TheNullChain";
1598 Vector s = new Vector();
1599 Vector c = new Vector();
1602 chains = new String[pdbentry.length][];
1604 if (sequence[pe] != null)
1606 for (int i = 0; i < sequence[pe].length; i++)
1608 s.addElement(sequence[pe][i]);
1609 if (chains[pe] != null)
1611 if (i < chains[pe].length)
1613 c.addElement(chains[pe][i]);
1617 c.addElement(nullChain);
1622 if (tchain != null && tchain.length > 0)
1624 c.addElement(nullChain);
1629 for (int i = 0; i < seq.length; i++)
1631 if (!s.contains(seq[i]))
1633 s.addElement(seq[i]);
1634 if (tchain != null && i < tchain.length)
1636 c.addElement(tchain[i] == null ? nullChain : tchain[i]);
1640 SequenceI[] tmp = new SequenceI[s.size()];
1645 String[] tch = new String[c.size()];
1647 for (int i = 0; i < tch.length; i++)
1649 if (tch[i] == nullChain)
1665 * @return text report of alignment between pdbfile and any associated
1666 * alignment sequences
1668 public String printMapping(String pdbfile)
1670 return ssm.printMapping(pdbfile);
1674 public void resizeInnerPanel(String data)
1676 // Jalview doesn't honour resize panel requests