2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.0b1)
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 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/>.
17 * The Jalview Authors are detailed in the 'AUTHORS' file.
19 package jalview.ext.jmol;
21 import jalview.api.AlignmentViewPanel;
22 import jalview.api.FeatureRenderer;
23 import jalview.api.SequenceRenderer;
24 import jalview.api.SequenceStructureBinding;
25 import jalview.api.StructureSelectionManagerProvider;
26 import jalview.datamodel.AlignmentI;
27 import jalview.datamodel.ColumnSelection;
28 import jalview.datamodel.PDBEntry;
29 import jalview.datamodel.SequenceI;
30 import jalview.io.AppletFormatAdapter;
31 import jalview.schemes.ColourSchemeI;
32 import jalview.schemes.ResidueProperties;
33 import jalview.structure.StructureListener;
34 import jalview.structure.StructureMapping;
35 import jalview.structure.StructureSelectionManager;
37 import java.awt.Color;
38 import java.awt.Container;
39 import java.awt.event.ComponentEvent;
40 import java.awt.event.ComponentListener;
43 import java.security.AccessControlException;
44 import java.util.Enumeration;
45 import java.util.Hashtable;
47 import java.util.Vector;
49 import org.jmol.adapter.smarter.SmarterJmolAdapter;
50 import org.jmol.api.JmolAppConsoleInterface;
51 import org.jmol.api.JmolSelectionListener;
52 import org.jmol.api.JmolStatusListener;
53 import org.jmol.api.JmolViewer;
54 import org.jmol.constant.EnumCallback;
55 import org.jmol.popup.JmolPopup;
57 public abstract class JalviewJmolBinding implements StructureListener,
58 JmolStatusListener, SequenceStructureBinding,
59 JmolSelectionListener, ComponentListener,
60 StructureSelectionManagerProvider
64 * set if Jmol state is being restored from some source - instructs binding
65 * not to apply default display style when structure set is updated for first
68 private boolean loadingFromArchive = false;
71 * second flag to indicate if the jmol viewer should ignore sequence colouring
72 * events from the structure manager because the GUI is still setting up
74 private boolean loadingFinished = true;
77 * state flag used to check if the Jmol viewer's paint method can be called
79 private boolean finishedInit = false;
81 public boolean isFinishedInit()
86 public void setFinishedInit(boolean finishedInit)
88 this.finishedInit = finishedInit;
91 boolean allChainsSelected = false;
94 * when true, try to search the associated datamodel for sequences that are
95 * associated with any unknown structures in the Jmol view.
97 private boolean associateNewStructs = false;
99 Vector atomsPicked = new Vector();
101 public Vector chainNames;
106 * array of target chains for seuqences - tied to pdbentry and sequence[]
108 protected String[][] chains;
110 boolean colourBySequence = true;
112 StringBuffer eval = new StringBuffer();
114 public String fileLoadingError;
117 * the default or current model displayed if the model cannot be identified
118 * from the selection message
122 protected JmolPopup jmolpopup;
128 boolean loadedInline;
131 * current set of model filenames loaded in the Jmol instance
133 String[] modelFileNames = null;
135 public PDBEntry[] pdbentry;
138 * datasource protocol for access to PDBEntrylatest
140 String protocol = null;
142 StringBuffer resetLastRes = new StringBuffer();
145 * sequences mapped to each pdbentry
147 public SequenceI[][] sequence;
149 public StructureSelectionManager ssm;
151 public JmolViewer viewer;
153 public JalviewJmolBinding(StructureSelectionManager ssm,
154 PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String[][] chains,
158 this.sequence = sequenceIs;
159 this.chains = chains;
160 this.pdbentry = pdbentry;
161 this.protocol = protocol;
164 this.chains = new String[pdbentry.length][];
167 * viewer = JmolViewer.allocateViewer(renderPanel, new SmarterJmolAdapter(),
168 * "jalviewJmol", ap.av.applet .getDocumentBase(),
169 * ap.av.applet.getCodeBase(), "", this);
171 * jmolpopup = JmolPopup.newJmolPopup(viewer, true, "Jmol", true);
175 public JalviewJmolBinding(StructureSelectionManager ssm,
180 viewer.setJmolStatusListener(this);
181 viewer.addSelectionListener(this);
185 * construct a title string for the viewer window based on the data jalview
190 public String getViewerTitle()
192 if (sequence == null || pdbentry == null || sequence.length < 1
193 || pdbentry.length < 1 || sequence[0].length < 1)
195 return ("Jalview Jmol Window");
197 // TODO: give a more informative title when multiple structures are
199 StringBuffer title = new StringBuffer(sequence[0][0].getName() + ":"
200 + pdbentry[0].getId());
202 if (pdbentry[0].getProperty() != null)
204 if (pdbentry[0].getProperty().get("method") != null)
206 title.append(" Method: ");
207 title.append(pdbentry[0].getProperty().get("method"));
209 if (pdbentry[0].getProperty().get("chains") != null)
211 title.append(" Chain:");
212 title.append(pdbentry[0].getProperty().get("chains"));
215 return title.toString();
219 * prepare the view for a given set of models/chains. chainList contains
220 * strings of the form 'pdbfilename:Chaincode'
223 * list of chains to make visible
225 public void centerViewer(Vector chainList)
227 StringBuffer cmd = new StringBuffer();
230 for (int i = 0, iSize = chainList.size(); i < iSize; i++)
233 lbl = (String) chainList.elementAt(i);
237 mlength = lbl.indexOf(":", p);
238 } while (p < mlength && mlength < (lbl.length() - 2));
239 // TODO: lookup each pdb id and recover proper model number for it.
240 cmd.append(":" + lbl.substring(mlength + 1) + " /"
241 + (1 + getModelNum((String) chainFile.get(lbl))) + " or ");
243 if (cmd.length() > 0)
244 cmd.setLength(cmd.length() - 4);
245 evalStateCommand("select *;restrict " + cmd + ";cartoon;center " + cmd);
248 public void closeViewer()
250 viewer.setModeMouse(org.jmol.viewer.JmolConstants.MOUSE_NONE);
251 // remove listeners for all structures in viewer
252 ssm.removeStructureViewerListener(this, this.getPdbFile());
253 // and shut down jmol
254 viewer.evalStringQuiet("zap");
255 viewer.setJmolStatusListener(null);
258 releaseUIResources();
262 * called by JalviewJmolbinding after closeViewer is called - release any
263 * resources and references so they can be garbage collected.
265 protected abstract void releaseUIResources();
267 public void colourByChain()
269 colourBySequence = false;
270 // TODO: colour by chain should colour each chain distinctly across all
272 // TODO: http://issues.jalview.org/browse/JAL-628
273 evalStateCommand("select *;color chain");
276 public void colourByCharge()
278 colourBySequence = false;
279 evalStateCommand("select *;color white;select ASP,GLU;color red;"
280 + "select LYS,ARG;color blue;select CYS;color yellow");
284 * superpose the structures associated with sequences in the alignment
285 * according to their corresponding positions.
287 public void superposeStructures(AlignmentI alignment)
289 superposeStructures(alignment, -1, null);
293 * superpose the structures associated with sequences in the alignment
294 * according to their corresponding positions. ded)
296 * @param refStructure
297 * - select which pdb file to use as reference (default is -1 - the
298 * first structure in the alignment)
300 public void superposeStructures(AlignmentI alignment, int refStructure)
302 superposeStructures(alignment, refStructure, null);
306 * superpose the structures associated with sequences in the alignment
307 * according to their corresponding positions. ded)
309 * @param refStructure
310 * - select which pdb file to use as reference (default is -1 - the
311 * first structure in the alignment)
315 public void superposeStructures(AlignmentI alignment, int refStructure,
316 ColumnSelection hiddenCols)
318 superposeStructures(new AlignmentI[]
319 { alignment }, new int[]
320 { refStructure }, new ColumnSelection[]
324 public void superposeStructures(AlignmentI[] _alignment,
325 int[] _refStructure, ColumnSelection[] _hiddenCols)
327 assert (_alignment.length == _refStructure.length && _alignment.length != _hiddenCols.length);
329 String[] files = getPdbFile();
330 // check to see if we are still waiting for Jmol files
331 long starttime=System.currentTimeMillis();
332 boolean waiting=true;
335 for (String file:files)
338 // HACK - in Jalview 2.8 this call may not be threadsafe so we catch
339 // every possible exception
340 StructureMapping[] sm = ssm.getMapping(file);
341 if (sm == null || sm.length == 0)
345 } catch (Exception x)
353 // we wait around for a reasonable time before we give up
354 } while (waiting && System.currentTimeMillis()<(10000+1000*files.length+starttime));
357 System.err.println("RUNTIME PROBLEM: Jmol seems to be taking a long time to process all the structures.");
360 StringBuffer selectioncom = new StringBuffer();
361 // In principle - nSeconds specifies the speed of animation for each
362 // superposition - but is seems to behave weirdly, so we don't specify it.
363 String nSeconds = " ";
364 if (files.length > 10)
366 nSeconds = " 0.00001 ";
370 nSeconds = " " + (2.0 / files.length) + " ";
371 // if (nSeconds).substring(0,5)+" ";
373 // see JAL-1345 - should really automatically turn off the animation for
374 // large numbers of structures, but Jmol doesn't seem to allow that.
376 // union of all aligned positions are collected together.
377 for (int a = 0; a < _alignment.length; a++)
379 int refStructure = _refStructure[a];
380 AlignmentI alignment = _alignment[a];
381 ColumnSelection hiddenCols = _hiddenCols[a];
383 && selectioncom.length() > 0
384 && !selectioncom.substring(selectioncom.length() - 1).equals(
387 selectioncom.append("|");
389 // process this alignment
390 if (refStructure >= files.length)
392 System.err.println("Invalid reference structure value "
396 if (refStructure < -1)
400 StringBuffer command = new StringBuffer();
402 boolean matched[] = new boolean[alignment.getWidth()];
403 for (int m = 0; m < matched.length; m++)
406 matched[m] = (hiddenCols != null) ? hiddenCols.isVisible(m) : true;
409 int commonrpositions[][] = new int[files.length][alignment.getWidth()];
410 String isel[] = new String[files.length];
411 // reference structure - all others are superposed in it
412 String[] targetC = new String[files.length];
413 String[] chainNames = new String[files.length];
414 for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
416 StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
417 // RACE CONDITION - getMapping only returns Jmol loaded filenames once
418 // Jmol callback has completed.
419 if (mapping == null || mapping.length < 1)
421 throw new Error("Implementation error - Jmol seems to be still working on getting its data - report at http://issues.jalview.org/browse/JAL-1016");
424 for (int s = 0; s < sequence[pdbfnum].length; s++)
426 for (int sp, m = 0; m < mapping.length; m++)
428 if (mapping[m].getSequence() == sequence[pdbfnum][s]
429 && (sp = alignment.findIndex(sequence[pdbfnum][s])) > -1)
431 if (refStructure == -1)
433 refStructure = pdbfnum;
435 SequenceI asp = alignment.getSequenceAt(sp);
436 for (int r = 0; r < matched.length; r++)
442 matched[r] = false; // assume this is not a good site
443 if (r >= asp.getLength())
448 if (jalview.util.Comparison.isGap(asp.getCharAt(r)))
450 // no mapping to gaps in sequence
453 int t = asp.findPosition(r); // sequence position
454 int apos = mapping[m].getAtomNum(t);
455 int pos = mapping[m].getPDBResNum(t);
457 if (pos < 1 || pos == lastPos)
459 // can't align unmapped sequence
462 matched[r] = true; // this is a good ite
464 // just record this residue position
465 commonrpositions[pdbfnum][r] = pos;
467 // create model selection suffix
468 isel[pdbfnum] = "/" + (pdbfnum + 1) + ".1";
469 if (mapping[m].getChain() == null
470 || mapping[m].getChain().trim().length() == 0)
472 targetC[pdbfnum] = "";
476 targetC[pdbfnum] = ":" + mapping[m].getChain();
478 chainNames[pdbfnum] = mapping[m].getPdbId()
480 // move on to next pdb file
481 s = sequence[pdbfnum].length;
488 // TODO: consider bailing if nmatched less than 4 because superposition
491 // TODO: refactor superposable position search (above) from jmol selection
492 // construction (below)
494 String[] selcom = new String[files.length];
496 // generate select statements to select regions to superimpose structures
498 for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
500 String chainCd = targetC[pdbfnum];
503 StringBuffer molsel = new StringBuffer();
505 for (int r = 0; r < matched.length; r++)
513 if (lpos != commonrpositions[pdbfnum][r] - 1)
519 molsel.append(chainCd);
520 // molsel.append("} {");
526 // continuous run - and lpos >-1
529 // at the beginning, so add dash
535 lpos = commonrpositions[pdbfnum][r];
536 // molsel.append(lpos);
539 // add final selection phrase
543 molsel.append(chainCd);
546 selcom[pdbfnum] = molsel.toString();
547 selectioncom.append("((");
548 selectioncom.append(selcom[pdbfnum].substring(1,
549 selcom[pdbfnum].length() - 1));
550 selectioncom.append(" )& ");
551 selectioncom.append(pdbfnum + 1);
552 selectioncom.append(".1)");
553 if (pdbfnum < files.length - 1)
555 selectioncom.append("|");
559 for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
561 if (pdbfnum == refStructure)
565 command.append("echo ");
566 command.append("\"Superposing (");
567 command.append(chainNames[pdbfnum]);
568 command.append(") against reference (");
569 command.append(chainNames[refStructure]);
570 command.append(")\";\ncompare "+nSeconds);
572 command.append(1 + pdbfnum);
573 command.append(".1} {");
574 command.append(1 + refStructure);
575 command.append(".1} SUBSET {*.CA | *.P} ATOMS ");
577 // form the matched pair strings
579 for (int s = 0; s < 2; s++)
581 command.append(selcom[(s == 0 ? pdbfnum : refStructure)]);
583 command.append(" ROTATE TRANSLATE;\n");
585 System.out.println("Select regions:\n" + selectioncom.toString());
586 evalStateCommand("select *; cartoons off; backbone; select ("
587 + selectioncom.toString() + "); cartoons; ");
588 // selcom.append("; ribbons; ");
589 System.out.println("Superimpose command(s):\n" + command.toString());
591 evalStateCommand(command.toString());
593 if (selectioncom.length() > 0)
594 {// finally, mark all regions that were superposed.
595 if (selectioncom.substring(selectioncom.length() - 1).equals("|"))
597 selectioncom.setLength(selectioncom.length() - 1);
599 System.out.println("Select regions:\n" + selectioncom.toString());
600 evalStateCommand("select *; cartoons off; backbone; select ("
601 + selectioncom.toString() + "); cartoons; ");
602 // evalStateCommand("select *; backbone; select "+selcom.toString()+"; cartoons; center "+selcom.toString());
606 public void evalStateCommand(String command)
609 if (lastCommand == null || !lastCommand.equals(command))
611 viewer.evalStringQuiet(command + "\n");
614 lastCommand = command;
618 * colour any structures associated with sequences in the given alignment
619 * using the getFeatureRenderer() and getSequenceRenderer() renderers but only
620 * if colourBySequence is enabled.
622 public void colourBySequence(boolean showFeatures,
623 jalview.api.AlignmentViewPanel alignmentv)
625 if (!colourBySequence || !loadingFinished)
631 String[] files = getPdbFile();
633 SequenceRenderer sr = getSequenceRenderer(alignmentv);
635 FeatureRenderer fr = null;
638 fr = getFeatureRenderer(alignmentv);
640 AlignmentI alignment = alignmentv.getAlignment();
642 for (jalview.structure.StructureMappingcommandSet cpdbbyseq : JmolCommands
643 .getColourBySequenceCommand(ssm, files, sequence, sr, fr,
645 for (String cbyseq : cpdbbyseq.commands)
647 evalStateCommand(cbyseq);
651 public boolean isColourBySequence()
653 return colourBySequence;
656 public void setColourBySequence(boolean colourBySequence)
658 this.colourBySequence = colourBySequence;
661 public void createImage(String file, String type, int quality)
663 System.out.println("JMOL CREATE IMAGE");
666 public String createImage(String fileName, String type,
667 Object textOrBytes, int quality)
669 System.out.println("JMOL CREATE IMAGE");
673 public String eval(String strEval)
675 // System.out.println(strEval);
676 // "# 'eval' is implemented only for the applet.";
680 // End StructureListener
681 // //////////////////////////
683 public float[][] functionXY(String functionName, int x, int y)
688 public float[][][] functionXYZ(String functionName, int nx, int ny, int nz)
690 // TODO Auto-generated method stub
694 public Color getColour(int atomIndex, int pdbResNum, String chain,
697 if (getModelNum(pdbfile) < 0)
699 // TODO: verify atomIndex is selecting correct model.
700 return new Color(viewer.getAtomArgb(atomIndex));
704 * returns the current featureRenderer that should be used to colour the
711 public abstract FeatureRenderer getFeatureRenderer(
712 AlignmentViewPanel alignment);
715 * instruct the Jalview binding to update the pdbentries vector if necessary
716 * prior to matching the jmol view's contents to the list of structure files
717 * Jalview knows about.
719 public abstract void refreshPdbEntries();
721 private int getModelNum(String modelFileName)
723 String[] mfn = getPdbFile();
728 for (int i = 0; i < mfn.length; i++)
730 if (mfn[i].equalsIgnoreCase(modelFileName))
737 * map between index of model filename returned from getPdbFile and the first
738 * index of models from this file in the viewer. Note - this is not trimmed -
739 * use getPdbFile to get number of unique models.
741 private int _modelFileNameMap[];
743 // ////////////////////////////////
744 // /StructureListener
745 public synchronized String[] getPdbFile()
749 return new String[0];
751 if (modelFileNames == null)
754 String mset[] = new String[viewer.getModelCount()];
755 _modelFileNameMap = new int[mset.length];
757 String m = viewer.getModelFileName(0);
762 mset[0] = new File(m).getAbsolutePath();
763 } catch (AccessControlException x)
765 // usually not allowed to do this in applet, so keep raw handle
767 // System.err.println("jmolBinding: Using local file string from Jmol: "+m);
770 for (int i = 1; i < mset.length; i++)
772 m = viewer.getModelFileName(i);
777 mset[j] = new File(m).getAbsolutePath();
778 } catch (AccessControlException x)
780 // usually not allowed to do this in applet, so keep raw handle
782 // System.err.println("jmolBinding: Using local file string from Jmol: "+m);
785 _modelFileNameMap[j] = i; // record the model index for the filename
786 // skip any additional models in the same file (NMR structures)
787 if ((mset[j] == null ? mset[j] != mset[j - 1]
788 : (mset[j - 1] == null || !mset[j].equals(mset[j - 1]))))
793 modelFileNames = new String[j];
794 System.arraycopy(mset, 0, modelFileNames, 0, j);
796 return modelFileNames;
800 * map from string to applet
802 public Map getRegistryInfo()
804 // TODO Auto-generated method stub
809 * returns the current sequenceRenderer that should be used to colour the
816 public abstract SequenceRenderer getSequenceRenderer(
817 AlignmentViewPanel alignment);
819 // ///////////////////////////////
820 // JmolStatusListener
822 public void handlePopupMenu(int x, int y)
824 jmolpopup.show(x, y);
828 public void highlightAtom(int atomIndex, int pdbResNum, String chain,
831 if (modelFileNames == null)
836 // look up file model number for this pdbfile
839 // may need to adjust for URLencoding here - we don't worry about that yet.
840 while (mdlNum < modelFileNames.length
841 && !pdbfile.equals(modelFileNames[mdlNum]))
843 // System.out.println("nomatch:"+pdbfile+"\nmodelfn:"+fn);
846 if (mdlNum == modelFileNames.length)
852 // if (!pdbfile.equals(pdbentry.getFile()))
854 if (resetLastRes.length() > 0)
856 viewer.evalStringQuiet(resetLastRes.toString());
860 eval.append("select " + pdbResNum); // +modelNum
862 resetLastRes.setLength(0);
863 resetLastRes.append("select " + pdbResNum); // +modelNum
866 resetLastRes.append(":");
867 if (!chain.equals(" "))
870 resetLastRes.append(chain);
873 eval.append(" /" + (mdlNum + 1));
874 resetLastRes.append("/" + (mdlNum + 1));
876 eval.append(";wireframe 100;" + eval.toString() + " and not hetero;");
878 resetLastRes.append(";wireframe 0;" + resetLastRes.toString()
879 + " and not hetero; spacefill 0;");
881 eval.append("spacefill 200;select none");
883 viewer.evalStringQuiet(eval.toString());
888 boolean debug = true;
890 private void jmolHistory(boolean enable)
892 viewer.evalStringQuiet("History " + ((debug || enable) ? "on" : "off"));
895 public void loadInline(String string)
899 // viewer.loadInline(strModel, isAppend);
901 // construct fake fullPathName and fileName so we can identify the file
903 // Then, construct pass a reader for the string to Jmol.
904 // ((org.jmol.Viewer.Viewer) viewer).loadModelFromFile(fullPathName,
905 // fileName, null, reader, false, null, null, 0);
906 viewer.openStringInline(string);
909 public void mouseOverStructure(int atomIndex, String strInfo)
912 int alocsep = strInfo.indexOf("^");
913 int mdlSep = strInfo.indexOf("/");
914 int chainSeparator = strInfo.indexOf(":"), chainSeparator1 = -1;
916 if (chainSeparator == -1)
918 chainSeparator = strInfo.indexOf(".");
919 if (mdlSep > -1 && mdlSep < chainSeparator)
921 chainSeparator1 = chainSeparator;
922 chainSeparator = mdlSep;
925 // handle insertion codes
928 pdbResNum = Integer.parseInt(strInfo.substring(
929 strInfo.indexOf("]") + 1, alocsep));
934 pdbResNum = Integer.parseInt(strInfo.substring(
935 strInfo.indexOf("]") + 1, chainSeparator));
939 if (strInfo.indexOf(":") > -1)
940 chainId = strInfo.substring(strInfo.indexOf(":") + 1,
941 strInfo.indexOf("."));
947 String pdbfilename = modelFileNames[frameNo]; // default is first or current
951 if (chainSeparator1 == -1)
953 chainSeparator1 = strInfo.indexOf(".", mdlSep);
955 String mdlId = (chainSeparator1 > -1) ? strInfo.substring(mdlSep + 1,
956 chainSeparator1) : strInfo.substring(mdlSep + 1);
959 // recover PDB filename for the model hovered over.
960 int _mp = _modelFileNameMap.length - 1, mnumber = new Integer(mdlId)
962 while (mnumber < _modelFileNameMap[_mp])
966 pdbfilename = modelFileNames[_mp];
967 if (pdbfilename == null)
969 pdbfilename = new File(viewer.getModelFileName(mnumber))
973 } catch (Exception e)
978 if (lastMessage == null || !lastMessage.equals(strInfo))
979 ssm.mouseOverStructure(pdbResNum, chainId, pdbfilename);
981 lastMessage = strInfo;
984 public void notifyAtomHovered(int atomIndex, String strInfo, String data)
988 System.err.println("Ignoring additional hover info: " + data
989 + " (other info: '" + strInfo + "' pos " + atomIndex + ")");
991 mouseOverStructure(atomIndex, strInfo);
995 * { if (history != null && strStatus != null &&
996 * !strStatus.equals("Script completed")) { history.append("\n" + strStatus);
1000 public void notifyAtomPicked(int atomIndex, String strInfo, String strData)
1003 * this implements the toggle label behaviour copied from the original
1004 * structure viewer, MCView
1006 if (strData != null)
1008 System.err.println("Ignoring additional pick data string " + strData);
1010 int chainSeparator = strInfo.indexOf(":");
1012 if (chainSeparator == -1)
1013 chainSeparator = strInfo.indexOf(".");
1015 String picked = strInfo.substring(strInfo.indexOf("]") + 1,
1017 String mdlString = "";
1018 if ((p = strInfo.indexOf(":")) > -1)
1019 picked += strInfo.substring(p + 1, strInfo.indexOf("."));
1021 if ((p = strInfo.indexOf("/")) > -1)
1023 mdlString += strInfo.substring(p, strInfo.indexOf(" #"));
1025 picked = "((" + picked + ".CA" + mdlString + ")|(" + picked + ".P"
1029 if (!atomsPicked.contains(picked))
1031 viewer.evalStringQuiet("select " + picked + ";label %n %r:%c");
1032 atomsPicked.addElement(picked);
1036 viewer.evalString("select " + picked + ";label off");
1037 atomsPicked.removeElement(picked);
1040 // TODO: in application this happens
1042 // if (scriptWindow != null)
1044 // scriptWindow.sendConsoleMessage(strInfo);
1045 // scriptWindow.sendConsoleMessage("\n");
1051 public void notifyCallback(EnumCallback type, Object[] data)
1058 notifyFileLoaded((String) data[1], (String) data[2],
1059 (String) data[3], (String) data[4],
1060 ((Integer) data[5]).intValue());
1064 notifyAtomPicked(((Integer) data[2]).intValue(), (String) data[1],
1066 // also highlight in alignment
1068 notifyAtomHovered(((Integer) data[2]).intValue(), (String) data[1],
1072 notifyScriptTermination((String) data[2],
1073 ((Integer) data[3]).intValue());
1076 sendConsoleEcho((String) data[1]);
1079 sendConsoleMessage((data == null) ? ((String) null)
1080 : (String) data[1]);
1083 // System.err.println("Ignoring error callback.");
1093 System.err.println("Unhandled callback " + type + " "
1094 + data[1].toString());
1097 } catch (Exception e)
1099 System.err.println("Squashed Jmol callback handler error:");
1100 e.printStackTrace();
1105 public boolean notifyEnabled(EnumCallback callbackPick)
1107 switch (callbackPick)
1127 // incremented every time a load notification is successfully handled -
1128 // lightweight mechanism for other threads to detect when they can start
1129 // referrring to new structures.
1130 private long loadNotifiesHandled = 0;
1132 public long getLoadNotifiesHandled()
1134 return loadNotifiesHandled;
1137 public void notifyFileLoaded(String fullPathName, String fileName2,
1138 String modelName, String errorMsg, int modelParts)
1140 if (errorMsg != null)
1142 fileLoadingError = errorMsg;
1146 // TODO: deal sensibly with models loaded inLine:
1147 // modelName will be null, as will fullPathName.
1149 // the rest of this routine ignores the arguments, and simply interrogates
1150 // the Jmol view to find out what structures it contains, and adds them to
1151 // the structure selection manager.
1152 fileLoadingError = null;
1153 String[] oldmodels = modelFileNames;
1154 modelFileNames = null;
1155 chainNames = new Vector();
1156 chainFile = new Hashtable();
1157 boolean notifyLoaded = false;
1158 String[] modelfilenames = getPdbFile();
1159 // first check if we've lost any structures
1160 if (oldmodels != null && oldmodels.length > 0)
1163 for (int i = 0; i < oldmodels.length; i++)
1165 for (int n = 0; n < modelfilenames.length; n++)
1167 if (modelfilenames[n] == oldmodels[i])
1169 oldmodels[i] = null;
1173 if (oldmodels[i] != null)
1180 String[] oldmfn = new String[oldm];
1182 for (int i = 0; i < oldmodels.length; i++)
1184 if (oldmodels[i] != null)
1186 oldmfn[oldm++] = oldmodels[i];
1189 // deregister the Jmol instance for these structures - we'll add
1190 // ourselves again at the end for the current structure set.
1191 ssm.removeStructureViewerListener(this, oldmfn);
1194 refreshPdbEntries();
1195 for (int modelnum = 0; modelnum < modelfilenames.length; modelnum++)
1197 String fileName = modelfilenames[modelnum];
1198 boolean foundEntry = false;
1199 MCview.PDBfile pdb = null;
1200 String pdbfile = null, pdbfhash = null;
1201 // model was probably loaded inline - so check the pdb file hashcode
1204 // calculate essential attributes for the pdb data imported inline.
1205 // prolly need to resolve modelnumber properly - for now just use our
1207 pdbfile = viewer.getData("" + (1 + _modelFileNameMap[modelnum])
1209 pdbfhash = "" + pdbfile.hashCode();
1211 if (pdbentry != null)
1213 // search pdbentries and sequences to find correct pdbentry for this
1215 for (int pe = 0; pe < pdbentry.length; pe++)
1217 boolean matches = false;
1218 if (fileName == null)
1221 // see JAL-623 - need method of matching pasted data up
1223 pdb = ssm.setMapping(sequence[pe], chains[pe], pdbfile,
1224 AppletFormatAdapter.PASTE);
1225 pdbentry[modelnum].setFile("INLINE" + pdb.id);
1233 if (matches = (fl = new File(pdbentry[pe].getFile()))
1234 .equals(new File(fileName)))
1237 // TODO: Jmol can in principle retrieve from CLASSLOADER but
1240 // to be tested. See mantis bug
1241 // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
1242 String protocol = AppletFormatAdapter.URL;
1247 protocol = AppletFormatAdapter.FILE;
1249 } catch (Exception e)
1254 // Explicitly map to the filename used by Jmol ;
1255 pdb = ssm.setMapping(sequence[pe], chains[pe], fileName,
1257 // pdbentry[pe].getFile(), protocol);
1263 // add an entry for every chain in the model
1264 for (int i = 0; i < pdb.chains.size(); i++)
1266 String chid = new String(pdb.id + ":"
1267 + ((MCview.PDBChain) pdb.chains.elementAt(i)).id);
1268 chainFile.put(chid, fileName);
1269 chainNames.addElement(chid);
1271 notifyLoaded = true;
1275 if (!foundEntry && associateNewStructs)
1277 // this is a foreign pdb file that jalview doesn't know about - add
1278 // it to the dataset and try to find a home - either on a matching
1279 // sequence or as a new sequence.
1280 String pdbcontent = viewer.getData("/" + (modelnum + 1) + ".1",
1282 // parse pdb file into a chain, etc.
1283 // locate best match for pdb in associated views and add mapping to
1285 // if properly registered then
1286 notifyLoaded = true;
1291 // so finally, update the jmol bits and pieces
1292 if (jmolpopup != null)
1294 // potential for deadlock here:
1295 // jmolpopup.updateComputedMenus();
1297 if (!isLoadingFromArchive())
1299 viewer.evalStringQuiet("model 0; select backbone;restrict;cartoon;wireframe off;spacefill off");
1301 // register ourselves as a listener and notify the gui that it needs to
1303 ssm.addStructureViewerListener(this);
1306 FeatureRenderer fr = getFeatureRenderer(null);
1312 loadNotifiesHandled++;
1314 setLoadingFromArchive(false);
1317 public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)
1319 notifyAtomPicked(iatom, strMeasure, null);
1322 public abstract void notifyScriptTermination(String strStatus,
1326 * display a message echoed from the jmol viewer
1330 public abstract void sendConsoleEcho(String strEcho); /*
1331 * { showConsole(true);
1333 * history.append("\n" +
1337 // /End JmolStatusListener
1338 // /////////////////////////////
1342 * status message - usually the response received after a script
1345 public abstract void sendConsoleMessage(String strStatus);
1347 public void setCallbackFunction(String callbackType,
1348 String callbackFunction)
1350 System.err.println("Ignoring set-callback request to associate "
1351 + callbackType + " with function " + callbackFunction);
1355 public void setJalviewColourScheme(ColourSchemeI cs)
1357 colourBySequence = false;
1366 // TODO: Switch between nucleotide or aa selection expressions
1367 Enumeration en = ResidueProperties.aa3Hash.keys();
1368 StringBuffer command = new StringBuffer("select *;color white;");
1369 while (en.hasMoreElements())
1371 res = en.nextElement().toString();
1372 index = ((Integer) ResidueProperties.aa3Hash.get(res)).intValue();
1376 col = cs.findColour(ResidueProperties.aa[index].charAt(0));
1378 command.append("select " + res + ";color[" + col.getRed() + ","
1379 + col.getGreen() + "," + col.getBlue() + "];");
1382 evalStateCommand(command.toString());
1386 public void showHelp()
1388 showUrl("http://jmol.sourceforge.net/docs/JmolUserGuide/", "jmolHelp");
1392 * open the URL somehow
1396 public abstract void showUrl(String url, String target);
1399 * called when the binding thinks the UI needs to be refreshed after a Jmol
1400 * state change. this could be because structures were loaded, or because an
1401 * error has occured.
1403 public abstract void refreshGUI();
1406 * called to show or hide the associated console window container.
1410 public abstract void showConsole(boolean show);
1413 * @param renderPanel
1415 * - when true will initialise jmol's file IO system (should be false
1416 * in applet context)
1418 * @param documentBase
1420 * @param commandOptions
1422 public void allocateViewer(Container renderPanel, boolean jmolfileio,
1423 String htmlName, URL documentBase, URL codeBase,
1424 String commandOptions)
1426 allocateViewer(renderPanel, jmolfileio, htmlName, documentBase,
1427 codeBase, commandOptions, null, null);
1432 * @param renderPanel
1434 * - when true will initialise jmol's file IO system (should be false
1435 * in applet context)
1437 * @param documentBase
1439 * @param commandOptions
1440 * @param consolePanel
1441 * - panel to contain Jmol console
1442 * @param buttonsToShow
1443 * - buttons to show on the console, in ordr
1445 public void allocateViewer(Container renderPanel, boolean jmolfileio,
1446 String htmlName, URL documentBase, URL codeBase,
1447 String commandOptions, final Container consolePanel,
1448 String buttonsToShow)
1450 if (commandOptions == null)
1452 commandOptions = "";
1454 viewer = JmolViewer.allocateViewer(renderPanel,
1455 (jmolfileio ? new SmarterJmolAdapter() : null), htmlName
1456 + ((Object) this).toString(), documentBase, codeBase,
1457 commandOptions, this);
1459 console = createJmolConsole(viewer, consolePanel, buttonsToShow);
1460 if (consolePanel != null)
1462 consolePanel.addComponentListener(this);
1468 protected abstract JmolAppConsoleInterface createJmolConsole(
1469 JmolViewer viewer2, Container consolePanel, String buttonsToShow);
1471 protected org.jmol.api.JmolAppConsoleInterface console = null;
1473 public void componentResized(ComponentEvent e)
1478 public void componentMoved(ComponentEvent e)
1483 public void componentShown(ComponentEvent e)
1488 public void componentHidden(ComponentEvent e)
1493 public void setLoadingFromArchive(boolean loadingFromArchive)
1495 this.loadingFromArchive = loadingFromArchive;
1500 * @return true if Jmol is still restoring state or loading is still going on (see setFinsihedLoadingFromArchive)
1502 public boolean isLoadingFromArchive()
1504 return loadingFromArchive && !loadingFinished;
1508 * modify flag which controls if sequence colouring events are honoured by the binding.
1509 * Should be true for normal operation
1510 * @param finishedLoading
1512 public void setFinishedLoadingFromArchive(boolean finishedLoading)
1514 loadingFinished = finishedLoading;
1517 public void setBackgroundColour(java.awt.Color col)
1520 viewer.evalStringQuiet("background [" + col.getRed() + ","
1521 + col.getGreen() + "," + col.getBlue() + "];");
1526 * add structures and any known sequence associations
1528 * @returns the pdb entries added to the current set.
1530 public synchronized PDBEntry[] addSequenceAndChain(PDBEntry[] pdbe,
1531 SequenceI[][] seq, String[][] chns)
1534 Vector v = new Vector();
1535 Vector rtn = new Vector();
1536 for (int i = 0; i < pdbentry.length; i++)
1538 v.addElement(pdbentry[i]);
1540 for (int i = 0; i < pdbe.length; i++)
1542 int r = v.indexOf(pdbe[i]);
1543 if (r == -1 || r >= pdbentry.length)
1545 rtn.addElement(new int[]
1547 v.addElement(pdbe[i]);
1551 // just make sure the sequence/chain entries are all up to date
1552 addSequenceAndChain(r, seq[i], chns[i]);
1555 pdbe = new PDBEntry[v.size()];
1560 // expand the tied seuqence[] and string[] arrays
1561 SequenceI[][] sqs = new SequenceI[pdbentry.length][];
1562 String[][] sch = new String[pdbentry.length][];
1563 System.arraycopy(sequence, 0, sqs, 0, sequence.length);
1564 System.arraycopy(chains, 0, sch, 0, this.chains.length);
1567 pdbe = new PDBEntry[rtn.size()];
1568 for (int r = 0; r < pdbe.length; r++)
1570 int[] stri = ((int[]) rtn.elementAt(r));
1571 // record the pdb file as a new addition
1572 pdbe[r] = pdbentry[stri[0]];
1573 // and add the new sequence/chain entries
1574 addSequenceAndChain(stri[0], seq[stri[1]], chns[stri[1]]);
1584 public void addSequence(int pe, SequenceI[] seq)
1586 // add sequences to the pe'th pdbentry's seuqence set.
1587 addSequenceAndChain(pe, seq, null);
1590 private void addSequenceAndChain(int pe, SequenceI[] seq, String[] tchain)
1592 if (pe < 0 || pe >= pdbentry.length)
1595 "Implementation error - no corresponding pdbentry (for index "
1596 + pe + ") to add sequences mappings to");
1598 final String nullChain = "TheNullChain";
1599 Vector s = new Vector();
1600 Vector c = new Vector();
1603 chains = new String[pdbentry.length][];
1605 if (sequence[pe] != null)
1607 for (int i = 0; i < sequence[pe].length; i++)
1609 s.addElement(sequence[pe][i]);
1610 if (chains[pe] != null)
1612 if (i < chains[pe].length)
1614 c.addElement(chains[pe][i]);
1618 c.addElement(nullChain);
1623 if (tchain != null && tchain.length > 0)
1625 c.addElement(nullChain);
1630 for (int i = 0; i < seq.length; i++)
1632 if (!s.contains(seq[i]))
1634 s.addElement(seq[i]);
1635 if (tchain != null && i < tchain.length)
1637 c.addElement(tchain[i] == null ? nullChain : tchain[i]);
1641 SequenceI[] tmp = new SequenceI[s.size()];
1646 String[] tch = new String[c.size()];
1648 for (int i = 0; i < tch.length; i++)
1650 if (tch[i] == nullChain)
1666 * @return text report of alignment between pdbfile and any associated
1667 * alignment sequences
1669 public String printMapping(String pdbfile)
1671 return ssm.printMapping(pdbfile);
1675 public void resizeInnerPanel(String data)
1677 // Jalview doesn't honour resize panel requests