Clover icon

jalviewX

  1. Project Clover database Wed Oct 31 2018 15:13:58 GMT
  2. Package jalview.ext.jmol

File JalviewJmolBinding.java

 

Coverage histogram

../../../img/srcFileCovDistChart5.png
40% of files have more coverage

Code metrics

194
452
54
1
1,520
1,042
194
0.43
8.37
54
3.59

Classes

Class Line # Actions
JalviewJmolBinding 64 452 194 362
0.4828571448.3%
 

Contributing tests

This file is covered by 20 tests. .

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ The Jalview Authors
4    *
5    * This file is part of Jalview.
6    *
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.
11    *
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.
16    *
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.
20    */
21    package jalview.ext.jmol;
22   
23    import jalview.api.AlignmentViewPanel;
24    import jalview.api.FeatureRenderer;
25    import jalview.api.SequenceRenderer;
26    import jalview.datamodel.AlignmentI;
27    import jalview.datamodel.HiddenColumns;
28    import jalview.datamodel.PDBEntry;
29    import jalview.datamodel.SequenceI;
30    import jalview.gui.IProgressIndicator;
31    import jalview.io.DataSourceType;
32    import jalview.io.StructureFile;
33    import jalview.schemes.ColourSchemeI;
34    import jalview.schemes.ResidueProperties;
35    import jalview.structure.AtomSpec;
36    import jalview.structure.StructureMappingcommandSet;
37    import jalview.structure.StructureSelectionManager;
38    import jalview.structures.models.AAStructureBindingModel;
39    import jalview.util.MessageManager;
40   
41    import java.awt.Color;
42    import java.awt.Container;
43    import java.awt.event.ComponentEvent;
44    import java.awt.event.ComponentListener;
45    import java.io.File;
46    import java.net.URL;
47    import java.security.AccessControlException;
48    import java.util.ArrayList;
49    import java.util.BitSet;
50    import java.util.Hashtable;
51    import java.util.List;
52    import java.util.Map;
53    import java.util.Vector;
54   
55    import org.jmol.adapter.smarter.SmarterJmolAdapter;
56    import org.jmol.api.JmolAppConsoleInterface;
57    import org.jmol.api.JmolSelectionListener;
58    import org.jmol.api.JmolStatusListener;
59    import org.jmol.api.JmolViewer;
60    import org.jmol.c.CBK;
61    import org.jmol.script.T;
62    import org.jmol.viewer.Viewer;
63   
 
