2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3 * Copyright (C) 2014 The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.ext.jmol;
23 import jalview.api.AlignmentViewPanel;
24 import jalview.api.FeatureRenderer;
25 import jalview.api.SequenceRenderer;
26 import jalview.api.SequenceStructureBinding;
27 import jalview.api.StructureSelectionManagerProvider;
28 import jalview.datamodel.AlignmentI;
29 import jalview.datamodel.ColumnSelection;
30 import jalview.datamodel.PDBEntry;
31 import jalview.datamodel.SequenceI;
32 import jalview.io.AppletFormatAdapter;
33 import jalview.schemes.ColourSchemeI;
34 import jalview.schemes.ResidueProperties;
35 import jalview.structure.StructureListener;
36 import jalview.structure.StructureMapping;
37 import jalview.structure.StructureSelectionManager;
39 import java.awt.Color;
40 import java.awt.Container;
41 import java.awt.event.ComponentEvent;
42 import java.awt.event.ComponentListener;
45 import java.security.AccessControlException;
46 import java.util.Enumeration;
47 import java.util.Hashtable;
49 import java.util.Vector;
51 import org.jmol.adapter.smarter.SmarterJmolAdapter;
52 import org.jmol.api.JmolAppConsoleInterface;
53 import org.jmol.api.JmolSelectionListener;
54 import org.jmol.api.JmolStatusListener;
55 import org.jmol.api.JmolViewer;
56 import org.jmol.constant.EnumCallback;
57 import org.jmol.popup.JmolPopup;
59 public abstract class JalviewJmolBinding implements StructureListener,
60 JmolStatusListener, SequenceStructureBinding,
61 JmolSelectionListener, ComponentListener,
62 StructureSelectionManagerProvider
66 * set if Jmol state is being restored from some source - instructs binding
67 * not to apply default display style when structure set is updated for first
70 private boolean loadingFromArchive = false;
73 * second flag to indicate if the jmol viewer should ignore sequence colouring
74 * events from the structure manager because the GUI is still setting up
76 private boolean loadingFinished = true;
79 * state flag used to check if the Jmol viewer's paint method can be called
81 private boolean finishedInit = false;
83 public boolean isFinishedInit()
88 public void setFinishedInit(boolean finishedInit)
90 this.finishedInit = finishedInit;
93 boolean allChainsSelected = false;
96 * when true, try to search the associated datamodel for sequences that are
97 * associated with any unknown structures in the Jmol view.
99 private boolean associateNewStructs = false;
101 Vector atomsPicked = new Vector();
103 public Vector chainNames;
108 * array of target chains for seuqences - tied to pdbentry and sequence[]
110 protected String[][] chains;
112 boolean colourBySequence = true;
114 StringBuffer eval = new StringBuffer();
116 public String fileLoadingError;
119 * the default or current model displayed if the model cannot be identified
120 * from the selection message
124 protected JmolPopup jmolpopup;
130 boolean loadedInline;
133 * current set of model filenames loaded in the Jmol instance
135 String[] modelFileNames = null;
137 public PDBEntry[] pdbentry;
140 * datasource protocol for access to PDBEntrylatest
142 String protocol = null;
144 StringBuffer resetLastRes = new StringBuffer();
147 * sequences mapped to each pdbentry
149 public SequenceI[][] sequence;
151 public StructureSelectionManager ssm;
153 public JmolViewer viewer;
155 public JalviewJmolBinding(StructureSelectionManager ssm,
156 PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String[][] chains,
160 this.sequence = sequenceIs;
161 this.chains = chains;
162 this.pdbentry = pdbentry;
163 this.protocol = protocol;
166 this.chains = new String[pdbentry.length][];
169 * viewer = JmolViewer.allocateViewer(renderPanel, new SmarterJmolAdapter(),
170 * "jalviewJmol", ap.av.applet .getDocumentBase(),
171 * ap.av.applet.getCodeBase(), "", this);
173 * jmolpopup = JmolPopup.newJmolPopup(viewer, true, "Jmol", true);
177 public JalviewJmolBinding(StructureSelectionManager ssm,
182 viewer.setJmolStatusListener(this);
183 viewer.addSelectionListener(this);
187 * construct a title string for the viewer window based on the data jalview
192 public String getViewerTitle()
194 if (sequence == null || pdbentry == null || sequence.length < 1
195 || pdbentry.length < 1 || sequence[0].length < 1)
197 return ("Jalview Jmol Window");
199 // TODO: give a more informative title when multiple structures are
201 StringBuffer title = new StringBuffer(sequence[0][0].getName() + ":"
202 + pdbentry[0].getId());
204 if (pdbentry[0].getProperty() != null)
206 if (pdbentry[0].getProperty().get("method") != null)
208 title.append(" Method: ");
209 title.append(pdbentry[0].getProperty().get("method"));
211 if (pdbentry[0].getProperty().get("chains") != null)
213 title.append(" Chain:");
214 title.append(pdbentry[0].getProperty().get("chains"));
217 return title.toString();
221 * prepare the view for a given set of models/chains. chainList contains
222 * strings of the form 'pdbfilename:Chaincode'
225 * list of chains to make visible
227 public void centerViewer(Vector chainList)
229 StringBuffer cmd = new StringBuffer();
232 for (int i = 0, iSize = chainList.size(); i < iSize; i++)
235 lbl = (String) chainList.elementAt(i);
239 mlength = lbl.indexOf(":", p);
240 } while (p < mlength && mlength < (lbl.length() - 2));
241 // TODO: lookup each pdb id and recover proper model number for it.
242 cmd.append(":" + lbl.substring(mlength + 1) + " /"
243 + (1 + getModelNum((String) chainFile.get(lbl))) + " or ");
245 if (cmd.length() > 0)
246 cmd.setLength(cmd.length() - 4);
247 evalStateCommand("select *;restrict " + cmd + ";cartoon;center " + cmd);
250 public void closeViewer()
252 viewer.setModeMouse(org.jmol.viewer.JmolConstants.MOUSE_NONE);
253 // remove listeners for all structures in viewer
254 ssm.removeStructureViewerListener(this, this.getPdbFile());
255 // and shut down jmol
256 viewer.evalStringQuiet("zap");
257 viewer.setJmolStatusListener(null);
260 releaseUIResources();
264 * called by JalviewJmolbinding after closeViewer is called - release any
265 * resources and references so they can be garbage collected.
267 protected abstract void releaseUIResources();
269 public void colourByChain()
271 colourBySequence = false;
272 // TODO: colour by chain should colour each chain distinctly across all
274 // TODO: http://issues.jalview.org/browse/JAL-628
275 evalStateCommand("select *;color chain");
278 public void colourByCharge()
280 colourBySequence = false;
281 evalStateCommand("select *;color white;select ASP,GLU;color red;"
282 + "select LYS,ARG;color blue;select CYS;color yellow");
286 * superpose the structures associated with sequences in the alignment
287 * according to their corresponding positions.
289 public void superposeStructures(AlignmentI alignment)
291 superposeStructures(alignment, -1, null);
295 * superpose the structures associated with sequences in the alignment
296 * according to their corresponding positions. ded)
298 * @param refStructure
299 * - select which pdb file to use as reference (default is -1 - the
300 * first structure in the alignment)
302 public void superposeStructures(AlignmentI alignment, int refStructure)
304 superposeStructures(alignment, refStructure, null);
308 * superpose the structures associated with sequences in the alignment
309 * according to their corresponding positions. ded)
311 * @param refStructure
312 * - select which pdb file to use as reference (default is -1 - the
313 * first structure in the alignment)
317 public void superposeStructures(AlignmentI alignment, int refStructure,
318 ColumnSelection hiddenCols)
320 superposeStructures(new AlignmentI[]
321 { alignment }, new int[]
322 { refStructure }, new ColumnSelection[]
326 public void superposeStructures(AlignmentI[] _alignment,
327 int[] _refStructure, ColumnSelection[] _hiddenCols)
329 assert (_alignment.length == _refStructure.length && _alignment.length != _hiddenCols.length);
331 String[] files = getPdbFile();
332 // check to see if we are still waiting for Jmol files
333 long starttime = System.currentTimeMillis();
334 boolean waiting = true;
338 for (String file : files)
342 // HACK - in Jalview 2.8 this call may not be threadsafe so we catch
343 // every possible exception
344 StructureMapping[] sm = ssm.getMapping(file);
345 if (sm == null || sm.length == 0)
349 } catch (Exception x)
357 // we wait around for a reasonable time before we give up
359 && System.currentTimeMillis() < (10000 + 1000 * files.length + starttime));
363 .println("RUNTIME PROBLEM: Jmol seems to be taking a long time to process all the structures.");
366 StringBuffer selectioncom = new StringBuffer();
367 // In principle - nSeconds specifies the speed of animation for each
368 // superposition - but is seems to behave weirdly, so we don't specify it.
369 String nSeconds = " ";
370 if (files.length > 10)
372 nSeconds = " 0.00001 ";
376 nSeconds = " " + (2.0 / files.length) + " ";
377 // if (nSeconds).substring(0,5)+" ";
379 // see JAL-1345 - should really automatically turn off the animation for
380 // large numbers of structures, but Jmol doesn't seem to allow that.
382 // union of all aligned positions are collected together.
383 for (int a = 0; a < _alignment.length; a++)
385 int refStructure = _refStructure[a];
386 AlignmentI alignment = _alignment[a];
387 ColumnSelection hiddenCols = _hiddenCols[a];
389 && selectioncom.length() > 0
390 && !selectioncom.substring(selectioncom.length() - 1).equals(
393 selectioncom.append("|");
395 // process this alignment
396 if (refStructure >= files.length)
398 System.err.println("Invalid reference structure value "
402 if (refStructure < -1)
406 StringBuffer command = new StringBuffer();
408 boolean matched[] = new boolean[alignment.getWidth()];
409 for (int m = 0; m < matched.length; m++)
412 matched[m] = (hiddenCols != null) ? hiddenCols.isVisible(m) : true;
415 int commonrpositions[][] = new int[files.length][alignment.getWidth()];
416 String isel[] = new String[files.length];
417 // reference structure - all others are superposed in it
418 String[] targetC = new String[files.length];
419 String[] chainNames = new String[files.length];
420 for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
422 StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
423 // RACE CONDITION - getMapping only returns Jmol loaded filenames once
424 // Jmol callback has completed.
425 if (mapping == null || mapping.length < 1)
428 "Implementation error - Jmol seems to be still working on getting its data - report at http://issues.jalview.org/browse/JAL-1016");
431 for (int s = 0; s < sequence[pdbfnum].length; s++)
433 for (int sp, m = 0; m < mapping.length; m++)
435 if (mapping[m].getSequence() == sequence[pdbfnum][s]
436 && (sp = alignment.findIndex(sequence[pdbfnum][s])) > -1)
438 if (refStructure == -1)
440 refStructure = pdbfnum;
442 SequenceI asp = alignment.getSequenceAt(sp);
443 for (int r = 0; r < matched.length; r++)
449 matched[r] = false; // assume this is not a good site
450 if (r >= asp.getLength())
455 if (jalview.util.Comparison.isGap(asp.getCharAt(r)))
457 // no mapping to gaps in sequence
460 int t = asp.findPosition(r); // sequence position
461 int apos = mapping[m].getAtomNum(t);
462 int pos = mapping[m].getPDBResNum(t);
464 if (pos < 1 || pos == lastPos)
466 // can't align unmapped sequence
469 matched[r] = true; // this is a good ite
471 // just record this residue position
472 commonrpositions[pdbfnum][r] = pos;
474 // create model selection suffix
475 isel[pdbfnum] = "/" + (pdbfnum + 1) + ".1";
476 if (mapping[m].getChain() == null
477 || mapping[m].getChain().trim().length() == 0)
479 targetC[pdbfnum] = "";
483 targetC[pdbfnum] = ":" + mapping[m].getChain();
485 chainNames[pdbfnum] = mapping[m].getPdbId()
487 // move on to next pdb file
488 s = sequence[pdbfnum].length;
495 // TODO: consider bailing if nmatched less than 4 because superposition
498 // TODO: refactor superposable position search (above) from jmol selection
499 // construction (below)
501 String[] selcom = new String[files.length];
503 // generate select statements to select regions to superimpose structures
505 for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
507 String chainCd = targetC[pdbfnum];
510 StringBuffer molsel = new StringBuffer();
512 for (int r = 0; r < matched.length; r++)
520 if (lpos != commonrpositions[pdbfnum][r] - 1)
526 molsel.append(chainCd);
527 // molsel.append("} {");
533 // continuous run - and lpos >-1
536 // at the beginning, so add dash
542 lpos = commonrpositions[pdbfnum][r];
543 // molsel.append(lpos);
546 // add final selection phrase
550 molsel.append(chainCd);
553 if (molsel.length() > 1)
555 selcom[pdbfnum] = molsel.toString();
556 selectioncom.append("((");
557 selectioncom.append(selcom[pdbfnum].substring(1,
558 selcom[pdbfnum].length() - 1));
559 selectioncom.append(" )& ");
560 selectioncom.append(pdbfnum + 1);
561 selectioncom.append(".1)");
562 if (pdbfnum < files.length - 1)
564 selectioncom.append("|");
569 selcom[pdbfnum] = null;
573 for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
575 if (pdbfnum == refStructure || selcom[pdbfnum] == null
576 || selcom[refStructure] == null)
580 command.append("echo ");
581 command.append("\"Superposing (");
582 command.append(chainNames[pdbfnum]);
583 command.append(") against reference (");
584 command.append(chainNames[refStructure]);
585 command.append(")\";\ncompare " + nSeconds);
587 command.append(1 + pdbfnum);
588 command.append(".1} {");
589 command.append(1 + refStructure);
590 command.append(".1} SUBSET {*.CA | *.P} ATOMS ");
592 // form the matched pair strings
594 for (int s = 0; s < 2; s++)
596 command.append(selcom[(s == 0 ? pdbfnum : refStructure)]);
598 command.append(" ROTATE TRANSLATE;\n");
600 if (selectioncom.length() > 0)
602 System.out.println("Select regions:\n" + selectioncom.toString());
603 evalStateCommand("select *; cartoons off; backbone; select ("
604 + selectioncom.toString() + "); cartoons; ");
605 // selcom.append("; ribbons; ");
607 .println("Superimpose command(s):\n" + command.toString());
609 evalStateCommand(command.toString());
612 if (selectioncom.length() > 0)
613 {// finally, mark all regions that were superposed.
614 if (selectioncom.substring(selectioncom.length() - 1).equals("|"))
616 selectioncom.setLength(selectioncom.length() - 1);
618 System.out.println("Select regions:\n" + selectioncom.toString());
619 evalStateCommand("select *; cartoons off; backbone; select ("
620 + selectioncom.toString() + "); cartoons; ");
621 // evalStateCommand("select *; backbone; select "+selcom.toString()+"; cartoons; center "+selcom.toString());
625 public void evalStateCommand(String command)
628 if (lastCommand == null || !lastCommand.equals(command))
630 viewer.evalStringQuiet(command + "\n");
633 lastCommand = command;
637 * colour any structures associated with sequences in the given alignment
638 * using the getFeatureRenderer() and getSequenceRenderer() renderers but only
639 * if colourBySequence is enabled.
641 public void colourBySequence(boolean showFeatures,
642 jalview.api.AlignmentViewPanel alignmentv)
644 if (!colourBySequence || !loadingFinished)
650 String[] files = getPdbFile();
652 SequenceRenderer sr = getSequenceRenderer(alignmentv);
654 FeatureRenderer fr = null;
657 fr = getFeatureRenderer(alignmentv);
659 AlignmentI alignment = alignmentv.getAlignment();
661 for (jalview.structure.StructureMappingcommandSet cpdbbyseq : JmolCommands
662 .getColourBySequenceCommand(ssm, files, sequence, sr, fr,
664 for (String cbyseq : cpdbbyseq.commands)
666 evalStateCommand(cbyseq);
670 public boolean isColourBySequence()
672 return colourBySequence;
675 public void setColourBySequence(boolean colourBySequence)
677 this.colourBySequence = colourBySequence;
680 public void createImage(String file, String type, int quality)
682 System.out.println("JMOL CREATE IMAGE");
685 public String createImage(String fileName, String type,
686 Object textOrBytes, int quality)
688 System.out.println("JMOL CREATE IMAGE");
692 public String eval(String strEval)
694 // System.out.println(strEval);
695 // "# 'eval' is implemented only for the applet.";
699 // End StructureListener
700 // //////////////////////////
702 public float[][] functionXY(String functionName, int x, int y)
707 public float[][][] functionXYZ(String functionName, int nx, int ny, int nz)
709 // TODO Auto-generated method stub
713 public Color getColour(int atomIndex, int pdbResNum, String chain,
716 if (getModelNum(pdbfile) < 0)
718 // TODO: verify atomIndex is selecting correct model.
719 return new Color(viewer.getAtomArgb(atomIndex));
723 * returns the current featureRenderer that should be used to colour the
730 public abstract FeatureRenderer getFeatureRenderer(
731 AlignmentViewPanel alignment);
734 * instruct the Jalview binding to update the pdbentries vector if necessary
735 * prior to matching the jmol view's contents to the list of structure files
736 * Jalview knows about.
738 public abstract void refreshPdbEntries();
740 private int getModelNum(String modelFileName)
742 String[] mfn = getPdbFile();
747 for (int i = 0; i < mfn.length; i++)
749 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 public void highlightAtom(int atomIndex, int pdbResNum, String chain,
850 if (modelFileNames == null)
855 // look up file model number for this pdbfile
858 // may need to adjust for URLencoding here - we don't worry about that yet.
859 while (mdlNum < modelFileNames.length
860 && !pdbfile.equals(modelFileNames[mdlNum]))
862 // System.out.println("nomatch:"+pdbfile+"\nmodelfn:"+fn);
865 if (mdlNum == modelFileNames.length)
871 // if (!pdbfile.equals(pdbentry.getFile()))
873 if (resetLastRes.length() > 0)
875 viewer.evalStringQuiet(resetLastRes.toString());
879 eval.append("select " + pdbResNum); // +modelNum
881 resetLastRes.setLength(0);
882 resetLastRes.append("select " + pdbResNum); // +modelNum
885 resetLastRes.append(":");
886 if (!chain.equals(" "))
889 resetLastRes.append(chain);
892 eval.append(" /" + (mdlNum + 1));
893 resetLastRes.append("/" + (mdlNum + 1));
895 eval.append(";wireframe 100;" + eval.toString() + " and not hetero;");
897 resetLastRes.append(";wireframe 0;" + resetLastRes.toString()
898 + " and not hetero; spacefill 0;");
900 eval.append("spacefill 200;select none");
902 viewer.evalStringQuiet(eval.toString());
907 boolean debug = true;
909 private void jmolHistory(boolean enable)
911 viewer.evalStringQuiet("History " + ((debug || enable) ? "on" : "off"));
914 public void loadInline(String string)
918 // viewer.loadInline(strModel, isAppend);
920 // construct fake fullPathName and fileName so we can identify the file
922 // Then, construct pass a reader for the string to Jmol.
923 // ((org.jmol.Viewer.Viewer) viewer).loadModelFromFile(fullPathName,
924 // fileName, null, reader, false, null, null, 0);
925 viewer.openStringInline(string);
928 public void mouseOverStructure(int atomIndex, String strInfo)
931 int alocsep = strInfo.indexOf("^");
932 int mdlSep = strInfo.indexOf("/");
933 int chainSeparator = strInfo.indexOf(":"), chainSeparator1 = -1;
935 if (chainSeparator == -1)
937 chainSeparator = strInfo.indexOf(".");
938 if (mdlSep > -1 && mdlSep < chainSeparator)
940 chainSeparator1 = chainSeparator;
941 chainSeparator = mdlSep;
944 // handle insertion codes
947 pdbResNum = Integer.parseInt(strInfo.substring(
948 strInfo.indexOf("]") + 1, alocsep));
953 pdbResNum = Integer.parseInt(strInfo.substring(
954 strInfo.indexOf("]") + 1, chainSeparator));
958 if (strInfo.indexOf(":") > -1)
959 chainId = strInfo.substring(strInfo.indexOf(":") + 1,
960 strInfo.indexOf("."));
966 String pdbfilename = modelFileNames[frameNo]; // default is first or current
970 if (chainSeparator1 == -1)
972 chainSeparator1 = strInfo.indexOf(".", mdlSep);
974 String mdlId = (chainSeparator1 > -1) ? strInfo.substring(mdlSep + 1,
975 chainSeparator1) : strInfo.substring(mdlSep + 1);
978 // recover PDB filename for the model hovered over.
979 int _mp = _modelFileNameMap.length - 1, mnumber = new Integer(mdlId)
981 while (mnumber < _modelFileNameMap[_mp])
985 pdbfilename = modelFileNames[_mp];
986 if (pdbfilename == null)
988 pdbfilename = new File(viewer.getModelFileName(mnumber))
992 } catch (Exception e)
997 if (lastMessage == null || !lastMessage.equals(strInfo))
998 ssm.mouseOverStructure(pdbResNum, chainId, pdbfilename);
1000 lastMessage = strInfo;
1003 public void notifyAtomHovered(int atomIndex, String strInfo, String data)
1007 System.err.println("Ignoring additional hover info: " + data
1008 + " (other info: '" + strInfo + "' pos " + atomIndex + ")");
1010 mouseOverStructure(atomIndex, strInfo);
1014 * { if (history != null && strStatus != null &&
1015 * !strStatus.equals("Script completed")) { history.append("\n" + strStatus);
1019 public void notifyAtomPicked(int atomIndex, String strInfo, String strData)
1022 * this implements the toggle label behaviour copied from the original
1023 * structure viewer, MCView
1025 if (strData != null)
1027 System.err.println("Ignoring additional pick data string " + strData);
1029 int chainSeparator = strInfo.indexOf(":");
1031 if (chainSeparator == -1)
1032 chainSeparator = strInfo.indexOf(".");
1034 String picked = strInfo.substring(strInfo.indexOf("]") + 1,
1036 String mdlString = "";
1037 if ((p = strInfo.indexOf(":")) > -1)
1038 picked += strInfo.substring(p + 1, strInfo.indexOf("."));
1040 if ((p = strInfo.indexOf("/")) > -1)
1042 mdlString += strInfo.substring(p, strInfo.indexOf(" #"));
1044 picked = "((" + picked + ".CA" + mdlString + ")|(" + picked + ".P"
1048 if (!atomsPicked.contains(picked))
1050 viewer.evalStringQuiet("select " + picked + ";label %n %r:%c");
1051 atomsPicked.addElement(picked);
1055 viewer.evalString("select " + picked + ";label off");
1056 atomsPicked.removeElement(picked);
1059 // TODO: in application this happens
1061 // if (scriptWindow != null)
1063 // scriptWindow.sendConsoleMessage(strInfo);
1064 // scriptWindow.sendConsoleMessage("\n");
1070 public void notifyCallback(EnumCallback type, Object[] data)
1077 notifyFileLoaded((String) data[1], (String) data[2],
1078 (String) data[3], (String) data[4],
1079 ((Integer) data[5]).intValue());
1083 notifyAtomPicked(((Integer) data[2]).intValue(), (String) data[1],
1085 // also highlight in alignment
1087 notifyAtomHovered(((Integer) data[2]).intValue(), (String) data[1],
1091 notifyScriptTermination((String) data[2],
1092 ((Integer) data[3]).intValue());
1095 sendConsoleEcho((String) data[1]);
1098 sendConsoleMessage((data == null) ? ((String) null)
1099 : (String) data[1]);
1102 // System.err.println("Ignoring error callback.");
1112 System.err.println("Unhandled callback " + type + " "
1113 + data[1].toString());
1116 } catch (Exception e)
1118 System.err.println("Squashed Jmol callback handler error:");
1119 e.printStackTrace();
1124 public boolean notifyEnabled(EnumCallback callbackPick)
1126 switch (callbackPick)
1146 // incremented every time a load notification is successfully handled -
1147 // lightweight mechanism for other threads to detect when they can start
1148 // referrring to new structures.
1149 private long loadNotifiesHandled = 0;
1151 public long getLoadNotifiesHandled()
1153 return loadNotifiesHandled;
1156 public void notifyFileLoaded(String fullPathName, String fileName2,
1157 String modelName, String errorMsg, int modelParts)
1159 if (errorMsg != null)
1161 fileLoadingError = errorMsg;
1165 // TODO: deal sensibly with models loaded inLine:
1166 // modelName will be null, as will fullPathName.
1168 // the rest of this routine ignores the arguments, and simply interrogates
1169 // the Jmol view to find out what structures it contains, and adds them to
1170 // the structure selection manager.
1171 fileLoadingError = null;
1172 String[] oldmodels = modelFileNames;
1173 modelFileNames = null;
1174 chainNames = new Vector();
1175 chainFile = new Hashtable();
1176 boolean notifyLoaded = false;
1177 String[] modelfilenames = getPdbFile();
1178 // first check if we've lost any structures
1179 if (oldmodels != null && oldmodels.length > 0)
1182 for (int i = 0; i < oldmodels.length; i++)
1184 for (int n = 0; n < modelfilenames.length; n++)
1186 if (modelfilenames[n] == oldmodels[i])
1188 oldmodels[i] = null;
1192 if (oldmodels[i] != null)
1199 String[] oldmfn = new String[oldm];
1201 for (int i = 0; i < oldmodels.length; i++)
1203 if (oldmodels[i] != null)
1205 oldmfn[oldm++] = oldmodels[i];
1208 // deregister the Jmol instance for these structures - we'll add
1209 // ourselves again at the end for the current structure set.
1210 ssm.removeStructureViewerListener(this, oldmfn);
1213 refreshPdbEntries();
1214 for (int modelnum = 0; modelnum < modelfilenames.length; modelnum++)
1216 String fileName = modelfilenames[modelnum];
1217 boolean foundEntry = false;
1218 MCview.PDBfile pdb = null;
1219 String pdbfile = null, pdbfhash = null;
1220 // model was probably loaded inline - so check the pdb file hashcode
1223 // calculate essential attributes for the pdb data imported inline.
1224 // prolly need to resolve modelnumber properly - for now just use our
1226 pdbfile = viewer.getData("" + (1 + _modelFileNameMap[modelnum])
1228 pdbfhash = "" + pdbfile.hashCode();
1230 if (pdbentry != null)
1232 // search pdbentries and sequences to find correct pdbentry for this
1234 for (int pe = 0; pe < pdbentry.length; pe++)
1236 boolean matches = false;
1237 if (fileName == null)
1240 // see JAL-623 - need method of matching pasted data up
1242 pdb = ssm.setMapping(sequence[pe], chains[pe], pdbfile,
1243 AppletFormatAdapter.PASTE);
1244 pdbentry[modelnum].setFile("INLINE" + pdb.id);
1252 if (matches = (fl = new File(pdbentry[pe].getFile()))
1253 .equals(new File(fileName)))
1256 // TODO: Jmol can in principle retrieve from CLASSLOADER but
1259 // to be tested. See mantis bug
1260 // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
1261 String protocol = AppletFormatAdapter.URL;
1266 protocol = AppletFormatAdapter.FILE;
1268 } catch (Exception e)
1273 // Explicitly map to the filename used by Jmol ;
1274 pdb = ssm.setMapping(sequence[pe], chains[pe], fileName,
1276 // pdbentry[pe].getFile(), protocol);
1282 // add an entry for every chain in the model
1283 for (int i = 0; i < pdb.chains.size(); i++)
1285 String chid = new String(pdb.id + ":"
1286 + ((MCview.PDBChain) pdb.chains.elementAt(i)).id);
1287 chainFile.put(chid, fileName);
1288 chainNames.addElement(chid);
1290 notifyLoaded = true;
1294 if (!foundEntry && associateNewStructs)
1296 // this is a foreign pdb file that jalview doesn't know about - add
1297 // it to the dataset and try to find a home - either on a matching
1298 // sequence or as a new sequence.
1299 String pdbcontent = viewer.getData("/" + (modelnum + 1) + ".1",
1301 // parse pdb file into a chain, etc.
1302 // locate best match for pdb in associated views and add mapping to
1304 // if properly registered then
1305 notifyLoaded = true;
1310 // so finally, update the jmol bits and pieces
1311 if (jmolpopup != null)
1313 // potential for deadlock here:
1314 // jmolpopup.updateComputedMenus();
1316 if (!isLoadingFromArchive())
1318 viewer.evalStringQuiet("model 0; select backbone;restrict;cartoon;wireframe off;spacefill off");
1320 // register ourselves as a listener and notify the gui that it needs to
1322 ssm.addStructureViewerListener(this);
1325 FeatureRenderer fr = getFeatureRenderer(null);
1331 loadNotifiesHandled++;
1333 setLoadingFromArchive(false);
1336 public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)
1338 notifyAtomPicked(iatom, strMeasure, null);
1341 public abstract void notifyScriptTermination(String strStatus,
1345 * display a message echoed from the jmol viewer
1349 public abstract void sendConsoleEcho(String strEcho); /*
1350 * { showConsole(true);
1352 * history.append("\n" +
1356 // /End JmolStatusListener
1357 // /////////////////////////////
1361 * status message - usually the response received after a script
1364 public abstract void sendConsoleMessage(String strStatus);
1366 public void setCallbackFunction(String callbackType,
1367 String callbackFunction)
1369 System.err.println("Ignoring set-callback request to associate "
1370 + callbackType + " with function " + callbackFunction);
1374 public void setJalviewColourScheme(ColourSchemeI cs)
1376 colourBySequence = false;
1385 // TODO: Switch between nucleotide or aa selection expressions
1386 Enumeration en = ResidueProperties.aa3Hash.keys();
1387 StringBuffer command = new StringBuffer("select *;color white;");
1388 while (en.hasMoreElements())
1390 res = en.nextElement().toString();
1391 index = ((Integer) ResidueProperties.aa3Hash.get(res)).intValue();
1395 col = cs.findColour(ResidueProperties.aa[index].charAt(0));
1397 command.append("select " + res + ";color[" + col.getRed() + ","
1398 + col.getGreen() + "," + col.getBlue() + "];");
1401 evalStateCommand(command.toString());
1405 public void showHelp()
1407 showUrl("http://jmol.sourceforge.net/docs/JmolUserGuide/", "jmolHelp");
1411 * open the URL somehow
1415 public abstract void showUrl(String url, String target);
1418 * called when the binding thinks the UI needs to be refreshed after a Jmol
1419 * state change. this could be because structures were loaded, or because an
1420 * error has occured.
1422 public abstract void refreshGUI();
1425 * called to show or hide the associated console window container.
1429 public abstract void showConsole(boolean show);
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
1441 public void allocateViewer(Container renderPanel, boolean jmolfileio,
1442 String htmlName, URL documentBase, URL codeBase,
1443 String commandOptions)
1445 allocateViewer(renderPanel, jmolfileio, htmlName, documentBase,
1446 codeBase, commandOptions, null, null);
1451 * @param renderPanel
1453 * - when true will initialise jmol's file IO system (should be false
1454 * in applet context)
1456 * @param documentBase
1458 * @param commandOptions
1459 * @param consolePanel
1460 * - panel to contain Jmol console
1461 * @param buttonsToShow
1462 * - buttons to show on the console, in ordr
1464 public void allocateViewer(Container renderPanel, boolean jmolfileio,
1465 String htmlName, URL documentBase, URL codeBase,
1466 String commandOptions, final Container consolePanel,
1467 String buttonsToShow)
1469 if (commandOptions == null)
1471 commandOptions = "";
1473 viewer = JmolViewer.allocateViewer(renderPanel,
1474 (jmolfileio ? new SmarterJmolAdapter() : null), htmlName
1475 + ((Object) this).toString(), documentBase, codeBase,
1476 commandOptions, this);
1478 console = createJmolConsole(viewer, consolePanel, buttonsToShow);
1479 if (consolePanel != null)
1481 consolePanel.addComponentListener(this);
1487 protected abstract JmolAppConsoleInterface createJmolConsole(
1488 JmolViewer viewer2, Container consolePanel, String buttonsToShow);
1490 protected org.jmol.api.JmolAppConsoleInterface console = null;
1492 public void componentResized(ComponentEvent e)
1497 public void componentMoved(ComponentEvent e)
1502 public void componentShown(ComponentEvent e)
1507 public void componentHidden(ComponentEvent e)
1512 public void setLoadingFromArchive(boolean loadingFromArchive)
1514 this.loadingFromArchive = loadingFromArchive;
1519 * @return true if Jmol is still restoring state or loading is still going on
1520 * (see setFinsihedLoadingFromArchive)
1522 public boolean isLoadingFromArchive()
1524 return loadingFromArchive && !loadingFinished;
1528 * modify flag which controls if sequence colouring events are honoured by the
1529 * binding. Should be true for normal operation
1531 * @param finishedLoading
1533 public void setFinishedLoadingFromArchive(boolean finishedLoading)
1535 loadingFinished = finishedLoading;
1538 public void setBackgroundColour(java.awt.Color col)
1541 viewer.evalStringQuiet("background [" + col.getRed() + ","
1542 + col.getGreen() + "," + col.getBlue() + "];");
1547 * add structures and any known sequence associations
1549 * @returns the pdb entries added to the current set.
1551 public synchronized PDBEntry[] addSequenceAndChain(PDBEntry[] pdbe,
1552 SequenceI[][] seq, String[][] chns)
1555 Vector v = new Vector();
1556 Vector rtn = new Vector();
1557 for (int i = 0; i < pdbentry.length; i++)
1559 v.addElement(pdbentry[i]);
1561 for (int i = 0; i < pdbe.length; i++)
1563 int r = v.indexOf(pdbe[i]);
1564 if (r == -1 || r >= pdbentry.length)
1566 rtn.addElement(new int[]
1568 v.addElement(pdbe[i]);
1572 // just make sure the sequence/chain entries are all up to date
1573 addSequenceAndChain(r, seq[i], chns[i]);
1576 pdbe = new PDBEntry[v.size()];
1581 // expand the tied seuqence[] and string[] arrays
1582 SequenceI[][] sqs = new SequenceI[pdbentry.length][];
1583 String[][] sch = new String[pdbentry.length][];
1584 System.arraycopy(sequence, 0, sqs, 0, sequence.length);
1585 System.arraycopy(chains, 0, sch, 0, this.chains.length);
1588 pdbe = new PDBEntry[rtn.size()];
1589 for (int r = 0; r < pdbe.length; r++)
1591 int[] stri = ((int[]) rtn.elementAt(r));
1592 // record the pdb file as a new addition
1593 pdbe[r] = pdbentry[stri[0]];
1594 // and add the new sequence/chain entries
1595 addSequenceAndChain(stri[0], seq[stri[1]], chns[stri[1]]);
1605 public void addSequence(int pe, SequenceI[] seq)
1607 // add sequences to the pe'th pdbentry's seuqence set.
1608 addSequenceAndChain(pe, seq, null);
1611 private void addSequenceAndChain(int pe, SequenceI[] seq, String[] tchain)
1613 if (pe < 0 || pe >= pdbentry.length)
1616 "Implementation error - no corresponding pdbentry (for index "
1617 + pe + ") to add sequences mappings to");
1619 final String nullChain = "TheNullChain";
1620 Vector s = new Vector();
1621 Vector c = new Vector();
1624 chains = new String[pdbentry.length][];
1626 if (sequence[pe] != null)
1628 for (int i = 0; i < sequence[pe].length; i++)
1630 s.addElement(sequence[pe][i]);
1631 if (chains[pe] != null)
1633 if (i < chains[pe].length)
1635 c.addElement(chains[pe][i]);
1639 c.addElement(nullChain);
1644 if (tchain != null && tchain.length > 0)
1646 c.addElement(nullChain);
1651 for (int i = 0; i < seq.length; i++)
1653 if (!s.contains(seq[i]))
1655 s.addElement(seq[i]);
1656 if (tchain != null && i < tchain.length)
1658 c.addElement(tchain[i] == null ? nullChain : tchain[i]);
1662 SequenceI[] tmp = new SequenceI[s.size()];
1667 String[] tch = new String[c.size()];
1669 for (int i = 0; i < tch.length; i++)
1671 if (tch[i] == nullChain)
1687 * @return text report of alignment between pdbfile and any associated
1688 * alignment sequences
1690 public String printMapping(String pdbfile)
1692 return ssm.printMapping(pdbfile);
1696 public void resizeInnerPanel(String data)
1698 // Jalview doesn't honour resize panel requests