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