64    public abstract class JalviewJmolBinding extends AAStructureBindingModel
65    implements JmolStatusListener, JmolSelectionListener,
66    ComponentListener
67    {
68    boolean allChainsSelected = false;
69   
70    /*
71    * when true, try to search the associated datamodel for sequences that are
72    * associated with any unknown structures in the Jmol view.
73    */
74    private boolean associateNewStructs = false;
75   
76    Vector<String> atomsPicked = new Vector<>();
77   
78    private List<String> chainNames;
79   
80    Hashtable<String, String> chainFile;
81   
82    /*
83    * the default or current model displayed if the model cannot be identified
84    * from the selection message
85    */
86    int frameNo = 0;
87   
88    // protected JmolGenericPopup jmolpopup; // not used - remove?
89   
90    String lastCommand;
91   
92    String lastMessage;
93   
94    boolean loadedInline;
95   
96    StringBuffer resetLastRes = new StringBuffer();
97   
98    public Viewer viewer;
99   
 
100  10 toggle public JalviewJmolBinding(StructureSelectionManager ssm,
101    PDBEntry[] pdbentry, SequenceI[][] sequenceIs,
102    DataSourceType protocol)
103    {
104  10 super(ssm, pdbentry, sequenceIs, protocol);
105    /*
106    * viewer = JmolViewer.allocateViewer(renderPanel, new SmarterJmolAdapter(),
107    * "jalviewJmol", ap.av.applet .getDocumentBase(),
108    * ap.av.applet.getCodeBase(), "", this);
109    *
110    * jmolpopup = JmolPopup.newJmolPopup(viewer, true, "Jmol", true);
111    */
112    }
113   
 
114  0 toggle public JalviewJmolBinding(StructureSelectionManager ssm,
115    SequenceI[][] seqs, Viewer theViewer)
116    {
117  0 super(ssm, seqs);
118   
119  0 viewer = theViewer;
120  0 viewer.setJmolStatusListener(this);
121  0 viewer.addSelectionListener(this);
122    }
123   
124    /**
125    * construct a title string for the viewer window based on the data jalview
126    * knows about
127    *
128    * @return
129    */
 
130  10 toggle public String getViewerTitle()
131    {
132  10 return getViewerTitle("Jmol", true);
133    }
134   
135    /**
136    * prepare the view for a given set of models/chains. chainList contains
137    * strings of the form 'pdbfilename:Chaincode'
138    *
139    * @param chainList
140    * list of chains to make visible
141    */
 
142  0 toggle public void centerViewer(Vector<String> chainList)
143    {
144  0 StringBuilder cmd = new StringBuilder(128);
145  0 int mlength, p;
146  0 for (String lbl : chainList)
147    {
148  0 mlength = 0;
149  0 do
150    {
151  0 p = mlength;
152  0 mlength = lbl.indexOf(":", p);
153  0 } while (p < mlength && mlength < (lbl.length() - 2));
154    // TODO: lookup each pdb id and recover proper model number for it.
155  0 cmd.append(":" + lbl.substring(mlength + 1) + " /"
156    + (1 + getModelNum(chainFile.get(lbl))) + " or ");
157    }
158  0 if (cmd.length() > 0)
159    {
160  0 cmd.setLength(cmd.length() - 4);
161    }
162  0 evalStateCommand("select *;restrict " + cmd + ";cartoon;center " + cmd);
163    }
164   
 
165  10 toggle public void closeViewer()
166    {
167    // remove listeners for all structures in viewer
168  10 getSsm().removeStructureViewerListener(this, this.getStructureFiles());
169  10 if (viewer != null)
170  10 viewer.dispose();
171  10 lastCommand = null;
172  10 viewer = null;
173  10 releaseUIResources();
174    }
175   
 
176  0 toggle @Override
177    public void colourByChain()
178    {
179  0 colourBySequence = false;
180    // TODO: colour by chain should colour each chain distinctly across all
181    // visible models
182    // TODO: http://issues.jalview.org/browse/JAL-628
183  0 evalStateCommand("select *;color chain");
184    }
185   
 
186  0 toggle @Override
187    public void colourByCharge()
188    {
189  0 colourBySequence = false;
190  0 evalStateCommand("select *;color white;select ASP,GLU;color red;"
191    + "select LYS,ARG;color blue;select CYS;color yellow");
192    }
193   
194    /**
195    * superpose the structures associated with sequences in the alignment
196    * according to their corresponding positions.
197    */
 
198  0 toggle public void superposeStructures(AlignmentI alignment)
199    {
200  0 superposeStructures(alignment, -1, null);
201    }
202   
203    /**
204    * superpose the structures associated with sequences in the alignment
205    * according to their corresponding positions. ded)
206    *
207    * @param refStructure
208    * - select which pdb file to use as reference (default is -1 - the
209    * first structure in the alignment)
210    */
 
211  0 toggle public void superposeStructures(AlignmentI alignment, int refStructure)
212    {
213  0 superposeStructures(alignment, refStructure, null);
214    }
215   
216    /**
217    * superpose the structures associated with sequences in the alignment
218    * according to their corresponding positions. ded)
219    *
220    * @param refStructure
221    * - select which pdb file to use as reference (default is -1 - the
222    * first structure in the alignment)
223    * @param hiddenCols
224    * TODO
225    */
 
226  0 toggle public void superposeStructures(AlignmentI alignment, int refStructure,
227    HiddenColumns hiddenCols)
228    {
229  0 superposeStructures(new AlignmentI[] { alignment },
230    new int[]
231    { refStructure }, new HiddenColumns[] { hiddenCols });
232    }
233   
234    /**
235    * {@inheritDoc}
236    */
 
237  1 toggle @Override
238    public String superposeStructures(AlignmentI[] _alignment,
239    int[] _refStructure, HiddenColumns[] _hiddenCols)
240    {
241  1 while (viewer.isScriptExecuting())
242    {
243  0 try
244    {
245  0 Thread.sleep(10);
246    } catch (InterruptedException i)
247    {
248    }
249    }
250   
251    /*
252    * get the distinct structure files modelled
253    * (a file with multiple chains may map to multiple sequences)
254    */
255  1 String[] files = getStructureFiles();
256  1 if (!waitForFileLoad(files))
257    {
258  0 return null;
259    }
260   
261  1 StringBuilder selectioncom = new StringBuilder(256);
262    // In principle - nSeconds specifies the speed of animation for each
263    // superposition - but is seems to behave weirdly, so we don't specify it.
264  1 String nSeconds = " ";
265  1 if (files.length > 10)
266    {
267  0 nSeconds = " 0.005 ";
268    }
269    else
270    {
271  1 nSeconds = " " + (2.0 / files.length) + " ";
272    // if (nSeconds).substring(0,5)+" ";
273    }
274   
275    // see JAL-1345 - should really automatically turn off the animation for
276    // large numbers of structures, but Jmol doesn't seem to allow that.
277    // nSeconds = " ";
278    // union of all aligned positions are collected together.
279  1 for (int a = 0; a < _alignment.length; a++)
280    {
281  1 int refStructure = _refStructure[a];
282  1 AlignmentI alignment = _alignment[a];
283  1 HiddenColumns hiddenCols = _hiddenCols[a];
284  1 if (a > 0 && selectioncom.length() > 0 && !selectioncom
285    .substring(selectioncom.length() - 1).equals("|"))
286    {
287  0 selectioncom.append("|");
288    }
289    // process this alignment
290  1 if (refStructure >= files.length)
291    {
292  0 System.err.println(
293    "Invalid reference structure value " + refStructure);
294  0 refStructure = -1;
295    }
296   
297    /*
298    * 'matched' bit j will be set for visible alignment columns j where
299    * all sequences have a residue with a mapping to the PDB structure
300    */
301  1 BitSet matched = new BitSet();
302  298 for (int m = 0; m < alignment.getWidth(); m++)
303    {
304  297 if (hiddenCols == null || hiddenCols.isVisible(m))
305    {
306  297 matched.set(m);
307    }
308    }
309   
310  1 SuperposeData[] structures = new SuperposeData[files.length];
311  3 for (int f = 0; f < files.length; f++)
312    {
313  2 structures[f] = new SuperposeData(alignment.getWidth());
314    }
315   
316    /*
317    * Calculate the superposable alignment columns ('matched'), and the
318    * corresponding structure residue positions (structures.pdbResNo)
319    */
320  1 int candidateRefStructure = findSuperposableResidues(alignment,
321    matched, structures);
322  1 if (refStructure < 0)
323    {
324    /*
325    * If no reference structure was specified, pick the first one that has
326    * a mapping in the alignment
327    */
328  1 refStructure = candidateRefStructure;
329    }
330   
331  1 String[] selcom = new String[files.length];
332  1 int nmatched = matched.cardinality();
333  1 if (nmatched < 4)
334    {
335  0 return (MessageManager.formatMessage("label.insufficient_residues",
336    nmatched));
337    }
338   
339    /*
340    * generate select statements to select regions to superimpose structures
341    */
342    {
343    // TODO extract method to construct selection statements
344  3 for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
345    {
346  2 String chainCd = ":" + structures[pdbfnum].chain;
347  2 int lpos = -1;
348  2 boolean run = false;
349  2 StringBuilder molsel = new StringBuilder();
350  2 molsel.append("{");
351   
352  2 int nextColumnMatch = matched.nextSetBit(0);
353  594 while (nextColumnMatch != -1)
354    {
355  592 int pdbResNo = structures[pdbfnum].pdbResNo[nextColumnMatch];
356  592 if (lpos != pdbResNo - 1)
357    {
358    // discontinuity
359  2 if (lpos != -1)
360    {
361  0 molsel.append(lpos);
362  0 molsel.append(chainCd);
363  0 molsel.append("|");
364    }
365  2 run = false;
366    }
367    else
368    {
369    // continuous run - and lpos >-1
370  590 if (!run)
371    {
372    // at the beginning, so add dash
373  2 molsel.append(lpos);
374  2 molsel.append("-");
375    }
376  590 run = true;
377    }
378  592 lpos = pdbResNo;
379  592 nextColumnMatch = matched.nextSetBit(nextColumnMatch + 1);
380    }
381    /*
382    * add final selection phrase
383    */
384  2 if (lpos != -1)
385    {
386  2 molsel.append(lpos);
387  2 molsel.append(chainCd);
388  2 molsel.append("}");
389    }
390  2 if (molsel.length() > 1)
391    {
392  2 selcom[pdbfnum] = molsel.toString();
393  2 selectioncom.append("((");
394  2 selectioncom.append(selcom[pdbfnum].substring(1,
395    selcom[pdbfnum].length() - 1));
396  2 selectioncom.append(" )& ");
397  2 selectioncom.append(pdbfnum + 1);
398  2 selectioncom.append(".1)");
399  2 if (pdbfnum < files.length - 1)
400    {
401  1 selectioncom.append("|");
402    }
403    }
404    else
405    {
406  0 selcom[pdbfnum] = null;
407    }
408    }
409    }
410  1 StringBuilder command = new StringBuilder(256);
411    // command.append("set spinFps 10;\n");
412   
413  3 for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
414    {
415  2 if (pdbfnum == refStructure || selcom[pdbfnum] == null
416    || selcom[refStructure] == null)
417    {
418  1 continue;
419    }
420  1 command.append("echo ");
421  1 command.append("\"Superposing (");
422  1 command.append(structures[pdbfnum].pdbId);
423  1 command.append(") against reference (");
424  1 command.append(structures[refStructure].pdbId);
425  1 command.append(")\";\ncompare " + nSeconds);
426  1 command.append("{");
427  1 command.append(Integer.toString(1 + pdbfnum));
428  1 command.append(".1} {");
429  1 command.append(Integer.toString(1 + refStructure));
430    // conformation=1 excludes alternate locations for CA (JAL-1757)
431  1 command.append(
432    ".1} SUBSET {(*.CA | *.P) and conformation=1} ATOMS ");
433   
434    // for (int s = 0; s < 2; s++)
435    // {
436    // command.append(selcom[(s == 0 ? pdbfnum : refStructure)]);
437    // }
438  1 command.append(selcom[pdbfnum]);
439  1 command.append(selcom[refStructure]);
440  1 command.append(" ROTATE TRANSLATE;\n");
441    }
442  1 if (selectioncom.length() > 0)
443    {
444    // TODO is performing selectioncom redundant here? is done later on
445    // System.out.println("Select regions:\n" + selectioncom.toString());
446  1 evalStateCommand("select *; cartoons off; backbone; select ("
447    + selectioncom.toString() + "); cartoons; ");
448    // selcom.append("; ribbons; ");
449  1 String cmdString = command.toString();
450    // System.out.println("Superimpose command(s):\n" + cmdString);
451   
452  1 evalStateCommand(cmdString);
453    }
454    }
455  0 if (selectioncom.length() > 0)
456    {// finally, mark all regions that were superposed.
457  0 if (selectioncom.substring(selectioncom.length() - 1).equals("|"))
458    {
459  0 selectioncom.setLength(selectioncom.length() - 1);
460    }
461    // System.out.println("Select regions:\n" + selectioncom.toString());
462  0 evalStateCommand("select *; cartoons off; backbone; select ("
463    + selectioncom.toString() + "); cartoons; ");
464    // evalStateCommand("select *; backbone; select "+selcom.toString()+";
465    // cartoons; center "+selcom.toString());
466    }
467   
468  0 return null;
469    }
470   
 
471  34 toggle public void evalStateCommand(String command)
472    {
473  34 jmolHistory(false);
474  34 if (lastCommand == null || !lastCommand.equals(command))
475    {
476  28 jmolScript(command + "\n");
477    }
478  34 jmolHistory(true);
479  33 lastCommand = command;
480    }
481   
482    Thread colourby = null;
483    /**
484    * Sends a set of colour commands to the structure viewer
485    *
486    * @param colourBySequenceCommands
487    */
 
488  9 toggle @Override
489    protected void colourBySequence(
490    final StructureMappingcommandSet[] colourBySequenceCommands)
491    {
492  9 if (colourby != null)
493    {
494  7 colourby.interrupt();
495  7 colourby = null;
496    }
497  9 colourby = new Thread(new Runnable()
498    {
 
499  9 toggle @Override
500    public void run()
501    {
502  9 for (StructureMappingcommandSet cpdbbyseq : colourBySequenceCommands)
503    {
504  11 for (String cbyseq : cpdbbyseq.commands)
505    {
506  11 executeWhenReady(cbyseq);
507    }
508    }
509    }
510    });
511  9 colourby.start();
512    }
513   
514    /**
515    * @param files
516    * @param sr
517    * @param viewPanel
518    * @return
519    */
 
520  9 toggle @Override
521    protected StructureMappingcommandSet[] getColourBySequenceCommands(
522    String[] files, SequenceRenderer sr, AlignmentViewPanel viewPanel)
523    {
524  9 return JmolCommands.getColourBySequenceCommand(getSsm(), files,
525    getSequence(), sr, viewPanel);
526    }
527   
528    /**
529    * @param command
530    */
 
531  11 toggle protected void executeWhenReady(String command)
532    {
533  11 evalStateCommand(command);
534    }
535   
 
536  0 toggle public void createImage(String file, String type, int quality)
537    {
538  0 System.out.println("JMOL CREATE IMAGE");
539    }
540   
 
541  0 toggle @Override
542    public String createImage(String fileName, String type,
543    Object textOrBytes, int quality)
544    {
545  0 System.out.println("JMOL CREATE IMAGE");
546  0 return null;
547    }
548   
 
549  0 toggle @Override
550    public String eval(String strEval)
551    {
552    // System.out.println(strEval);
553    // "# 'eval' is implemented only for the applet.";
554  0 return null;
555    }
556   
557    // End StructureListener
558    // //////////////////////////
559   
 
560  0 toggle @Override
561    public float[][] functionXY(String functionName, int x, int y)
562    {
563  0 return null;
564    }
565   
 
566  0 toggle @Override
567    public float[][][] functionXYZ(String functionName, int nx, int ny,
568    int nz)
569    {
570    // TODO Auto-generated method stub
571  0 return null;
572    }
573   
 
574  0 toggle public Color getColour(int atomIndex, int pdbResNum, String chain,
575    String pdbfile)
576    {
577  0 if (getModelNum(pdbfile) < 0)
578    {
579  0 return null;
580    }
581    // TODO: verify atomIndex is selecting correct model.
582    // return new Color(viewer.getAtomArgb(atomIndex)); Jmol 12.2.4
583  0 int colour = viewer.ms.at[atomIndex].atomPropertyInt(T.color);
584  0 return new Color(colour);
585    }
586   
587    /**
588    * instruct the Jalview binding to update the pdbentries vector if necessary
589    * prior to matching the jmol view's contents to the list of structure files
590    * Jalview knows about.
591    */
592    public abstract void refreshPdbEntries();
593   
 
594  0 toggle private int getModelNum(String modelFileName)
595    {
596  0 String[] mfn = getStructureFiles();
597  0 if (mfn == null)
598    {
599  0 return -1;
600    }
601  0 for (int i = 0; i < mfn.length; i++)
602    {
603  0 if (mfn[i].equalsIgnoreCase(modelFileName))
604    {
605  0 return i;
606    }
607    }
608  0 return -1;
609    }
610   
611    /**
612    * map between index of model filename returned from getPdbFile and the first
613    * index of models from this file in the viewer. Note - this is not trimmed -
614    * use getPdbFile to get number of unique models.
615    */
616    private int _modelFileNameMap[];
617   
618    // ////////////////////////////////
619    // /StructureListener
620    // @Override
 
621  0 toggle public synchronized String[] getPdbFilex()
622    {
623  0 if (viewer == null)
624    {
625  0 return new String[0];
626    }
627  0 if (modelFileNames == null)
628    {
629  0 List<String> mset = new ArrayList<>();
630  0 _modelFileNameMap = new int[viewer.ms.mc];
631  0 String m = viewer.ms.getModelFileName(0);
632  0 if (m != null)
633    {
634  0 String filePath = m;
635  0 try
636    {
637  0 filePath = new File(m).getAbsolutePath();
638    } catch (AccessControlException x)
639    {
640    // usually not allowed to do this in applet
641  0 System.err.println(
642    "jmolBinding: Using local file string from Jmol: " + m);
643    }
644  0 if (filePath.indexOf("/file:") != -1)
645    {
646    // applet path with docroot - discard as format won't match pdbfile
647  0 filePath = m;
648    }
649  0 mset.add(filePath);
650  0 _modelFileNameMap[0] = 0; // filename index for first model is always 0.
651    }
652  0 int j = 1;
653  0 for (int i = 1; i < viewer.ms.mc; i++)
654    {
655  0 m = viewer.ms.getModelFileName(i);
656  0 String filePath = m;
657  0 if (m != null)
658    {
659  0 try
660    {
661  0 filePath = new File(m).getAbsolutePath();
662    } catch (AccessControlException x)
663    {
664    // usually not allowed to do this in applet, so keep raw handle
665    // System.err.println("jmolBinding: Using local file string from
666    // Jmol: "+m);
667    }
668    }
669   
670    /*
671    * add this model unless it is read from a structure file we have
672    * already seen (example: 2MJW is an NMR structure with 10 models)
673    */
674  0 if (!mset.contains(filePath))
675    {
676  0 mset.add(filePath);
677  0 _modelFileNameMap[j] = i; // record the model index for the filename
678  0 j++;
679    }
680    }
681  0 modelFileNames = mset.toArray(new String[mset.size()]);
682    }
683  0 return modelFileNames;
684    }
685   
 
686  52 toggle @Override
687    public synchronized String[] getStructureFiles()
688    {
689  52 List<String> mset = new ArrayList<>();
690  52 if (viewer == null)
691    {
692  2 return new String[0];
693    }
694   
695  50 if (modelFileNames == null)
696    {
697  11 int modelCount = viewer.ms.mc;
698  11 String filePath = null;
699  23 for (int i = 0; i < modelCount; ++i)
700    {
701  12 filePath = viewer.ms.getModelFileName(i);
702  12 if (!mset.contains(filePath))
703    {
704  12 mset.add(filePath);
705    }
706    }
707  11 modelFileNames = mset.toArray(new String[mset.size()]);
708    }
709   
710  50 return modelFileNames;
711    }
712   
713    /**
714    * map from string to applet
715    */
 
716  0 toggle @Override
717    public Map<String, Object> getRegistryInfo()
718    {
719    // TODO Auto-generated method stub
720  0 return null;
721    }
722   
723    // ///////////////////////////////
724    // JmolStatusListener
725   
 
726  0 toggle public void handlePopupMenu(int x, int y)
727    {
728    // jmolpopup.show(x, y);
729    // jmolpopup.jpiShow(x, y);
730    }
731   
732    /**
733    * Highlight zero, one or more atoms on the structure
734    */
 
735  0 toggle @Override
736    public void highlightAtoms(List<AtomSpec> atoms)
737    {
738  0 if (atoms != null)
739    {
740  0 if (resetLastRes.length() > 0)
741    {
742  0 jmolScript(resetLastRes.toString());
743  0 resetLastRes.setLength(0);
744    }
745  0 for (AtomSpec atom : atoms)
746    {
747  0 highlightAtom(atom.getAtomIndex(), atom.getPdbResNum(),
748    atom.getChain(), atom.getPdbFile());
749    }
750    }
751    }
752   
753    // jmol/ssm only
 
754  0 toggle public void highlightAtom(int atomIndex, int pdbResNum, String chain,
755    String pdbfile)
756    {
757  0 if (modelFileNames == null)
758    {
759  0 return;
760    }
761   
762    // look up file model number for this pdbfile
763  0 int mdlNum = 0;
764    // may need to adjust for URLencoding here - we don't worry about that yet.
765  0 while (mdlNum < modelFileNames.length
766    && !pdbfile.equals(modelFileNames[mdlNum]))
767    {
768  0 mdlNum++;
769    }
770  0 if (mdlNum == modelFileNames.length)
771    {
772  0 return;
773    }
774   
775  0 jmolHistory(false);
776   
777  0 StringBuilder cmd = new StringBuilder(64);
778  0 cmd.append("select " + pdbResNum); // +modelNum
779   
780  0 resetLastRes.append("select " + pdbResNum); // +modelNum
781   
782  0 cmd.append(":");
783  0 resetLastRes.append(":");
784  0 if (!chain.equals(" "))
785    {
786  0 cmd.append(chain);
787  0 resetLastRes.append(chain);
788    }
789    {
790  0 cmd.append(" /" + (mdlNum + 1));
791  0 resetLastRes.append("/" + (mdlNum + 1));
792    }
793  0 cmd.append(";wireframe 100;" + cmd.toString() + " and not hetero;");
794   
795  0 resetLastRes.append(";wireframe 0;" + resetLastRes.toString()
796    + " and not hetero; spacefill 0;");
797   
798  0 cmd.append("spacefill 200;select none");
799   
800  0 jmolScript(cmd.toString());
801  0 jmolHistory(true);
802   
803    }
804   
805    boolean debug = true;
806   
 
807  68 toggle private void jmolHistory(boolean enable)
808    {
809  68 jmolScript("History " + ((debug || enable) ? "on" : "off"));
810    }
811   
 
812  0 toggle public void loadInline(String string)
813    {
814  0 loadedInline = true;
815    // TODO: re JAL-623
816    // viewer.loadInline(strModel, isAppend);
817    // could do this:
818    // construct fake fullPathName and fileName so we can identify the file
819    // later.
820    // Then, construct pass a reader for the string to Jmol.
821    // ((org.jmol.Viewer.Viewer) viewer).loadModelFromFile(fullPathName,
822    // fileName, null, reader, false, null, null, 0);
823  0 viewer.openStringInline(string);
824    }
825   
 
826  1 toggle public void mouseOverStructure(int atomIndex, String strInfo)
827    {
828  1 int pdbResNum;
829  1 int alocsep = strInfo.indexOf("^");
830  1 int mdlSep = strInfo.indexOf("/");
831  1 int chainSeparator = strInfo.indexOf(":"), chainSeparator1 = -1;
832   
833  1 if (chainSeparator == -1)
834    {
835  0 chainSeparator = strInfo.indexOf(".");
836  0 if (mdlSep > -1 && mdlSep < chainSeparator)
837    {
838  0 chainSeparator1 = chainSeparator;
839  0 chainSeparator = mdlSep;
840    }
841    }
842    // handle insertion codes
843  1 if (alocsep != -1)
844    {
845  0 pdbResNum = Integer.parseInt(
846    strInfo.substring(strInfo.indexOf("]") + 1, alocsep));
847   
848    }
849    else
850    {
851  1 pdbResNum = Integer.parseInt(
852    strInfo.substring(strInfo.indexOf("]") + 1, chainSeparator));
853    }
854  1 String chainId;
855   
856  1 if (strInfo.indexOf(":") > -1)
857    {
858  1 chainId = strInfo.substring(strInfo.indexOf(":") + 1,
859    strInfo.indexOf("."));
860    }
861    else
862    {
863  0 chainId = " ";
864    }
865   
866  1 String pdbfilename = modelFileNames[frameNo]; // default is first or current
867    // model
868  1 if (mdlSep > -1)
869    {
870  1 if (chainSeparator1 == -1)
871    {
872  1 chainSeparator1 = strInfo.indexOf(".", mdlSep);
873    }
874  1 String mdlId = (chainSeparator1 > -1)
875    ? strInfo.substring(mdlSep + 1, chainSeparator1)
876    : strInfo.substring(mdlSep + 1);
877  1 try
878    {
879    // recover PDB filename for the model hovered over.
880  1 int mnumber = new Integer(mdlId).intValue() - 1;
881  1 if (_modelFileNameMap != null)
882    {
883  0 int _mp = _modelFileNameMap.length - 1;
884   
885  0 while (mnumber < _modelFileNameMap[_mp])
886    {
887  0 _mp--;
888    }
889  0 pdbfilename = modelFileNames[_mp];
890    }
891    else
892    {
893  1 if (mnumber >= 0 && mnumber < modelFileNames.length)
894    {
895  1 pdbfilename = modelFileNames[mnumber];
896    }
897   
898  1 if (pdbfilename == null)
899    {
900  0 pdbfilename = new File(viewer.ms.getModelFileName(mnumber))
901    .getAbsolutePath();
902    }
903    }
904    } catch (Exception e)
905    {
906    }
907  1 ;
908    }
909  1 if (lastMessage == null || !lastMessage.equals(strInfo))
910    {
911  1 getSsm().mouseOverStructure(pdbResNum, chainId, pdbfilename);
912    }
913   
914  1 lastMessage = strInfo;
915    }
916   
 
917  0 toggle public void notifyAtomHovered(int atomIndex, String strInfo, String data)
918    {
919  0 if (data != null)
920    {
921  0 System.err.println("Ignoring additional hover info: " + data
922    + " (other info: '" + strInfo + "' pos " + atomIndex + ")");
923    }
924  0 mouseOverStructure(atomIndex, strInfo);
925    }
926   
927    /*
928    * { if (history != null && strStatus != null &&
929    * !strStatus.equals("Script completed")) { history.append("\n" + strStatus);
930    * } }
931    */
932   
 
933  0 toggle public void notifyAtomPicked(int atomIndex, String strInfo,
934    String strData)
935    {
936    /**
937    * this implements the toggle label behaviour copied from the original
938    * structure viewer, mc_view
939    */
940  0 if (strData != null)
941    {
942  0 System.err.println("Ignoring additional pick data string " + strData);
943    }
944  0 int chainSeparator = strInfo.indexOf(":");
945  0 int p = 0;
946  0 if (chainSeparator == -1)
947    {
948  0 chainSeparator = strInfo.indexOf(".");
949    }
950   
951  0 String picked = strInfo.substring(strInfo.indexOf("]") + 1,
952    chainSeparator);
953  0 String mdlString = "";
954  0 if ((p = strInfo.indexOf(":")) > -1)
955    {
956  0 picked += strInfo.substring(p, strInfo.indexOf("."));
957    }
958   
959  0 if ((p = strInfo.indexOf("/")) > -1)
960    {
961  0 mdlString += strInfo.substring(p, strInfo.indexOf(" #"));
962    }
963  0 picked = "((" + picked + ".CA" + mdlString + ")|(" + picked + ".P"
964    + mdlString + "))";
965  0 jmolHistory(false);
966   
967  0 if (!atomsPicked.contains(picked))
968    {
969  0 jmolScript("select " + picked + ";label %n %r:%c");
970  0 atomsPicked.addElement(picked);
971    }
972    else
973    {
974  0 viewer.evalString("select " + picked + ";label off");
975  0 atomsPicked.removeElement(picked);
976    }
977  0 jmolHistory(true);
978    // TODO: in application this happens
979    //
980    // if (scriptWindow != null)
981    // {
982    // scriptWindow.sendConsoleMessage(strInfo);
983    // scriptWindow.sendConsoleMessage("\n");
984    // }
985   
986    }
987   
 
988  359 toggle @Override
989    public void notifyCallback(CBK type, Object[] data)
990    {
991  359 try
992    {
993  359 switch (type)
994    {
995  11 case LOADSTRUCT:
996  11 notifyFileLoaded((String) data[1], (String) data[2],
997    (String) data[3], (String) data[4],
998    ((Integer) data[5]).intValue());
999   
1000  11 break;
1001  0 case PICK:
1002  0 notifyAtomPicked(((Integer) data[2]).intValue(), (String) data[1],
1003    (String) data[0]);
1004    // also highlight in alignment
1005    // deliberate fall through
1006  0 case HOVER:
1007  0 notifyAtomHovered(((Integer) data[2]).intValue(), (String) data[1],
1008    (String) data[0]);
1009  0 break;
1010  318 case SCRIPT:
1011  318 notifyScriptTermination((String) data[2],
1012    ((Integer) data[3]).intValue());
1013  318 break;
1014  22 case ECHO:
1015  22 sendConsoleEcho((String) data[1]);
1016  22 break;
1017  0 case MESSAGE:
1018  0 sendConsoleMessage(
1019  0 (data == null) ? ((String) null) : (String) data[1]);
1020  0 break;
1021  0 case ERROR:
1022    // System.err.println("Ignoring error callback.");
1023  0 break;
1024  0 case SYNC:
1025  0 case RESIZE:
1026  0 refreshGUI();
1027  0 break;
1028  8 case MEASURE:
1029   
1030  0 case CLICK:
1031  0 default:
1032  8 System.err.println(
1033    "Unhandled callback " + type + " " + data[1].toString());
1034  8 break;
1035    }
1036    } catch (Exception e)
1037    {
1038  0 System.err.println("Squashed Jmol callback handler error:");
1039  0 e.printStackTrace();
1040    }
1041    }
1042   
 
1043  403 toggle @Override
1044    public boolean notifyEnabled(CBK callbackPick)
1045    {
1046  403 switch (callbackPick)
1047    {
1048  22 case ECHO:
1049  11 case LOADSTRUCT:
1050  8 case MEASURE:
1051  0 case MESSAGE:
1052  0 case PICK:
1053  318 case SCRIPT:
1054  0 case HOVER:
1055  0 case ERROR:
1056  359 return true;
1057  44 default:
1058  44 return false;
1059    }
1060    }
1061   
1062    // incremented every time a load notification is successfully handled -
1063    // lightweight mechanism for other threads to detect when they can start
1064    // referrring to new structures.
1065    private long loadNotifiesHandled = 0;
1066   
 
1067  5 toggle public long getLoadNotifiesHandled()
1068    {
1069  5 return loadNotifiesHandled;
1070    }
1071   
 
1072  11 toggle public void notifyFileLoaded(String fullPathName, String fileName2,
1073    String modelName, String errorMsg, int modelParts)
1074    {
1075  11 if (errorMsg != null)
1076    {
1077  0 fileLoadingError = errorMsg;
1078  0 refreshGUI();
1079  0 return;
1080    }
1081    // TODO: deal sensibly with models loaded inLine:
1082    // modelName will be null, as will fullPathName.
1083   
1084    // the rest of this routine ignores the arguments, and simply interrogates
1085    // the Jmol view to find out what structures it contains, and adds them to
1086    // the structure selection manager.
1087  11 fileLoadingError = null;
1088  11 String[] oldmodels = modelFileNames;
1089  11 modelFileNames = null;
1090  11 chainNames = new ArrayList<>();
1091  11 chainFile = new Hashtable<>();
1092  11 boolean notifyLoaded = false;
1093  11 String[] modelfilenames = getStructureFiles();
1094    // first check if we've lost any structures
1095  11 if (oldmodels != null && oldmodels.length > 0)
1096    {
1097  1 int oldm = 0;
1098  2 for (int i = 0; i < oldmodels.length; i++)
1099    {
1100  1 for (int n = 0; n < modelfilenames.length; n++)
1101    {
1102  1 if (modelfilenames[n] == oldmodels[i])
1103    {
1104  1 oldmodels[i] = null;
1105  1 break;
1106    }
1107    }
1108  1 if (oldmodels[i] != null)
1109    {
1110  0 oldm++;
1111    }
1112    }
1113  1 if (oldm > 0)
1114    {
1115  0 String[] oldmfn = new String[oldm];
1116  0 oldm = 0;
1117  0 for (int i = 0; i < oldmodels.length; i++)
1118    {
1119  0 if (oldmodels[i] != null)
1120    {
1121  0 oldmfn[oldm++] = oldmodels[i];
1122    }
1123    }
1124    // deregister the Jmol instance for these structures - we'll add
1125    // ourselves again at the end for the current structure set.
1126  0 getSsm().removeStructureViewerListener(this, oldmfn);
1127    }
1128    }
1129  11 refreshPdbEntries();
1130  23 for (int modelnum = 0; modelnum < modelfilenames.length; modelnum++)
1131    {
1132  12 String fileName = modelfilenames[modelnum];
1133  12 boolean foundEntry = false;
1134  12 StructureFile pdb = null;
1135  12 String pdbfile = null;
1136    // model was probably loaded inline - so check the pdb file hashcode
1137  12 if (loadedInline)
1138    {
1139    // calculate essential attributes for the pdb data imported inline.
1140    // prolly need to resolve modelnumber properly - for now just use our
1141    // 'best guess'
1142  0 pdbfile = viewer.getData(
1143    "" + (1 + _modelFileNameMap[modelnum]) + ".0", "PDB");
1144    }
1145    // search pdbentries and sequences to find correct pdbentry for this
1146    // model
1147  26 for (int pe = 0; pe < getPdbCount(); pe++)
1148    {
1149  14 boolean matches = false;
1150  14 addSequence(pe, getSequence()[pe]);
1151  14 if (fileName == null)
1152    {
1153  0 if (false)
1154    // see JAL-623 - need method of matching pasted data up
1155    {
1156  0 pdb = getSsm().setMapping(getSequence()[pe], getChains()[pe],
1157    pdbfile, DataSourceType.PASTE,
1158    getIProgressIndicator());
1159  0 getPdbEntry(modelnum).setFile("INLINE" + pdb.getId());
1160  0 matches = true;
1161  0 foundEntry = true;
1162    }
1163    }
1164    else
1165    {
1166  14 File fl = new File(getPdbEntry(pe).getFile());
1167  14 matches = fl.equals(new File(fileName));
1168  14 if (matches)
1169    {
1170  12 foundEntry = true;
1171    // TODO: Jmol can in principle retrieve from CLASSLOADER but
1172    // this
1173    // needs
1174    // to be tested. See mantis bug
1175    // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
1176  12 DataSourceType protocol = DataSourceType.URL;
1177  12 try
1178    {
1179  12 if (fl.exists())
1180    {
1181  12 protocol = DataSourceType.FILE;
1182    }
1183    } catch (Exception e)
1184    {
1185    } catch (Error e)
1186    {
1187    }
1188    // Explicitly map to the filename used by Jmol ;
1189  12 pdb = getSsm().setMapping(getSequence()[pe], getChains()[pe],
1190    fileName, protocol, getIProgressIndicator());
1191    // pdbentry[pe].getFile(), protocol);
1192   
1193    }
1194    }
1195  14 if (matches)
1196    {
1197    // add an entry for every chain in the model
1198  33 for (int i = 0; i < pdb.getChains().size(); i++)
1199    {
1200  21 String chid = new String(
1201    pdb.getId() + ":" + pdb.getChains().elementAt(i).id);
1202  21 chainFile.put(chid, fileName);
1203  21 chainNames.add(chid);
1204    }
1205  12 notifyLoaded = true;
1206    }
1207    }
1208   
1209  12 if (!foundEntry && associateNewStructs)
1210    {
1211    // this is a foreign pdb file that jalview doesn't know about - add
1212    // it to the dataset and try to find a home - either on a matching
1213    // sequence or as a new sequence.
1214  0 String pdbcontent = viewer.getData("/" + (modelnum + 1) + ".1",
1215    "PDB");
1216    // parse pdb file into a chain, etc.
1217    // locate best match for pdb in associated views and add mapping to
1218    // ssm
1219    // if properly registered then
1220  0 notifyLoaded = true;
1221   
1222    }
1223    }
1224    // FILE LOADED OK
1225    // so finally, update the jmol bits and pieces
1226    // if (jmolpopup != null)
1227    // {
1228    // // potential for deadlock here:
1229    // // jmolpopup.updateComputedMenus();
1230    // }
1231  11 if (!isLoadingFromArchive())
1232    {
1233  11 jmolScript(
1234    "model *; select backbone;restrict;cartoon;wireframe off;spacefill off");
1235    }
1236    // register ourselves as a listener and notify the gui that it needs to
1237    // update itself.
1238  11 getSsm().addStructureViewerListener(this);
1239  11 if (notifyLoaded)
1240    {
1241  11 FeatureRenderer fr = getFeatureRenderer(null);
1242  11 if (fr != null)
1243    {
1244  1 fr.featuresAdded();
1245    }
1246  11 refreshGUI();
1247  11 loadNotifiesHandled++;
1248    }
1249  11 setLoadingFromArchive(false);
1250    }
1251   
 
1252  11 toggle @Override
1253    public List<String> getChainNames()
1254    {
1255  11 return chainNames;
1256    }
1257   
1258    protected abstract IProgressIndicator getIProgressIndicator();
1259   
 
1260  0 toggle public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)
1261    {
1262  0 notifyAtomPicked(iatom, strMeasure, null);
1263    }
1264   
1265    public abstract void notifyScriptTermination(String strStatus,
1266    int msWalltime);
1267   
1268    /**
1269    * display a message echoed from the jmol viewer
1270    *
1271    * @param strEcho
1272    */
1273    public abstract void sendConsoleEcho(String strEcho); /*
1274    * { showConsole(true);
1275    *
1276    * history.append("\n" +
1277    * strEcho); }
1278    */
1279   
1280    // /End JmolStatusListener
1281    // /////////////////////////////
1282   
1283    /**
1284    * @param strStatus
1285    * status message - usually the response received after a script
1286    * executed
1287    */
1288    public abstract void sendConsoleMessage(String strStatus);
1289   
 
