2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)
\r
3 * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, G Barton, M Clamp, S Searle
\r
5 * This file is part of Jalview.
\r
7 * Jalview is free software: you can redistribute it and/or
\r
8 * modify it under the terms of the GNU General Public License
\r
9 * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
\r
11 * Jalview is distributed in the hope that it will be useful, but
\r
12 * WITHOUT ANY WARRANTY; without even the implied warranty
\r
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
\r
14 * PURPOSE. See the GNU General Public License for more details.
\r
16 * You should have received a copy of the GNU General Public License along with Jalview. If not, see <http://www.gnu.org/licenses/>.
\r
18 package jalview.ext.jmol;
\r
20 import jalview.api.AlignmentViewPanel;
\r
21 import jalview.api.FeatureRenderer;
\r
22 import jalview.api.SequenceRenderer;
\r
23 import jalview.api.SequenceStructureBinding;
\r
24 import jalview.api.StructureSelectionManagerProvider;
\r
25 import jalview.datamodel.AlignmentI;
\r
26 import jalview.datamodel.ColumnSelection;
\r
27 import jalview.datamodel.PDBEntry;
\r
28 import jalview.datamodel.SequenceI;
\r
29 import jalview.io.AppletFormatAdapter;
\r
30 import jalview.schemes.ColourSchemeI;
\r
31 import jalview.schemes.ResidueProperties;
\r
32 import jalview.structure.StructureListener;
\r
33 import jalview.structure.StructureMapping;
\r
34 import jalview.structure.StructureSelectionManager;
\r
36 import java.awt.Color;
\r
37 import java.awt.Container;
\r
38 import java.awt.event.ComponentEvent;
\r
39 import java.awt.event.ComponentListener;
\r
40 import java.io.File;
\r
41 import java.net.URL;
\r
42 import java.security.AccessControlException;
\r
43 import java.util.Enumeration;
\r
44 import java.util.Hashtable;
\r
45 import java.util.Map;
\r
46 import java.util.Vector;
\r
48 import org.jmol.adapter.smarter.SmarterJmolAdapter;
\r
49 import org.jmol.api.JmolAppConsoleInterface;
\r
50 import org.jmol.api.JmolSelectionListener;
\r
51 import org.jmol.api.JmolStatusListener;
\r
52 import org.jmol.api.JmolViewer;
\r
53 import org.jmol.constant.EnumCallback;
\r
54 import org.jmol.popup.JmolPopup;
\r
56 public abstract class JalviewJmolBinding implements StructureListener,
\r
57 JmolStatusListener, SequenceStructureBinding,
\r
58 JmolSelectionListener, ComponentListener, StructureSelectionManagerProvider
\r
62 * set if Jmol state is being restored from some source - instructs binding
\r
63 * not to apply default display style when structure set is updated for first
\r
66 private boolean loadingFromArchive = false;
\r
69 * state flag used to check if the Jmol viewer's paint method can be called
\r
71 private boolean finishedInit = false;
\r
73 public boolean isFinishedInit()
\r
75 return finishedInit;
\r
78 public void setFinishedInit(boolean finishedInit)
\r
80 this.finishedInit = finishedInit;
\r
83 boolean allChainsSelected = false;
\r
86 * when true, try to search the associated datamodel for sequences that are
\r
87 * associated with any unknown structures in the Jmol view.
\r
89 private boolean associateNewStructs = false;
\r
91 Vector atomsPicked = new Vector();
\r
93 public Vector chainNames;
\r
95 Hashtable chainFile;
\r
98 * array of target chains for seuqences - tied to pdbentry and sequence[]
\r
100 protected String[][] chains;
\r
102 boolean colourBySequence = true;
\r
104 StringBuffer eval = new StringBuffer();
\r
106 public String fileLoadingError;
\r
109 * the default or current model displayed if the model cannot be identified
\r
110 * from the selection message
\r
114 protected JmolPopup jmolpopup;
\r
116 String lastCommand;
\r
118 String lastMessage;
\r
120 boolean loadedInline;
\r
123 * current set of model filenames loaded in the Jmol instance
\r
125 String[] modelFileNames = null;
\r
127 public PDBEntry[] pdbentry;
\r
130 * datasource protocol for access to PDBEntrylatest
\r
132 String protocol = null;
\r
134 StringBuffer resetLastRes = new StringBuffer();
\r
137 * sequences mapped to each pdbentry
\r
139 public SequenceI[][] sequence;
\r
141 public StructureSelectionManager ssm;
\r
143 public JmolViewer viewer;
\r
145 public JalviewJmolBinding(StructureSelectionManager ssm, PDBEntry[] pdbentry, SequenceI[][] sequenceIs,
\r
146 String[][] chains, String protocol)
\r
149 this.sequence = sequenceIs;
\r
150 this.chains = chains;
\r
151 this.pdbentry = pdbentry;
\r
152 this.protocol = protocol;
\r
153 if (chains == null)
\r
155 this.chains = new String[pdbentry.length][];
\r
158 * viewer = JmolViewer.allocateViewer(renderPanel, new SmarterJmolAdapter(),
\r
159 * "jalviewJmol", ap.av.applet .getDocumentBase(),
\r
160 * ap.av.applet.getCodeBase(), "", this);
\r
162 * jmolpopup = JmolPopup.newJmolPopup(viewer, true, "Jmol", true);
\r
166 public JalviewJmolBinding(StructureSelectionManager ssm, JmolViewer viewer2)
\r
170 viewer.setJmolStatusListener(this);
\r
171 viewer.addSelectionListener(this);
\r
175 * construct a title string for the viewer window based on the data jalview
\r
180 public String getViewerTitle()
\r
182 if (sequence == null || pdbentry == null || sequence.length < 1
\r
183 || pdbentry.length < 1 || sequence[0].length < 1)
\r
185 return ("Jalview Jmol Window");
\r
187 // TODO: give a more informative title when multiple structures are
\r
189 StringBuffer title = new StringBuffer(sequence[0][0].getName() + ":"
\r
190 + pdbentry[0].getId());
\r
192 if (pdbentry[0].getProperty() != null)
\r
194 if (pdbentry[0].getProperty().get("method") != null)
\r
196 title.append(" Method: ");
\r
197 title.append(pdbentry[0].getProperty().get("method"));
\r
199 if (pdbentry[0].getProperty().get("chains") != null)
\r
201 title.append(" Chain:");
\r
202 title.append(pdbentry[0].getProperty().get("chains"));
\r
205 return title.toString();
\r
209 * prepare the view for a given set of models/chains. chainList contains
\r
210 * strings of the form 'pdbfilename:Chaincode'
\r
213 * list of chains to make visible
\r
215 public void centerViewer(Vector chainList)
\r
217 StringBuffer cmd = new StringBuffer();
\r
220 for (int i = 0, iSize = chainList.size(); i < iSize; i++)
\r
223 lbl = (String) chainList.elementAt(i);
\r
227 mlength = lbl.indexOf(":", p);
\r
228 } while (p < mlength && mlength < (lbl.length() - 2));
\r
229 // TODO: lookup each pdb id and recover proper model number for it.
\r
230 cmd.append(":" + lbl.substring(mlength + 1) + " /"
\r
231 + (1 + getModelNum((String) chainFile.get(lbl))) + " or ");
\r
233 if (cmd.length() > 0)
\r
234 cmd.setLength(cmd.length() - 4);
\r
235 evalStateCommand("select *;restrict " + cmd + ";cartoon;center " + cmd);
\r
238 public void closeViewer()
\r
240 viewer.setModeMouse(org.jmol.viewer.JmolConstants.MOUSE_NONE);
\r
241 // remove listeners for all structures in viewer
\r
242 ssm.removeStructureViewerListener(this, this.getPdbFile());
\r
243 // and shut down jmol
\r
244 viewer.evalStringQuiet("zap");
\r
245 viewer.setJmolStatusListener(null);
\r
246 lastCommand = null;
\r
248 releaseUIResources();
\r
252 * called by JalviewJmolbinding after closeViewer is called - release any
\r
253 * resources and references so they can be garbage collected.
\r
255 protected abstract void releaseUIResources();
\r
257 public void colourByChain()
\r
259 colourBySequence = false;
\r
260 // TODO: colour by chain should colour each chain distinctly across all
\r
262 // TODO: http://issues.jalview.org/browse/JAL-628
\r
263 evalStateCommand("select *;color chain");
\r
266 public void colourByCharge()
\r
268 colourBySequence = false;
\r
269 evalStateCommand("select *;color white;select ASP,GLU;color red;"
\r
270 + "select LYS,ARG;color blue;select CYS;color yellow");
\r
274 * superpose the structures associated with sequences in the alignment
\r
275 * according to their corresponding positions.
\r
277 public void superposeStructures(AlignmentI alignment)
\r
279 superposeStructures(alignment, -1, null);
\r
283 * superpose the structures associated with sequences in the alignment
\r
284 * according to their corresponding positions. ded)
\r
286 * @param refStructure
\r
287 * - select which pdb file to use as reference (default is -1 - the
\r
288 * first structure in the alignment)
\r
290 public void superposeStructures(AlignmentI alignment, int refStructure)
\r
292 superposeStructures(alignment, refStructure, null);
\r
296 * superpose the structures associated with sequences in the alignment
\r
297 * according to their corresponding positions. ded)
\r
299 * @param refStructure
\r
300 * - select which pdb file to use as reference (default is -1 - the
\r
301 * first structure in the alignment)
\r
302 * @param hiddenCols
\r
305 public void superposeStructures(AlignmentI alignment, int refStructure,
\r
306 ColumnSelection hiddenCols)
\r
308 superposeStructures(new AlignmentI[]
\r
309 { alignment }, new int[]
\r
310 { refStructure }, new ColumnSelection[]
\r
314 public void superposeStructures(AlignmentI[] _alignment,
\r
315 int[] _refStructure, ColumnSelection[] _hiddenCols)
\r
317 String[] files = getPdbFile();
\r
318 StringBuffer selectioncom = new StringBuffer();
\r
319 assert (_alignment.length == _refStructure.length && _alignment.length != _hiddenCols.length);
\r
320 // union of all aligned positions are collected together.
\r
321 for (int a = 0; a < _alignment.length; a++)
\r
323 int refStructure = _refStructure[a];
\r
324 AlignmentI alignment = _alignment[a];
\r
325 ColumnSelection hiddenCols = _hiddenCols[a];
\r
327 && selectioncom.length() > 0
\r
328 && !selectioncom.substring(selectioncom.length() - 1).equals(
\r
331 selectioncom.append("|");
\r
333 // process this alignment
\r
334 if (refStructure >= files.length)
\r
336 System.err.println("Invalid reference structure value "
\r
340 if (refStructure < -1)
\r
344 StringBuffer command = new StringBuffer();
\r
346 boolean matched[] = new boolean[alignment.getWidth()];
\r
347 for (int m = 0; m < matched.length; m++)
\r
350 matched[m] = (hiddenCols != null) ? hiddenCols.isVisible(m) : true;
\r
353 int commonrpositions[][] = new int[files.length][alignment.getWidth()];
\r
354 String isel[] = new String[files.length];
\r
355 // reference structure - all others are superposed in it
\r
356 String[] targetC = new String[files.length];
\r
357 String[] chainNames = new String[files.length];
\r
358 for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
\r
360 StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
\r
361 // RACE CONDITION - getMapping only returns Jmol loaded filenames once Jmol callback has completed.
\r
362 if (mapping == null || mapping.length < 1)
\r
366 for (int s = 0; s < sequence[pdbfnum].length; s++)
\r
368 for (int sp, m = 0; m < mapping.length; m++)
\r
370 if (mapping[m].getSequence() == sequence[pdbfnum][s]
\r
371 && (sp = alignment.findIndex(sequence[pdbfnum][s])) > -1)
\r
373 if (refStructure == -1)
\r
375 refStructure = pdbfnum;
\r
377 SequenceI asp = alignment.getSequenceAt(sp);
\r
378 for (int r = 0; r < matched.length; r++)
\r
384 matched[r] = false; // assume this is not a good site
\r
385 if (r >= asp.getLength())
\r
390 if (jalview.util.Comparison.isGap(asp.getCharAt(r)))
\r
392 // no mapping to gaps in sequence
\r
395 int t = asp.findPosition(r); // sequence position
\r
396 int apos = mapping[m].getAtomNum(t);
\r
397 int pos = mapping[m].getPDBResNum(t);
\r
399 if (pos < 1 || pos == lastPos)
\r
401 // can't align unmapped sequence
\r
404 matched[r] = true; // this is a good ite
\r
406 // just record this residue position
\r
407 commonrpositions[pdbfnum][r] = pos;
\r
409 // create model selection suffix
\r
410 isel[pdbfnum] = "/" + (pdbfnum + 1) + ".1";
\r
411 if (mapping[m].getChain() == null
\r
412 || mapping[m].getChain().trim().length() == 0)
\r
414 targetC[pdbfnum] = "";
\r
418 targetC[pdbfnum] = ":" + mapping[m].getChain();
\r
420 chainNames[pdbfnum] = mapping[m].getPdbId()
\r
421 + targetC[pdbfnum];
\r
422 // move on to next pdb file
\r
423 s = sequence[pdbfnum].length;
\r
429 String[] selcom = new String[files.length];
\r
431 // generate select statements to select regions to superimpose structures
\r
433 for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
\r
435 String chainCd = targetC[pdbfnum];
\r
437 boolean run = false;
\r
438 StringBuffer molsel = new StringBuffer();
\r
439 molsel.append("{");
\r
440 for (int r = 0; r < matched.length; r++)
\r
448 if (lpos != commonrpositions[pdbfnum][r] - 1)
\r
453 molsel.append(lpos);
\r
454 molsel.append(chainCd);
\r
455 // molsel.append("} {");
\r
456 molsel.append("|");
\r
461 // continuous run - and lpos >-1
\r
464 // at the beginning, so add dash
\r
465 molsel.append(lpos);
\r
466 molsel.append("-");
\r
470 lpos = commonrpositions[pdbfnum][r];
\r
471 // molsel.append(lpos);
\r
474 // add final selection phrase
\r
477 molsel.append(lpos);
\r
478 molsel.append(chainCd);
\r
479 molsel.append("}");
\r
481 selcom[pdbfnum] = molsel.toString();
\r
482 selectioncom.append("((");
\r
483 selectioncom.append(selcom[pdbfnum].substring(1,
\r
484 selcom[pdbfnum].length() - 1));
\r
485 selectioncom.append(" )& ");
\r
486 selectioncom.append(pdbfnum + 1);
\r
487 selectioncom.append(".1)");
\r
488 if (pdbfnum < files.length - 1)
\r
490 selectioncom.append("|");
\r
494 // TODO: consider bailing if nmatched less than 4 because superposition
\r
497 // TODO: refactor superposable position search (above) from jmol selection
\r
498 // construction (below)
\r
499 for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
\r
501 if (pdbfnum == refStructure)
\r
505 command.append("echo ");
\r
506 command.append("\"Superposing (");
\r
507 command.append(chainNames[pdbfnum]);
\r
508 command.append(") against reference (");
\r
509 command.append(chainNames[refStructure]);
\r
510 command.append(")\";\ncompare ");
\r
511 command.append("{");
\r
512 command.append(1 + pdbfnum);
\r
513 command.append(".1} {");
\r
514 command.append(1 + refStructure);
\r
515 command.append(".1} SUBSET {*.CA | *.P} ATOMS ");
\r
517 // form the matched pair strings
\r
519 for (int s = 0; s < 2; s++)
\r
521 command.append(selcom[(s == 0 ? pdbfnum : refStructure)]);
\r
523 command.append(" ROTATE TRANSLATE;\n");
\r
525 System.out.println("Select regions:\n" + selectioncom.toString());
\r
526 evalStateCommand("select *; cartoons off; backbone; select ("
\r
527 + selectioncom.toString() + "); cartoons; ");
\r
528 // selcom.append("; ribbons; ");
\r
529 System.out.println("Superimpose command(s):\n" + command.toString());
\r
531 evalStateCommand(command.toString());
\r
533 if (selectioncom.length() > 0)
\r
534 {// finally, mark all regions that were superposed.
\r
535 if (selectioncom.substring(selectioncom.length() - 1).equals("|"))
\r
537 selectioncom.setLength(selectioncom.length() - 1);
\r
539 System.out.println("Select regions:\n" + selectioncom.toString());
\r
540 evalStateCommand("select *; cartoons off; backbone; select ("
\r
541 + selectioncom.toString() + "); cartoons; ");
\r
542 // evalStateCommand("select *; backbone; select "+selcom.toString()+"; cartoons; center "+selcom.toString());
\r
546 public void evalStateCommand(String command)
\r
548 jmolHistory(false);
\r
549 if (lastCommand == null || !lastCommand.equals(command))
\r
551 viewer.evalStringQuiet(command + "\n");
\r
554 lastCommand = command;
\r
558 * colour any structures associated with sequences in the given alignment
\r
559 * using the getFeatureRenderer() and getSequenceRenderer() renderers but only
\r
560 * if colourBySequence is enabled.
\r
562 public void colourBySequence(boolean showFeatures,
\r
563 jalview.api.AlignmentViewPanel alignmentv)
\r
565 if (!colourBySequence)
\r
571 String[] files = getPdbFile();
\r
573 SequenceRenderer sr = getSequenceRenderer(alignmentv);
\r
575 FeatureRenderer fr = null;
\r
578 fr = getFeatureRenderer(alignmentv);
\r
580 AlignmentI alignment = alignmentv.getAlignment();
\r
582 for (jalview.structure.StructureMappingcommandSet cpdbbyseq: JmolCommands.getColourBySequenceCommand(ssm, files, sequence, sr, fr, alignment))
\r
583 for (String cbyseq : cpdbbyseq.commands) {
\r
584 evalStateCommand(cbyseq);
\r
588 public boolean isColourBySequence()
\r
590 return colourBySequence;
\r
593 public void setColourBySequence(boolean colourBySequence)
\r
595 this.colourBySequence = colourBySequence;
\r
598 public void createImage(String file, String type, int quality)
\r
600 System.out.println("JMOL CREATE IMAGE");
\r
603 public String createImage(String fileName, String type,
\r
604 Object textOrBytes, int quality)
\r
606 System.out.println("JMOL CREATE IMAGE");
\r
610 public String eval(String strEval)
\r
612 // System.out.println(strEval);
\r
613 // "# 'eval' is implemented only for the applet.";
\r
617 // End StructureListener
\r
618 // //////////////////////////
\r
620 public float[][] functionXY(String functionName, int x, int y)
\r
625 public float[][][] functionXYZ(String functionName, int nx, int ny, int nz)
\r
627 // TODO Auto-generated method stub
\r
631 public Color getColour(int atomIndex, int pdbResNum, String chain,
\r
634 if (getModelNum(pdbfile) < 0)
\r
636 // TODO: verify atomIndex is selecting correct model.
\r
637 return new Color(viewer.getAtomArgb(atomIndex));
\r
641 * returns the current featureRenderer that should be used to colour the
\r
648 public abstract FeatureRenderer getFeatureRenderer(
\r
649 AlignmentViewPanel alignment);
\r
652 * instruct the Jalview binding to update the pdbentries vector if necessary
\r
653 * prior to matching the jmol view's contents to the list of structure files
\r
654 * Jalview knows about.
\r
656 public abstract void refreshPdbEntries();
\r
658 private int getModelNum(String modelFileName)
\r
660 String[] mfn = getPdbFile();
\r
665 for (int i = 0; i < mfn.length; i++)
\r
667 if (mfn[i].equalsIgnoreCase(modelFileName))
\r
674 * map between index of model filename returned from getPdbFile and the first
\r
675 * index of models from this file in the viewer. Note - this is not trimmed -
\r
676 * use getPdbFile to get number of unique models.
\r
678 private int _modelFileNameMap[];
\r
680 // ////////////////////////////////
\r
681 // /StructureListener
\r
682 public synchronized String[] getPdbFile()
\r
684 if (viewer == null)
\r
686 return new String[0];
\r
688 if (modelFileNames == null)
\r
691 String mset[] = new String[viewer.getModelCount()];
\r
692 _modelFileNameMap = new int[mset.length];
\r
694 String m=viewer.getModelFileName(0);
\r
698 mset[0] = new File(m).getAbsolutePath();
\r
699 } catch (AccessControlException x) {
\r
700 // usually not allowed to do this in applet, so keep raw handle
\r
702 //System.err.println("jmolBinding: Using local file string from Jmol: "+m);
\r
705 for (int i = 1; i < mset.length; i++)
\r
707 m=viewer.getModelFileName(i);
\r
710 mset[j] = new File(m).getAbsolutePath();
\r
711 } catch (AccessControlException x) {
\r
712 // usually not allowed to do this in applet, so keep raw handle
\r
714 //System.err.println("jmolBinding: Using local file string from Jmol: "+m);
\r
717 _modelFileNameMap[j] = i; // record the model index for the filename
\r
718 // skip any additional models in the same file (NMR structures)
\r
719 if ((mset[j] == null ? mset[j] != mset[j - 1]
\r
720 : (mset[j - 1] == null || !mset[j].equals(mset[j - 1]))))
\r
725 modelFileNames = new String[j];
\r
726 System.arraycopy(mset, 0, modelFileNames, 0, j);
\r
728 return modelFileNames;
\r
732 * map from string to applet
\r
734 public Map getRegistryInfo()
\r
736 // TODO Auto-generated method stub
\r
741 * returns the current sequenceRenderer that should be used to colour the
\r
748 public abstract SequenceRenderer getSequenceRenderer(
\r
749 AlignmentViewPanel alignment);
\r
751 // ///////////////////////////////
\r
752 // JmolStatusListener
\r
754 public void handlePopupMenu(int x, int y)
\r
756 jmolpopup.show(x, y);
\r
760 public void highlightAtom(int atomIndex, int pdbResNum, String chain,
\r
763 if (modelFileNames == null)
\r
768 // look up file model number for this pdbfile
\r
771 // may need to adjust for URLencoding here - we don't worry about that yet.
\r
772 while (mdlNum < modelFileNames.length
\r
773 && !pdbfile.equals(modelFileNames[mdlNum]))
\r
775 // System.out.println("nomatch:"+pdbfile+"\nmodelfn:"+fn);
\r
778 if (mdlNum == modelFileNames.length)
\r
783 jmolHistory(false);
\r
784 // if (!pdbfile.equals(pdbentry.getFile()))
\r
786 if (resetLastRes.length() > 0)
\r
788 viewer.evalStringQuiet(resetLastRes.toString());
\r
792 eval.append("select " + pdbResNum); // +modelNum
\r
794 resetLastRes.setLength(0);
\r
795 resetLastRes.append("select " + pdbResNum); // +modelNum
\r
798 resetLastRes.append(":");
\r
799 if (!chain.equals(" "))
\r
801 eval.append(chain);
\r
802 resetLastRes.append(chain);
\r
805 eval.append(" /" + (mdlNum + 1));
\r
806 resetLastRes.append("/" + (mdlNum + 1));
\r
808 eval.append(";wireframe 100;" + eval.toString() + " and not hetero;");
\r
810 resetLastRes.append(";wireframe 0;" + resetLastRes.toString()
\r
811 + " and not hetero; spacefill 0;");
\r
813 eval.append("spacefill 200;select none");
\r
815 viewer.evalStringQuiet(eval.toString());
\r
820 boolean debug = true;
\r
822 private void jmolHistory(boolean enable)
\r
824 viewer.evalStringQuiet("History " + ((debug || enable) ? "on" : "off"));
\r
827 public void loadInline(String string)
\r
829 loadedInline = true;
\r
830 // TODO: re JAL-623
\r
831 // viewer.loadInline(strModel, isAppend);
\r
833 // construct fake fullPathName and fileName so we can identify the file
\r
835 // Then, construct pass a reader for the string to Jmol.
\r
836 // ((org.jmol.Viewer.Viewer) viewer).loadModelFromFile(fullPathName,
\r
837 // fileName, null, reader, false, null, null, 0);
\r
838 viewer.openStringInline(string);
\r
841 public void mouseOverStructure(int atomIndex, String strInfo)
\r
844 int alocsep = strInfo.indexOf("^");
\r
845 int mdlSep = strInfo.indexOf("/");
\r
846 int chainSeparator = strInfo.indexOf(":"), chainSeparator1 = -1;
\r
848 if (chainSeparator == -1)
\r
850 chainSeparator = strInfo.indexOf(".");
\r
851 if (mdlSep > -1 && mdlSep < chainSeparator)
\r
853 chainSeparator1 = chainSeparator;
\r
854 chainSeparator = mdlSep;
\r
857 // handle insertion codes
\r
860 pdbResNum = Integer.parseInt(strInfo.substring(
\r
861 strInfo.indexOf("]") + 1, alocsep));
\r
866 pdbResNum = Integer.parseInt(strInfo.substring(
\r
867 strInfo.indexOf("]") + 1, chainSeparator));
\r
871 if (strInfo.indexOf(":") > -1)
\r
872 chainId = strInfo.substring(strInfo.indexOf(":") + 1,
\r
873 strInfo.indexOf("."));
\r
879 String pdbfilename = modelFileNames[frameNo]; // default is first or current
\r
883 if (chainSeparator1 == -1)
\r
885 chainSeparator1 = strInfo.indexOf(".", mdlSep);
\r
887 String mdlId = (chainSeparator1 > -1) ? strInfo.substring(mdlSep + 1,
\r
888 chainSeparator1) : strInfo.substring(mdlSep + 1);
\r
891 // recover PDB filename for the model hovered over.
\r
892 int _mp=_modelFileNameMap.length-1,
\r
893 mnumber=new Integer(mdlId).intValue() - 1;
\r
894 while(mnumber<_modelFileNameMap[_mp])
\r
898 pdbfilename = modelFileNames[_mp];
\r
899 if (pdbfilename==null) {pdbfilename=new File(viewer
\r
900 .getModelFileName(mnumber)).getAbsolutePath();
\r
903 } catch (Exception e)
\r
908 if (lastMessage == null || !lastMessage.equals(strInfo))
\r
909 ssm.mouseOverStructure(pdbResNum, chainId, pdbfilename);
\r
911 lastMessage = strInfo;
\r
914 public void notifyAtomHovered(int atomIndex, String strInfo, String data)
\r
918 System.err.println("Ignoring additional hover info: " + data
\r
919 + " (other info: '" + strInfo + "' pos " + atomIndex + ")");
\r
921 mouseOverStructure(atomIndex, strInfo);
\r
925 * { if (history != null && strStatus != null &&
\r
926 * !strStatus.equals("Script completed")) { history.append("\n" + strStatus);
\r
930 public void notifyAtomPicked(int atomIndex, String strInfo, String strData)
\r
933 * this implements the toggle label behaviour copied from the original
\r
934 * structure viewer, MCView
\r
936 if (strData != null)
\r
938 System.err.println("Ignoring additional pick data string " + strData);
\r
940 int chainSeparator = strInfo.indexOf(":");
\r
942 if (chainSeparator == -1)
\r
943 chainSeparator = strInfo.indexOf(".");
\r
945 String picked = strInfo.substring(strInfo.indexOf("]") + 1,
\r
947 String mdlString = "";
\r
948 if ((p = strInfo.indexOf(":")) > -1)
\r
949 picked += strInfo.substring(p + 1, strInfo.indexOf("."));
\r
951 if ((p = strInfo.indexOf("/")) > -1)
\r
953 mdlString += strInfo.substring(p, strInfo.indexOf(" #"));
\r
955 picked = "((" + picked + ".CA" + mdlString + ")|(" + picked + ".P"
\r
956 + mdlString + "))";
\r
957 jmolHistory(false);
\r
959 if (!atomsPicked.contains(picked))
\r
961 viewer.evalStringQuiet("select " + picked + ";label %n %r:%c");
\r
962 atomsPicked.addElement(picked);
\r
966 viewer.evalString("select " + picked + ";label off");
\r
967 atomsPicked.removeElement(picked);
\r
970 // TODO: in application this happens
\r
972 // if (scriptWindow != null)
\r
974 // scriptWindow.sendConsoleMessage(strInfo);
\r
975 // scriptWindow.sendConsoleMessage("\n");
\r
981 public void notifyCallback(EnumCallback type, Object[] data)
\r
988 notifyFileLoaded((String) data[1], (String) data[2],
\r
989 (String) data[3], (String) data[4],
\r
990 ((Integer) data[5]).intValue());
\r
994 notifyAtomPicked(((Integer) data[2]).intValue(), (String) data[1],
\r
996 // also highlight in alignment
\r
998 notifyAtomHovered(((Integer) data[2]).intValue(), (String) data[1],
\r
1002 notifyScriptTermination((String) data[2],
\r
1003 ((Integer) data[3]).intValue());
\r
1006 sendConsoleEcho((String) data[1]);
\r
1009 sendConsoleMessage((data == null) ? ((String) null)
\r
1010 : (String) data[1]);
\r
1013 // System.err.println("Ignoring error callback.");
\r
1023 System.err.println("Unhandled callback " + type + " "
\r
1024 + data[1].toString());
\r
1027 } catch (Exception e)
\r
1029 System.err.println("Squashed Jmol callback handler error:");
\r
1030 e.printStackTrace();
\r
1035 public boolean notifyEnabled(EnumCallback callbackPick)
\r
1037 switch (callbackPick)
\r
1052 case MINIMIZATION:
\r
1057 // incremented every time a load notification is successfully handled -
\r
1058 // lightweight mechanism for other threads to detect when they can start
\r
1059 // referrring to new structures.
\r
1060 private long loadNotifiesHandled = 0;
\r
1062 public long getLoadNotifiesHandled()
\r
1064 return loadNotifiesHandled;
\r
1067 public void notifyFileLoaded(String fullPathName, String fileName2,
\r
1068 String modelName, String errorMsg, int modelParts)
\r
1070 if (errorMsg != null)
\r
1072 fileLoadingError = errorMsg;
\r
1076 // TODO: deal sensibly with models loaded inLine:
\r
1077 // modelName will be null, as will fullPathName.
\r
1079 // the rest of this routine ignores the arguments, and simply interrogates
\r
1080 // the Jmol view to find out what structures it contains, and adds them to
\r
1081 // the structure selection manager.
\r
1082 fileLoadingError = null;
\r
1083 String[] oldmodels = modelFileNames;
\r
1084 modelFileNames = null;
\r
1085 chainNames = new Vector();
\r
1086 chainFile = new Hashtable();
\r
1087 boolean notifyLoaded = false;
\r
1088 String[] modelfilenames = getPdbFile();
\r
1089 // first check if we've lost any structures
\r
1090 if (oldmodels != null && oldmodels.length > 0)
\r
1093 for (int i = 0; i < oldmodels.length; i++)
\r
1095 for (int n = 0; n < modelfilenames.length; n++)
\r
1097 if (modelfilenames[n] == oldmodels[i])
\r
1099 oldmodels[i] = null;
\r
1103 if (oldmodels[i] != null)
\r
1110 String[] oldmfn = new String[oldm];
\r
1112 for (int i = 0; i < oldmodels.length; i++)
\r
1114 if (oldmodels[i] != null)
\r
1116 oldmfn[oldm++] = oldmodels[i];
\r
1119 // deregister the Jmol instance for these structures - we'll add
\r
1120 // ourselves again at the end for the current structure set.
\r
1121 ssm.removeStructureViewerListener(this, oldmfn);
\r
1124 refreshPdbEntries();
\r
1125 for (int modelnum = 0; modelnum < modelfilenames.length; modelnum++)
\r
1127 String fileName = modelfilenames[modelnum];
\r
1128 boolean foundEntry = false;
\r
1129 MCview.PDBfile pdb = null;
\r
1130 String pdbfile = null, pdbfhash = null;
\r
1131 // model was probably loaded inline - so check the pdb file hashcode
\r
1134 // calculate essential attributes for the pdb data imported inline.
\r
1135 // prolly need to resolve modelnumber properly - for now just use our
\r
1137 pdbfile = viewer.getData("" + (1 + _modelFileNameMap[modelnum])
\r
1139 pdbfhash = "" + pdbfile.hashCode();
\r
1141 if (pdbentry != null)
\r
1143 // search pdbentries and sequences to find correct pdbentry for this
\r
1145 for (int pe = 0; pe < pdbentry.length; pe++)
\r
1147 boolean matches = false;
\r
1148 if (fileName == null)
\r
1151 // see JAL-623 - need method of matching pasted data up
\r
1153 pdb = ssm.setMapping(sequence[pe], chains[pe], pdbfile,
\r
1154 AppletFormatAdapter.PASTE);
\r
1155 pdbentry[modelnum].setFile("INLINE" + pdb.id);
\r
1157 foundEntry = true;
\r
1163 if (matches = (fl=new File(pdbentry[pe].getFile())).equals(new File(fileName)))
\r
1165 foundEntry = true;
\r
1166 // TODO: Jmol can in principle retrieve from CLASSLOADER but
\r
1169 // to be tested. See mantis bug
\r
1170 // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
\r
1171 String protocol = AppletFormatAdapter.URL;
\r
1176 protocol = AppletFormatAdapter.FILE;
\r
1178 } catch (Exception e)
\r
1183 //Explicitly map to the filename used by Jmol ;
\r
1184 pdb = ssm.setMapping(sequence[pe], chains[pe],
\r
1185 fileName, protocol);
\r
1186 //pdbentry[pe].getFile(), protocol);
\r
1192 // add an entry for every chain in the model
\r
1193 for (int i = 0; i < pdb.chains.size(); i++)
\r
1195 String chid = new String(pdb.id + ":"
\r
1196 + ((MCview.PDBChain) pdb.chains.elementAt(i)).id);
\r
1197 chainFile.put(chid, fileName);
\r
1198 chainNames.addElement(chid);
\r
1200 notifyLoaded = true;
\r
1204 if (!foundEntry && associateNewStructs)
\r
1206 // this is a foreign pdb file that jalview doesn't know about - add
\r
1207 // it to the dataset and try to find a home - either on a matching
\r
1208 // sequence or as a new sequence.
\r
1209 String pdbcontent = viewer.getData("/" + (modelnum + 1) + ".1",
\r
1211 // parse pdb file into a chain, etc.
\r
1212 // locate best match for pdb in associated views and add mapping to
\r
1214 // if properly registered then
\r
1215 notifyLoaded = true;
\r
1220 // so finally, update the jmol bits and pieces
\r
1221 if (jmolpopup != null)
\r
1223 // potential for deadlock here:
\r
1224 // jmolpopup.updateComputedMenus();
\r
1226 if (!isLoadingFromArchive())
\r
1228 viewer.evalStringQuiet("model 0; select backbone;restrict;cartoon;wireframe off;spacefill off");
\r
1230 // register ourselves as a listener and notify the gui that it needs to
\r
1232 ssm.addStructureViewerListener(this);
\r
1235 FeatureRenderer fr = getFeatureRenderer(null);
\r
1238 fr.featuresAdded();
\r
1241 loadNotifiesHandled++;
\r
1243 setLoadingFromArchive(false);
\r
1246 public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)
\r
1248 notifyAtomPicked(iatom, strMeasure, null);
\r
1251 public abstract void notifyScriptTermination(String strStatus,
\r
1255 * display a message echoed from the jmol viewer
\r
1259 public abstract void sendConsoleEcho(String strEcho); /*
\r
1260 * { showConsole(true);
\r
1262 * history.append("\n" +
\r
1266 // /End JmolStatusListener
\r
1267 // /////////////////////////////
\r
1270 * @param strStatus
\r
1271 * status message - usually the response received after a script
\r
1274 public abstract void sendConsoleMessage(String strStatus);
\r
1276 public void setCallbackFunction(String callbackType,
\r
1277 String callbackFunction)
\r
1279 System.err.println("Ignoring set-callback request to associate "
\r
1280 + callbackType + " with function " + callbackFunction);
\r
1284 public void setJalviewColourScheme(ColourSchemeI cs)
\r
1286 colourBySequence = false;
\r
1294 jmolHistory(false);
\r
1295 // TODO: Switch between nucleotide or aa selection expressions
\r
1296 Enumeration en = ResidueProperties.aa3Hash.keys();
\r
1297 StringBuffer command = new StringBuffer("select *;color white;");
\r
1298 while (en.hasMoreElements())
\r
1300 res = en.nextElement().toString();
\r
1301 index = ((Integer) ResidueProperties.aa3Hash.get(res)).intValue();
\r
1305 col = cs.findColour(ResidueProperties.aa[index].charAt(0));
\r
1307 command.append("select " + res + ";color[" + col.getRed() + ","
\r
1308 + col.getGreen() + "," + col.getBlue() + "];");
\r
1311 evalStateCommand(command.toString());
\r
1312 jmolHistory(true);
\r
1315 public void showHelp()
\r
1317 showUrl("http://jmol.sourceforge.net/docs/JmolUserGuide/", "jmolHelp");
\r
1321 * open the URL somehow
\r
1325 public abstract void showUrl(String url, String target);
\r
1328 * called when the binding thinks the UI needs to be refreshed after a Jmol
\r
1329 * state change. this could be because structures were loaded, or because an
\r
1330 * error has occured.
\r
1332 public abstract void refreshGUI();
\r
1335 * called to show or hide the associated console window container.
\r
1339 public abstract void showConsole(boolean show);
\r
1342 * @param renderPanel
\r
1343 * @param jmolfileio
\r
1344 * - when true will initialise jmol's file IO system (should be false
\r
1345 * in applet context)
\r
1347 * @param documentBase
\r
1349 * @param commandOptions
\r
1351 public void allocateViewer(Container renderPanel, boolean jmolfileio,
\r
1352 String htmlName, URL documentBase, URL codeBase,
\r
1353 String commandOptions)
\r
1355 allocateViewer(renderPanel, jmolfileio, htmlName, documentBase,
\r
1356 codeBase, commandOptions, null, null);
\r
1361 * @param renderPanel
\r
1362 * @param jmolfileio
\r
1363 * - when true will initialise jmol's file IO system (should be false
\r
1364 * in applet context)
\r
1366 * @param documentBase
\r
1368 * @param commandOptions
\r
1369 * @param consolePanel
\r
1370 * - panel to contain Jmol console
\r
1371 * @param buttonsToShow
\r
1372 * - buttons to show on the console, in ordr
\r
1374 public void allocateViewer(Container renderPanel, boolean jmolfileio,
\r
1375 String htmlName, URL documentBase, URL codeBase,
\r
1376 String commandOptions, final Container consolePanel,
\r
1377 String buttonsToShow)
\r
1379 if (commandOptions==null) {
1382 viewer = JmolViewer.allocateViewer(renderPanel,
\r
1383 (jmolfileio ? new SmarterJmolAdapter() : null), htmlName
\r
1384 + ((Object) this).toString(), documentBase, codeBase,
\r
1385 commandOptions, this);
\r
1387 console = createJmolConsole(viewer, consolePanel, buttonsToShow);
\r
1388 if (consolePanel != null)
\r
1390 consolePanel.addComponentListener(this);
\r
1396 protected abstract JmolAppConsoleInterface createJmolConsole(
\r
1397 JmolViewer viewer2, Container consolePanel, String buttonsToShow);
\r
1399 protected org.jmol.api.JmolAppConsoleInterface console = null;
\r
1401 public void componentResized(ComponentEvent e)
\r
1406 public void componentMoved(ComponentEvent e)
\r
1411 public void componentShown(ComponentEvent e)
\r
1413 showConsole(true);
\r
1416 public void componentHidden(ComponentEvent e)
\r
1418 showConsole(false);
\r
1421 public void setLoadingFromArchive(boolean loadingFromArchive)
\r
1423 this.loadingFromArchive = loadingFromArchive;
\r
1426 public boolean isLoadingFromArchive()
\r
1428 return loadingFromArchive;
\r
1431 public void setBackgroundColour(java.awt.Color col)
\r
1433 jmolHistory(false);
\r
1434 viewer.evalStringQuiet("background [" + col.getRed() + ","
\r
1435 + col.getGreen() + "," + col.getBlue() + "];");
\r
1436 jmolHistory(true);
\r
1440 * add structures and any known sequence associations
\r
1442 * @returns the pdb entries added to the current set.
\r
1444 public synchronized PDBEntry[] addSequenceAndChain(PDBEntry[] pdbe,
\r
1445 SequenceI[][] seq, String[][] chns)
\r
1448 Vector v = new Vector();
\r
1449 Vector rtn = new Vector();
\r
1450 for (int i = 0; i < pdbentry.length; i++)
\r
1452 v.addElement(pdbentry[i]);
\r
1454 for (int i = 0; i < pdbe.length; i++)
\r
1456 int r = v.indexOf(pdbe[i]);
\r
1457 if (r == -1 || r >= pdbentry.length)
\r
1459 rtn.addElement(new int[]
\r
1461 v.addElement(pdbe[i]);
\r
1465 // just make sure the sequence/chain entries are all up to date
\r
1466 addSequenceAndChain(r, seq[i], chns[i]);
\r
1469 pdbe = new PDBEntry[v.size()];
\r
1472 if (rtn.size() > 0)
\r
1474 // expand the tied seuqence[] and string[] arrays
\r
1475 SequenceI[][] sqs = new SequenceI[pdbentry.length][];
\r
1476 String[][] sch = new String[pdbentry.length][];
\r
1477 System.arraycopy(sequence, 0, sqs, 0, sequence.length);
\r
1478 System.arraycopy(chains, 0, sch, 0, this.chains.length);
\r
1481 pdbe = new PDBEntry[rtn.size()];
\r
1482 for (int r = 0; r < pdbe.length; r++)
\r
1484 int[] stri = ((int[]) rtn.elementAt(r));
\r
1485 // record the pdb file as a new addition
\r
1486 pdbe[r] = pdbentry[stri[0]];
\r
1487 // and add the new sequence/chain entries
\r
1488 addSequenceAndChain(stri[0], seq[stri[1]], chns[stri[1]]);
\r
1498 public void addSequence(int pe, SequenceI[] seq)
\r
1500 // add sequences to the pe'th pdbentry's seuqence set.
\r
1501 addSequenceAndChain(pe, seq, null);
\r
1504 private void addSequenceAndChain(int pe, SequenceI[] seq, String[] tchain)
\r
1506 if (pe < 0 || pe >= pdbentry.length)
\r
1509 "Implementation error - no corresponding pdbentry (for index "
\r
1510 + pe + ") to add sequences mappings to");
\r
1512 final String nullChain = "TheNullChain";
\r
1513 Vector s = new Vector();
\r
1514 Vector c = new Vector();
\r
1515 if (chains == null)
\r
1517 chains = new String[pdbentry.length][];
\r
1519 if (sequence[pe] != null)
\r
1521 for (int i = 0; i < sequence[pe].length; i++)
\r
1523 s.addElement(sequence[pe][i]);
\r
1524 if (chains[pe] != null)
\r
1526 if (i < chains[pe].length)
\r
1528 c.addElement(chains[pe][i]);
\r
1532 c.addElement(nullChain);
\r
1537 if (tchain != null && tchain.length > 0)
\r
1539 c.addElement(nullChain);
\r
1544 for (int i = 0; i < seq.length; i++)
\r
1546 if (!s.contains(seq[i]))
\r
1548 s.addElement(seq[i]);
\r
1549 if (tchain != null && i < tchain.length)
\r
1551 c.addElement(tchain[i] == null ? nullChain : tchain[i]);
\r
1555 SequenceI[] tmp = new SequenceI[s.size()];
\r
1557 sequence[pe] = tmp;
\r
1560 String[] tch = new String[c.size()];
\r
1562 for (int i = 0; i < tch.length; i++)
\r
1564 if (tch[i] == nullChain)
\r
1573 chains[pe] = null;
\r
1579 * @return text report of alignment between pdbfile and any associated alignment sequences
\r
1581 public String printMapping(String pdbfile)
\r
1583 return ssm.printMapping(pdbfile);
\r
1586 public void resizeInnerPanel(String data)
\r
1588 // Jalview doesn't honour resize panel requests
\r