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.Enumeration;
50 import java.util.Hashtable;
51 import java.util.List;
53 import java.util.Vector;
55 import org.jmol.adapter.smarter.SmarterJmolAdapter;
56 import org.jmol.api.JmolAppConsoleInterface;
57 import org.jmol.api.JmolSelectionListener;
58 import org.jmol.api.JmolStatusListener;
59 import org.jmol.api.JmolViewer;
60 import org.jmol.constant.EnumCallback;
61 import org.jmol.popup.JmolPopup;
63 public abstract class JalviewJmolBinding extends SequenceStructureBindingModel implements StructureListener,
64 JmolStatusListener, SequenceStructureBinding,
65 JmolSelectionListener, ComponentListener,
66 StructureSelectionManagerProvider
70 * state flag used to check if the Jmol viewer's paint method can be called
72 private boolean finishedInit = false;
74 public boolean isFinishedInit()
79 public void setFinishedInit(boolean finishedInit)
81 this.finishedInit = finishedInit;
84 boolean allChainsSelected = false;
87 * when true, try to search the associated datamodel for sequences that are
88 * associated with any unknown structures in the Jmol view.
90 private boolean associateNewStructs = false;
92 Vector atomsPicked = new Vector();
94 public Vector chainNames;
99 * array of target chains for seuqences - tied to pdbentry and sequence[]
101 protected String[][] chains;
103 boolean colourBySequence = true;
105 StringBuffer eval = new StringBuffer();
107 public String fileLoadingError;
110 * the default or current model displayed if the model cannot be identified
111 * from the selection message
115 protected JmolPopup jmolpopup;
121 boolean loadedInline;
124 * current set of model filenames loaded in the Jmol instance
126 String[] modelFileNames = null;
128 public PDBEntry[] pdbentry;
131 * datasource protocol for access to PDBEntrylatest
133 String protocol = null;
135 StringBuffer resetLastRes = new StringBuffer();
138 * sequences mapped to each pdbentry
140 public SequenceI[][] sequence;
142 public StructureSelectionManager ssm;
144 public JmolViewer viewer;
146 public JalviewJmolBinding(StructureSelectionManager ssm,
147 PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String[][] chains,
151 this.sequence = sequenceIs;
152 this.chains = chains;
153 this.pdbentry = pdbentry;
154 this.protocol = protocol;
157 this.chains = new String[pdbentry.length][];
160 * viewer = JmolViewer.allocateViewer(renderPanel, new SmarterJmolAdapter(),
161 * "jalviewJmol", ap.av.applet .getDocumentBase(),
162 * ap.av.applet.getCodeBase(), "", this);
164 * jmolpopup = JmolPopup.newJmolPopup(viewer, true, "Jmol", true);
168 public JalviewJmolBinding(StructureSelectionManager ssm,
173 viewer.setJmolStatusListener(this);
174 viewer.addSelectionListener(this);
178 * construct a title string for the viewer window based on the data jalview
183 public String getViewerTitle()
185 if (sequence == null || pdbentry == null || sequence.length < 1
186 || pdbentry.length < 1 || sequence[0].length < 1)
188 return ("Jalview Jmol Window");
190 // TODO: give a more informative title when multiple structures are
192 StringBuffer title = new StringBuffer(sequence[0][0].getName() + ":"
193 + pdbentry[0].getId());
195 if (pdbentry[0].getProperty() != null)
197 if (pdbentry[0].getProperty().get("method") != null)
199 title.append(" Method: ");
200 title.append(pdbentry[0].getProperty().get("method"));
202 if (pdbentry[0].getProperty().get("chains") != null)
204 title.append(" Chain:");
205 title.append(pdbentry[0].getProperty().get("chains"));
208 return title.toString();
212 * prepare the view for a given set of models/chains. chainList contains
213 * strings of the form 'pdbfilename:Chaincode'
216 * list of chains to make visible
218 public void centerViewer(Vector chainList)
220 StringBuffer cmd = new StringBuffer();
223 for (int i = 0, iSize = chainList.size(); i < iSize; i++)
226 lbl = (String) chainList.elementAt(i);
230 mlength = lbl.indexOf(":", p);
231 } while (p < mlength && mlength < (lbl.length() - 2));
232 // TODO: lookup each pdb id and recover proper model number for it.
233 cmd.append(":" + lbl.substring(mlength + 1) + " /"
234 + (1 + getModelNum((String) chainFile.get(lbl))) + " or ");
236 if (cmd.length() > 0)
238 cmd.setLength(cmd.length() - 4);
240 evalStateCommand("select *;restrict " + cmd + ";cartoon;center " + cmd);
243 public void closeViewer()
245 viewer.setModeMouse(org.jmol.viewer.JmolConstants.MOUSE_NONE);
246 // remove listeners for all structures in viewer
247 ssm.removeStructureViewerListener(this, this.getPdbFile());
248 // and shut down jmol
249 viewer.evalStringQuiet("zap");
250 viewer.setJmolStatusListener(null);
253 releaseUIResources();
257 * called by JalviewJmolbinding after closeViewer is called - release any
258 * resources and references so they can be garbage collected.
260 protected abstract void releaseUIResources();
262 public void colourByChain()
264 colourBySequence = false;
265 // TODO: colour by chain should colour each chain distinctly across all
267 // TODO: http://issues.jalview.org/browse/JAL-628
268 evalStateCommand("select *;color chain");
271 public void colourByCharge()
273 colourBySequence = false;
274 evalStateCommand("select *;color white;select ASP,GLU;color red;"
275 + "select LYS,ARG;color blue;select CYS;color yellow");
279 * superpose the structures associated with sequences in the alignment
280 * according to their corresponding positions.
282 public void superposeStructures(AlignmentI alignment)
284 superposeStructures(alignment, -1, null);
288 * superpose the structures associated with sequences in the alignment
289 * according to their corresponding positions. ded)
291 * @param refStructure
292 * - select which pdb file to use as reference (default is -1 - the
293 * first structure in the alignment)
295 public void superposeStructures(AlignmentI alignment, int refStructure)
297 superposeStructures(alignment, refStructure, null);
301 * superpose the structures associated with sequences in the alignment
302 * according to their corresponding positions. ded)
304 * @param refStructure
305 * - select which pdb file to use as reference (default is -1 - the
306 * first structure in the alignment)
310 public void superposeStructures(AlignmentI alignment, int refStructure,
311 ColumnSelection hiddenCols)
313 superposeStructures(new AlignmentI[]
314 { alignment }, new int[]
315 { refStructure }, new ColumnSelection[]
319 public void superposeStructures(AlignmentI[] _alignment,
320 int[] _refStructure, ColumnSelection[] _hiddenCols)
322 assert (_alignment.length == _refStructure.length && _alignment.length != _hiddenCols.length);
324 String[] files = getPdbFile();
325 // check to see if we are still waiting for Jmol files
326 long starttime = System.currentTimeMillis();
327 boolean waiting = true;
331 for (String file : files)
335 // HACK - in Jalview 2.8 this call may not be threadsafe so we catch
336 // every possible exception
337 StructureMapping[] sm = ssm.getMapping(file);
338 if (sm == null || sm.length == 0)
342 } catch (Exception x)
350 // we wait around for a reasonable time before we give up
352 && System.currentTimeMillis() < (10000 + 1000 * files.length + starttime));
356 .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(MessageManager.getString("error.implementation_error_jmol_getting_data"));
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 if (molsel.length() > 1)
547 selcom[pdbfnum] = molsel.toString();
548 selectioncom.append("((");
549 selectioncom.append(selcom[pdbfnum].substring(1,
550 selcom[pdbfnum].length() - 1));
551 selectioncom.append(" )& ");
552 selectioncom.append(pdbfnum + 1);
553 selectioncom.append(".1)");
554 if (pdbfnum < files.length - 1)
556 selectioncom.append("|");
561 selcom[pdbfnum] = null;
565 for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
567 if (pdbfnum == refStructure || selcom[pdbfnum] == null
568 || 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 || !isLoadingFinished())
644 String[] files = getPdbFile();
646 SequenceRenderer sr = getSequenceRenderer(alignmentv);
648 FeatureRenderer fr = null;
651 fr = getFeatureRenderer(alignmentv);
653 AlignmentI alignment = alignmentv.getAlignment();
655 for (jalview.structure.StructureMappingcommandSet cpdbbyseq : JmolCommands
656 .getColourBySequenceCommand(ssm, files, sequence, sr, fr,
659 for (String cbyseq : cpdbbyseq.commands)
661 evalStateCommand(cbyseq);
666 public boolean isColourBySequence()
668 return colourBySequence;
671 public void setColourBySequence(boolean colourBySequence)
673 this.colourBySequence = colourBySequence;
676 public void createImage(String file, String type, int quality)
678 System.out.println("JMOL CREATE IMAGE");
681 public String createImage(String fileName, String type,
682 Object textOrBytes, int quality)
684 System.out.println("JMOL CREATE IMAGE");
688 public String eval(String strEval)
690 // System.out.println(strEval);
691 // "# 'eval' is implemented only for the applet.";
695 // End StructureListener
696 // //////////////////////////
698 public float[][] functionXY(String functionName, int x, int y)
703 public float[][][] functionXYZ(String functionName, int nx, int ny, int nz)
705 // TODO Auto-generated method stub
709 public Color getColour(int atomIndex, int pdbResNum, String chain,
712 if (getModelNum(pdbfile) < 0)
716 // TODO: verify atomIndex is selecting correct model.
717 return new Color(viewer.getAtomArgb(atomIndex));
721 * returns the current featureRenderer that should be used to colour the
728 public abstract FeatureRenderer getFeatureRenderer(
729 AlignmentViewPanel alignment);
732 * instruct the Jalview binding to update the pdbentries vector if necessary
733 * prior to matching the jmol view's contents to the list of structure files
734 * Jalview knows about.
736 public abstract void refreshPdbEntries();
738 private int getModelNum(String modelFileName)
740 String[] mfn = getPdbFile();
745 for (int i = 0; i < mfn.length; i++)
747 if (mfn[i].equalsIgnoreCase(modelFileName))
756 * map between index of model filename returned from getPdbFile and the first
757 * index of models from this file in the viewer. Note - this is not trimmed -
758 * use getPdbFile to get number of unique models.
760 private int _modelFileNameMap[];
762 // ////////////////////////////////
763 // /StructureListener
764 public synchronized String[] getPdbFile()
768 return new String[0];
770 if (modelFileNames == null)
773 String mset[] = new String[viewer.getModelCount()];
774 _modelFileNameMap = new int[mset.length];
776 String m = viewer.getModelFileName(0);
781 mset[0] = new File(m).getAbsolutePath();
782 } catch (AccessControlException x)
784 // usually not allowed to do this in applet, so keep raw handle
786 // System.err.println("jmolBinding: Using local file string from Jmol: "+m);
789 for (int i = 1; i < mset.length; i++)
791 m = viewer.getModelFileName(i);
796 mset[j] = new File(m).getAbsolutePath();
797 } catch (AccessControlException x)
799 // usually not allowed to do this in applet, so keep raw handle
801 // System.err.println("jmolBinding: Using local file string from Jmol: "+m);
804 _modelFileNameMap[j] = i; // record the model index for the filename
805 // skip any additional models in the same file (NMR structures)
806 if ((mset[j] == null ? mset[j] != mset[j - 1]
807 : (mset[j - 1] == null || !mset[j].equals(mset[j - 1]))))
812 modelFileNames = new String[j];
813 System.arraycopy(mset, 0, modelFileNames, 0, j);
815 return modelFileNames;
819 * map from string to applet
821 public Map getRegistryInfo()
823 // TODO Auto-generated method stub
828 * returns the current sequenceRenderer that should be used to colour the
835 public abstract SequenceRenderer getSequenceRenderer(
836 AlignmentViewPanel alignment);
838 // ///////////////////////////////
839 // JmolStatusListener
841 public void handlePopupMenu(int x, int y)
843 jmolpopup.show(x, y);
847 * Highlight the specified atoms in the structure.
852 public void highlightAtoms(List<AtomSpec> atoms)
854 if (modelFileNames == null)
859 for (AtomSpec atom : atoms)
861 String pdbfile = atom.getPdbId();
862 int pdbResNum = atom.getPdbResNum();
863 String chain = atom.getChain();
865 // look up file model number for this pdbfile
868 // may need to adjust for URLencoding here - we don't worry about that
870 while (mdlNum < modelFileNames.length
871 && !pdbfile.equals(modelFileNames[mdlNum]))
873 // System.out.println("nomatch:"+pdbfile+"\nmodelfn:"+fn);
876 if (mdlNum == modelFileNames.length)
882 // if (!pdbfile.equals(pdbentry.getFile()))
884 if (resetLastRes.length() > 0)
886 viewer.evalStringQuiet(resetLastRes.toString());
890 eval.append("select " + pdbResNum); // +modelNum
892 resetLastRes.setLength(0);
893 resetLastRes.append("select " + pdbResNum); // +modelNum
896 resetLastRes.append(":");
897 if (!chain.equals(" "))
900 resetLastRes.append(chain);
903 eval.append(" /" + (mdlNum + 1));
904 resetLastRes.append("/" + (mdlNum + 1));
906 eval.append(";wireframe 100;" + eval.toString() + " and not hetero;");
908 resetLastRes.append(";wireframe 0;" + resetLastRes.toString()
909 + " and not hetero; spacefill 0;");
911 eval.append("spacefill 200;select none");
913 viewer.evalStringQuiet(eval.toString());
919 boolean debug = true;
921 private void jmolHistory(boolean enable)
923 viewer.evalStringQuiet("History " + ((debug || enable) ? "on" : "off"));
926 public void loadInline(String string)
930 // viewer.loadInline(strModel, isAppend);
932 // construct fake fullPathName and fileName so we can identify the file
934 // Then, construct pass a reader for the string to Jmol.
935 // ((org.jmol.Viewer.Viewer) viewer).loadModelFromFile(fullPathName,
936 // fileName, null, reader, false, null, null, 0);
937 viewer.openStringInline(string);
940 public void mouseOverStructure(int atomIndex, String strInfo)
943 int alocsep = strInfo.indexOf("^");
944 int mdlSep = strInfo.indexOf("/");
945 int chainSeparator = strInfo.indexOf(":"), chainSeparator1 = -1;
947 if (chainSeparator == -1)
949 chainSeparator = strInfo.indexOf(".");
950 if (mdlSep > -1 && mdlSep < chainSeparator)
952 chainSeparator1 = chainSeparator;
953 chainSeparator = mdlSep;
956 // handle insertion codes
959 pdbResNum = Integer.parseInt(strInfo.substring(
960 strInfo.indexOf("]") + 1, alocsep));
965 pdbResNum = Integer.parseInt(strInfo.substring(
966 strInfo.indexOf("]") + 1, chainSeparator));
970 if (strInfo.indexOf(":") > -1)
972 chainId = strInfo.substring(strInfo.indexOf(":") + 1,
973 strInfo.indexOf("."));
980 String pdbfilename = modelFileNames[frameNo]; // default is first or current
984 if (chainSeparator1 == -1)
986 chainSeparator1 = strInfo.indexOf(".", mdlSep);
988 String mdlId = (chainSeparator1 > -1) ? strInfo.substring(mdlSep + 1,
989 chainSeparator1) : strInfo.substring(mdlSep + 1);
992 // recover PDB filename for the model hovered over.
993 int _mp = _modelFileNameMap.length - 1, mnumber = new Integer(mdlId)
995 while (mnumber < _modelFileNameMap[_mp])
999 pdbfilename = modelFileNames[_mp];
1000 if (pdbfilename == null)
1002 pdbfilename = new File(viewer.getModelFileName(mnumber))
1006 } catch (Exception e)
1011 if (lastMessage == null || !lastMessage.equals(strInfo))
1013 ssm.mouseOverStructure(pdbResNum, chainId, pdbfilename);
1016 lastMessage = strInfo;
1019 public void notifyAtomHovered(int atomIndex, String strInfo, String data)
1023 System.err.println("Ignoring additional hover info: " + data
1024 + " (other info: '" + strInfo + "' pos " + atomIndex + ")");
1026 mouseOverStructure(atomIndex, strInfo);
1030 * { if (history != null && strStatus != null &&
1031 * !strStatus.equals("Script completed")) { history.append("\n" + strStatus);
1035 public void notifyAtomPicked(int atomIndex, String strInfo, String strData)
1038 * this implements the toggle label behaviour copied from the original
1039 * structure viewer, MCView
1041 if (strData != null)
1043 System.err.println("Ignoring additional pick data string " + strData);
1045 int chainSeparator = strInfo.indexOf(":");
1047 if (chainSeparator == -1)
1049 chainSeparator = strInfo.indexOf(".");
1052 String picked = strInfo.substring(strInfo.indexOf("]") + 1,
1054 String mdlString = "";
1055 if ((p = strInfo.indexOf(":")) > -1)
1057 picked += strInfo.substring(p + 1, strInfo.indexOf("."));
1060 if ((p = strInfo.indexOf("/")) > -1)
1062 mdlString += strInfo.substring(p, strInfo.indexOf(" #"));
1064 picked = "((" + picked + ".CA" + mdlString + ")|(" + picked + ".P"
1068 if (!atomsPicked.contains(picked))
1070 viewer.evalStringQuiet("select " + picked + ";label %n %r:%c");
1071 atomsPicked.addElement(picked);
1075 viewer.evalString("select " + picked + ";label off");
1076 atomsPicked.removeElement(picked);
1079 // TODO: in application this happens
1081 // if (scriptWindow != null)
1083 // scriptWindow.sendConsoleMessage(strInfo);
1084 // scriptWindow.sendConsoleMessage("\n");
1090 public void notifyCallback(EnumCallback type, Object[] data)
1097 notifyFileLoaded((String) data[1], (String) data[2],
1098 (String) data[3], (String) data[4],
1099 ((Integer) data[5]).intValue());
1103 notifyAtomPicked(((Integer) data[2]).intValue(), (String) data[1],
1105 // also highlight in alignment
1107 notifyAtomHovered(((Integer) data[2]).intValue(), (String) data[1],
1111 notifyScriptTermination((String) data[2],
1112 ((Integer) data[3]).intValue());
1115 sendConsoleEcho((String) data[1]);
1118 sendConsoleMessage((data == null) ? ((String) null)
1119 : (String) data[1]);
1122 // System.err.println("Ignoring error callback.");
1132 System.err.println("Unhandled callback " + type + " "
1133 + data[1].toString());
1136 } catch (Exception e)
1138 System.err.println("Squashed Jmol callback handler error:");
1139 e.printStackTrace();
1144 public boolean notifyEnabled(EnumCallback callbackPick)
1146 switch (callbackPick)
1166 // incremented every time a load notification is successfully handled -
1167 // lightweight mechanism for other threads to detect when they can start
1168 // referrring to new structures.
1169 private long loadNotifiesHandled = 0;
1171 public long getLoadNotifiesHandled()
1173 return loadNotifiesHandled;
1176 public void notifyFileLoaded(String fullPathName, String fileName2,
1177 String modelName, String errorMsg, int modelParts)
1179 if (errorMsg != null)
1181 fileLoadingError = errorMsg;
1185 // TODO: deal sensibly with models loaded inLine:
1186 // modelName will be null, as will fullPathName.
1188 // the rest of this routine ignores the arguments, and simply interrogates
1189 // the Jmol view to find out what structures it contains, and adds them to
1190 // the structure selection manager.
1191 fileLoadingError = null;
1192 String[] oldmodels = modelFileNames;
1193 modelFileNames = null;
1194 chainNames = new Vector();
1195 chainFile = new Hashtable();
1196 boolean notifyLoaded = false;
1197 String[] modelfilenames = getPdbFile();
1198 // first check if we've lost any structures
1199 if (oldmodels != null && oldmodels.length > 0)
1202 for (int i = 0; i < oldmodels.length; i++)
1204 for (int n = 0; n < modelfilenames.length; n++)
1206 if (modelfilenames[n] == oldmodels[i])
1208 oldmodels[i] = null;
1212 if (oldmodels[i] != null)
1219 String[] oldmfn = new String[oldm];
1221 for (int i = 0; i < oldmodels.length; i++)
1223 if (oldmodels[i] != null)
1225 oldmfn[oldm++] = oldmodels[i];
1228 // deregister the Jmol instance for these structures - we'll add
1229 // ourselves again at the end for the current structure set.
1230 ssm.removeStructureViewerListener(this, oldmfn);
1233 refreshPdbEntries();
1234 for (int modelnum = 0; modelnum < modelfilenames.length; modelnum++)
1236 String fileName = modelfilenames[modelnum];
1237 boolean foundEntry = false;
1238 MCview.PDBfile pdb = null;
1239 String pdbfile = null, pdbfhash = null;
1240 // model was probably loaded inline - so check the pdb file hashcode
1243 // calculate essential attributes for the pdb data imported inline.
1244 // prolly need to resolve modelnumber properly - for now just use our
1246 pdbfile = viewer.getData("" + (1 + _modelFileNameMap[modelnum])
1248 pdbfhash = "" + pdbfile.hashCode();
1250 if (pdbentry != null)
1252 // search pdbentries and sequences to find correct pdbentry for this
1254 for (int pe = 0; pe < pdbentry.length; pe++)
1256 boolean matches = false;
1257 if (fileName == null)
1260 // see JAL-623 - need method of matching pasted data up
1262 pdb = ssm.setMapping(sequence[pe], chains[pe], pdbfile,
1263 AppletFormatAdapter.PASTE);
1264 pdbentry[modelnum].setFile("INLINE" + pdb.id);
1272 if (matches = (fl = new File(pdbentry[pe].getFile()))
1273 .equals(new File(fileName)))
1276 // TODO: Jmol can in principle retrieve from CLASSLOADER but
1279 // to be tested. See mantis bug
1280 // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
1281 String protocol = AppletFormatAdapter.URL;
1286 protocol = AppletFormatAdapter.FILE;
1288 } catch (Exception e)
1293 // Explicitly map to the filename used by Jmol ;
1294 pdb = ssm.setMapping(sequence[pe], chains[pe], fileName,
1296 // pdbentry[pe].getFile(), protocol);
1302 // add an entry for every chain in the model
1303 for (int i = 0; i < pdb.chains.size(); i++)
1305 String chid = new String(pdb.id + ":"
1306 + pdb.chains.elementAt(i).id);
1307 chainFile.put(chid, fileName);
1308 chainNames.addElement(chid);
1310 notifyLoaded = true;
1314 if (!foundEntry && associateNewStructs)
1316 // this is a foreign pdb file that jalview doesn't know about - add
1317 // it to the dataset and try to find a home - either on a matching
1318 // sequence or as a new sequence.
1319 String pdbcontent = viewer.getData("/" + (modelnum + 1) + ".1",
1321 // parse pdb file into a chain, etc.
1322 // locate best match for pdb in associated views and add mapping to
1324 // if properly registered then
1325 notifyLoaded = true;
1330 // so finally, update the jmol bits and pieces
1331 if (jmolpopup != null)
1333 // potential for deadlock here:
1334 // jmolpopup.updateComputedMenus();
1336 if (!isLoadingFromArchive())
1338 viewer.evalStringQuiet("model 0; select backbone;restrict;cartoon;wireframe off;spacefill off");
1340 // register ourselves as a listener and notify the gui that it needs to
1342 ssm.addStructureViewerListener(this);
1345 FeatureRenderer fr = getFeatureRenderer(null);
1351 loadNotifiesHandled++;
1353 setLoadingFromArchive(false);
1356 public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)
1358 notifyAtomPicked(iatom, strMeasure, null);
1361 public abstract void notifyScriptTermination(String strStatus,
1365 * display a message echoed from the jmol viewer
1369 public abstract void sendConsoleEcho(String strEcho); /*
1370 * { showConsole(true);
1372 * history.append("\n" +
1376 // /End JmolStatusListener
1377 // /////////////////////////////
1381 * status message - usually the response received after a script
1384 public abstract void sendConsoleMessage(String strStatus);
1386 public void setCallbackFunction(String callbackType,
1387 String callbackFunction)
1389 System.err.println("Ignoring set-callback request to associate "
1390 + callbackType + " with function " + callbackFunction);
1394 public void setJalviewColourScheme(ColourSchemeI cs)
1396 colourBySequence = false;
1407 // TODO: Switch between nucleotide or aa selection expressions
1408 Enumeration en = ResidueProperties.aa3Hash.keys();
1409 StringBuffer command = new StringBuffer("select *;color white;");
1410 while (en.hasMoreElements())
1412 res = en.nextElement().toString();
1413 index = ((Integer) ResidueProperties.aa3Hash.get(res)).intValue();
1419 col = cs.findColour(ResidueProperties.aa[index].charAt(0));
1421 command.append("select " + res + ";color[" + col.getRed() + ","
1422 + col.getGreen() + "," + col.getBlue() + "];");
1425 evalStateCommand(command.toString());
1429 public void showHelp()
1431 showUrl("http://jmol.sourceforge.net/docs/JmolUserGuide/", "jmolHelp");
1435 * open the URL somehow
1439 public abstract void showUrl(String url, String target);
1442 * called when the binding thinks the UI needs to be refreshed after a Jmol
1443 * state change. this could be because structures were loaded, or because an
1444 * error has occured.
1446 public abstract void refreshGUI();
1449 * called to show or hide the associated console window container.
1453 public abstract void showConsole(boolean show);
1456 * @param renderPanel
1458 * - when true will initialise jmol's file IO system (should be false
1459 * in applet context)
1461 * @param documentBase
1463 * @param commandOptions
1465 public void allocateViewer(Container renderPanel, boolean jmolfileio,
1466 String htmlName, URL documentBase, URL codeBase,
1467 String commandOptions)
1469 allocateViewer(renderPanel, jmolfileio, htmlName, documentBase,
1470 codeBase, commandOptions, null, null);
1475 * @param renderPanel
1477 * - when true will initialise jmol's file IO system (should be false
1478 * in applet context)
1480 * @param documentBase
1482 * @param commandOptions
1483 * @param consolePanel
1484 * - panel to contain Jmol console
1485 * @param buttonsToShow
1486 * - buttons to show on the console, in ordr
1488 public void allocateViewer(Container renderPanel, boolean jmolfileio,
1489 String htmlName, URL documentBase, URL codeBase,
1490 String commandOptions, final Container consolePanel,
1491 String buttonsToShow)
1493 if (commandOptions == null)
1495 commandOptions = "";
1497 viewer = JmolViewer.allocateViewer(renderPanel,
1498 (jmolfileio ? new SmarterJmolAdapter() : null), htmlName
1499 + ((Object) this).toString(), documentBase, codeBase,
1500 commandOptions, this);
1502 console = createJmolConsole(viewer, consolePanel, buttonsToShow);
1503 if (consolePanel != null)
1505 consolePanel.addComponentListener(this);
1511 protected abstract JmolAppConsoleInterface createJmolConsole(
1512 JmolViewer viewer2, Container consolePanel, String buttonsToShow);
1514 protected org.jmol.api.JmolAppConsoleInterface console = null;
1516 public void componentResized(ComponentEvent e)
1521 public void componentMoved(ComponentEvent e)
1526 public void componentShown(ComponentEvent e)
1531 public void componentHidden(ComponentEvent e)
1536 public void setBackgroundColour(java.awt.Color col)
1539 viewer.evalStringQuiet("background [" + col.getRed() + ","
1540 + col.getGreen() + "," + col.getBlue() + "];");
1545 * add structures and any known sequence associations
1547 * @returns the pdb entries added to the current set.
1549 public synchronized PDBEntry[] addSequenceAndChain(PDBEntry[] pdbe,
1550 SequenceI[][] seq, String[][] chns)
1553 Vector v = new Vector();
1554 Vector rtn = new Vector();
1555 for (int i = 0; i < pdbentry.length; i++)
1557 v.addElement(pdbentry[i]);
1559 for (int i = 0; i < pdbe.length; i++)
1561 int r = v.indexOf(pdbe[i]);
1562 if (r == -1 || r >= pdbentry.length)
1564 rtn.addElement(new int[]
1566 v.addElement(pdbe[i]);
1570 // just make sure the sequence/chain entries are all up to date
1571 addSequenceAndChain(r, seq[i], chns[i]);
1574 pdbe = new PDBEntry[v.size()];
1579 // expand the tied seuqence[] and string[] arrays
1580 SequenceI[][] sqs = new SequenceI[pdbentry.length][];
1581 String[][] sch = new String[pdbentry.length][];
1582 System.arraycopy(sequence, 0, sqs, 0, sequence.length);
1583 System.arraycopy(chains, 0, sch, 0, this.chains.length);
1586 pdbe = new PDBEntry[rtn.size()];
1587 for (int r = 0; r < pdbe.length; r++)
1589 int[] stri = ((int[]) rtn.elementAt(r));
1590 // record the pdb file as a new addition
1591 pdbe[r] = pdbentry[stri[0]];
1592 // and add the new sequence/chain entries
1593 addSequenceAndChain(stri[0], seq[stri[1]], chns[stri[1]]);
1603 public void addSequence(int pe, SequenceI[] seq)
1605 // add sequences to the pe'th pdbentry's seuqence set.
1606 addSequenceAndChain(pe, seq, null);
1609 private void addSequenceAndChain(int pe, SequenceI[] seq, String[] tchain)
1611 if (pe < 0 || pe >= pdbentry.length)
1613 throw new Error(MessageManager.formatMessage("error.implementation_error_no_pdbentry_from_index", new String[]{Integer.valueOf(pe).toString()}));
1615 final String nullChain = "TheNullChain";
1616 Vector s = new Vector();
1617 Vector c = new Vector();
1620 chains = new String[pdbentry.length][];
1622 if (sequence[pe] != null)
1624 for (int i = 0; i < sequence[pe].length; i++)
1626 s.addElement(sequence[pe][i]);
1627 if (chains[pe] != null)
1629 if (i < chains[pe].length)
1631 c.addElement(chains[pe][i]);
1635 c.addElement(nullChain);
1640 if (tchain != null && tchain.length > 0)
1642 c.addElement(nullChain);
1647 for (int i = 0; i < seq.length; i++)
1649 if (!s.contains(seq[i]))
1651 s.addElement(seq[i]);
1652 if (tchain != null && i < tchain.length)
1654 c.addElement(tchain[i] == null ? nullChain : tchain[i]);
1658 SequenceI[] tmp = new SequenceI[s.size()];
1663 String[] tch = new String[c.size()];
1665 for (int i = 0; i < tch.length; i++)
1667 if (tch[i] == nullChain)
1683 * @return text report of alignment between pdbfile and any associated
1684 * alignment sequences
1686 public String printMapping(String pdbfile)
1688 return ssm.printMapping(pdbfile);
1692 public void resizeInnerPanel(String data)
1694 // Jalview doesn't honour resize panel requests