1290  0 toggle @Override
1291    public void setCallbackFunction(String callbackType,
1292    String callbackFunction)
1293    {
1294  0 System.err.println("Ignoring set-callback request to associate "
1295    + callbackType + " with function " + callbackFunction);
1296   
1297    }
1298   
 
1299  0 toggle @Override
1300    public void setJalviewColourScheme(ColourSchemeI cs)
1301    {
1302  0 colourBySequence = false;
1303   
1304  0 if (cs == null)
1305    {
1306  0 return;
1307    }
1308   
1309  0 jmolHistory(false);
1310  0 StringBuilder command = new StringBuilder(128);
1311  0 command.append("select *;color white;");
1312  0 List<String> residueSet = ResidueProperties.getResidues(isNucleotide(),
1313    false);
1314  0 for (String resName : residueSet)
1315    {
1316  0 char res = resName.length() == 3
1317    ? ResidueProperties.getSingleCharacterCode(resName)
1318    : resName.charAt(0);
1319  0 Color col = cs.findColour(res, 0, null, null, 0f);
1320  0 command.append("select " + resName + ";color[" + col.getRed() + ","
1321    + col.getGreen() + "," + col.getBlue() + "];");
1322    }
1323   
1324  0 evalStateCommand(command.toString());
1325  0 jmolHistory(true);
1326    }
1327   
 
