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