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