1328  0 toggle public void showHelp()
1329    {
1330  0 showUrl("http://jmol.sourceforge.net/docs/JmolUserGuide/", "jmolHelp");
1331    }
1332   
1333    /**
1334    * open the URL somehow
1335    *
1336    * @param target
1337    */
1338    public abstract void showUrl(String url, String target);
1339   
1340    /**
1341    * called when the binding thinks the UI needs to be refreshed after a Jmol
1342    * state change. this could be because structures were loaded, or because an
1343    * error has occured.
1344    */
1345    public abstract void refreshGUI();
1346   
1347    /**
1348    * called to show or hide the associated console window container.
1349    *
1350    * @param show
1351    */
1352    public abstract void showConsole(boolean show);
1353   
1354   
 
1355  92 toggle public static Viewer getJmolData(JmolParser jmolParser)
1356    {
1357  92 return (Viewer) JmolViewer.allocateViewer(null, null, null, null, null,
1358    "-x -o -n", jmolParser);
1359    }
1360   
1361    /**
1362    *
1363    *
1364    *
1365    * @param renderPanel
1366    * @param jmolfileio
1367    * - when true will initialise jmol's file IO system (should be false
1368    * in applet context)
1369    * @param htmlName
1370    * @param documentBase
1371    * @param codeBase
1372    * @param commandOptions
1373    */
 
