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