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