1374  0 toggle public void allocateViewer(Container renderPanel, boolean jmolfileio,
1375    String htmlName, URL documentBase, URL codeBase,
1376    String commandOptions)
1377    {
1378  0 allocateViewer(renderPanel, jmolfileio, htmlName, documentBase,
1379    codeBase, commandOptions, null, null);
1380    }
1381   
1382    /**
1383    *
1384    * @param renderPanel
1385    * @param jmolfileio
1386    * - when true will initialise jmol's file IO system (should be false
1387    * in applet context)
1388    * @param htmlName
1389    * @param documentBase
1390    * @param codeBase
1391    * @param commandOptions
1392    * @param consolePanel
1393    * - panel to contain Jmol console
1394    * @param buttonsToShow
1395    * - buttons to show on the console, in order
1396    */
 
1397  10 toggle public void allocateViewer(Container renderPanel, boolean jmolfileio,
1398    String htmlName, URL documentBase, URL codeBase,
1399    String commandOptions, final Container consolePanel,
1400    String buttonsToShow)
1401    {
1402   
1403  10 System.err.println("Allocating Jmol Viewer: " + commandOptions);
1404   
1405  10 if (commandOptions == null)
1406    {
1407  0 commandOptions = "";
1408    }
1409  10 viewer = (Viewer) JmolViewer.allocateViewer(renderPanel,
1410  10 (jmolfileio ? new SmarterJmolAdapter() : null),
1411    htmlName + ((Object) this).toString(), documentBase, codeBase,
1412    commandOptions, this);
1413   
1414  10 viewer.setJmolStatusListener(this); // extends JmolCallbackListener
1415   
1416  10 try {
1417  10 console = createJmolConsole(consolePanel, buttonsToShow);
1418    } catch (Throwable e) {
1419  0 System.err.println("Could not create Jmol application console. " + e.getMessage());
1420  0 e.printStackTrace();
1421    }
1422  10 if (consolePanel != null)
1423    {
1424  10 consolePanel.addComponentListener(this);
1425   
1426    }
1427   
1428    }
1429   
1430    protected abstract JmolAppConsoleInterface createJmolConsole(
1431    Container consolePanel, String buttonsToShow);
1432   
1433    // BH 2018 -- Jmol console is not working due to problems with styled documents.
1434   
1435   
1436    protected org.jmol.api.JmolAppConsoleInterface console = null;
1437   
 
