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