1438  0 toggle @Override
1439    public void setBackgroundColour(java.awt.Color col)
1440    {
1441  0 jmolHistory(false);
1442  0 jmolScript("background [" + col.getRed() + ","
1443    + col.getGreen() + "," + col.getBlue() + "];");
1444  0 jmolHistory(true);
1445    }
1446   
 
1447  107 toggle private String jmolScript(String script)
1448    {
1449   
1450  107 System.err.println(">>Jmol>> " + script);
1451   
1452  107 String s = viewer.scriptWait(script);
1453   
1454  106 System.err.println("<<Jmol<< " + s);
1455   
1456  106 return s;
1457    }
1458   
 
1459  0 toggle @Override
1460    public int[] resizeInnerPanel(String data)
1461    {
1462    // Jalview doesn't honour resize panel requests
1463  0 return null;
1464    }
1465   
1466    /**
1467    *
1468    */
 
1469  10 toggle protected void closeConsole()
1470    {
1471  10 if (console != null)
1472    {
1473  0 try
1474    {
1475  0 console.setVisible(false);
1476    } catch (Error e)
1477    {
1478    } catch (Exception x)
1479    {
1480    }
1481  0 ;
1482  0 console = null;
1483    }
1484    }
1485   
1486    /**
1487    * ComponentListener method
1488    */
 
1489  0 toggle @Override
1490    public void componentMoved(ComponentEvent e)
1491    {
1492    }
1493   
1494    /**
1495    * ComponentListener method
1496    */
 
1497  0 toggle @Override
1498    public void componentResized(ComponentEvent e)
1499    {
1500    }
1501   
1502    /**
1503    * ComponentListener method
1504    */
 
1505  0 toggle @Override
1506    public void componentShown(ComponentEvent e)
1507    {
1508  0 showConsole(true);
1509    }
1510   
1511    /**
1512    * ComponentListener method
1513    */
 
1514  0 toggle @Override
1515    public void componentHidden(ComponentEvent e)
1516    {
1517  0 showConsole(false);
1518    }
1519   
1520    }