correct initing of JmolViewer to avoid security exception in unsigned applet moed
[jalview.git] / src / jalview / ext / jmol / JalviewJmolBinding.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5)
3  * Copyright (C) 2010 J Procter, AM Waterhouse, 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 java.io.File;
21 import java.net.URL;
22 import java.util.*;
23 import java.applet.Applet;
24 import java.awt.*;
25 import java.awt.event.*;
26
27 import jalview.api.FeatureRenderer;
28 import jalview.api.SequenceRenderer;
29 import jalview.api.SequenceStructureBinding;
30 import jalview.datamodel.*;
31 import jalview.structure.*;
32 import jalview.io.*;
33
34 import org.jmol.api.*;
35 import org.jmol.adapter.smarter.SmarterJmolAdapter;
36
37 import org.jmol.popup.*;
38 import org.jmol.viewer.JmolConstants;
39
40 import jalview.schemes.*;
41
42 public abstract class JalviewJmolBinding implements StructureListener,
43         JmolStatusListener, SequenceStructureBinding, JmolSelectionListener
44
45 {
46   /**
47    * set if Jmol state is being restored from some source - instructs binding
48    * not to apply default display style when structure set is updated for first
49    * time.
50    */
51   private boolean loadingFromArchive = false;
52
53   /**
54    * state flag used to check if the Jmol viewer's paint method can be called
55    */
56   private boolean finishedInit = false;
57
58   public boolean isFinishedInit()
59   {
60     return finishedInit;
61   }
62
63   public void setFinishedInit(boolean finishedInit)
64   {
65     this.finishedInit = finishedInit;
66   }
67
68   boolean allChainsSelected = false;
69
70   /**
71    * when true, try to search the associated datamodel for sequences that are
72    * associated with any unknown structures in the Jmol view.
73    */
74   private boolean associateNewStructs = false;
75
76   Vector atomsPicked = new Vector();
77
78   public Vector chainNames;
79
80   String[] chains;
81
82   boolean colourBySequence = true;
83
84   StringBuffer eval = new StringBuffer();
85
86   public String fileLoadingError;
87
88   /**
89    * the default or current model displayed if the model cannot be identified
90    * from the selection message
91    */
92   int frameNo = 0;
93
94   protected JmolPopup jmolpopup;
95
96   String lastCommand;
97
98   String lastMessage;
99
100   boolean loadedInline;
101
102   /**
103    * current set of model filenames loaded in the Jmol instance
104    */
105   String[] modelFileNames = null;
106
107   public PDBEntry[] pdbentry;
108
109   /**
110    * datasource protocol for access to PDBEntry
111    */
112   String protocol = null;
113
114   StringBuffer resetLastRes = new StringBuffer();
115
116   public SequenceI[] sequence;
117
118   StructureSelectionManager ssm;
119
120   public JmolViewer viewer;
121
122   public JalviewJmolBinding(PDBEntry[] pdbentry, SequenceI[] seq,
123           String[] chains, String protocol)
124   {
125     this.sequence = seq;
126     this.chains = chains;
127     this.pdbentry = pdbentry;
128     this.protocol = protocol;
129
130     /*
131      * viewer = JmolViewer.allocateViewer(renderPanel, new SmarterJmolAdapter(),
132      * "jalviewJmol", ap.av.applet .getDocumentBase(),
133      * ap.av.applet.getCodeBase(), "", this);
134      * 
135      * jmolpopup = JmolPopup.newJmolPopup(viewer, true, "Jmol", true);
136      */
137   }
138
139   public JalviewJmolBinding(JmolViewer viewer2)
140   {
141     viewer = viewer2;
142     viewer.setJmolStatusListener(this);
143     viewer.addSelectionListener(this);
144   }
145
146   /**
147    * construct a title string for the viewer window based on the data jalview
148    * knows about
149    * 
150    * @return
151    */
152   public String getViewerTitle()
153   {
154     if (sequence == null || pdbentry == null || sequence.length < 1
155             || pdbentry.length < 1)
156     {
157       return ("Jalview Jmol Window");
158     }
159     StringBuffer title = new StringBuffer(sequence[0].getName() + ":"
160             + pdbentry[0].getId());
161
162     if (pdbentry[0].getProperty() != null)
163     {
164       if (pdbentry[0].getProperty().get("method") != null)
165       {
166         title.append(" Method: ");
167         title.append(pdbentry[0].getProperty().get("method"));
168       }
169       if (pdbentry[0].getProperty().get("chains") != null)
170       {
171         title.append(" Chain:");
172         title.append(pdbentry[0].getProperty().get("chains"));
173       }
174     }
175     return title.toString();
176   }
177
178   /**
179    * prepare the view for a given set of models/chains. chainList contains
180    * strings of the form 'pdbfilename:Chaincode'
181    * 
182    * @param chainList
183    *          list of chains to make visible
184    */
185   public void centerViewer(Vector chainList)
186   {
187     StringBuffer cmd = new StringBuffer();
188     String lbl;
189     int mlength, p;
190     for (int i = 0, iSize = chainList.size(); i < iSize; i++)
191     {
192       mlength = 0;
193       lbl = (String) chainList.elementAt(i);
194       do
195       {
196         p = mlength;
197         mlength = lbl.indexOf(":", p);
198       } while (p < mlength && mlength < (lbl.length() - 2));
199       cmd.append(":" + lbl.substring(mlength + 1) + " /"
200               + getModelNum(lbl.substring(0, mlength)) + " or ");
201     }
202     if (cmd.length() > 0)
203       cmd.setLength(cmd.length() - 4);
204     evalStateCommand("select *;restrict " + cmd + ";cartoon;center " + cmd);
205   }
206
207   public void closeViewer()
208   {
209     viewer.setModeMouse(org.jmol.viewer.JmolConstants.MOUSE_NONE);
210     // remove listeners for all structures in viewer
211     StructureSelectionManager.getStructureSelectionManager()
212             .removeStructureViewerListener(this, this.getPdbFile());
213     // and shut down jmol
214     viewer.evalStringQuiet("zap");
215     viewer.setJmolStatusListener(null);
216     lastCommand = null;
217     viewer = null;
218   }
219
220   public void colourByChain()
221   {
222     colourBySequence = false;
223     evalStateCommand("select *;color chain");
224   }
225
226   public void colourByCharge()
227   {
228     colourBySequence = false;
229     evalStateCommand("select *;color white;select ASP,GLU;color red;"
230             + "select LYS,ARG;color blue;select CYS;color yellow");
231   }
232
233   /**
234    * superpose the structures associated with sequences in the alignment
235    * according to their corresponding positions.
236    */
237   public void superposeStructures(AlignmentI alignment)
238   {
239     String[] files = getPdbFile();
240
241     StringBuffer command = new StringBuffer();
242     boolean matched[] = new boolean[alignment.getWidth()];
243     String commonpositions[][] = new String[files.length][alignment
244             .getWidth()];
245     for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
246     {
247       StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
248
249       if (mapping == null || mapping.length < 1)
250         continue;
251
252       int lastPos = -1;
253       for (int s = 0; s < sequence.length; s++)
254       {
255         for (int sp, m = 0; m < mapping.length; m++)
256         {
257           if (mapping[m].getSequence() == sequence[s]
258                   && (sp = alignment.findIndex(sequence[s])) > -1)
259           {
260             SequenceI asp = alignment.getSequenceAt(sp);
261             for (int r = 0; r < asp.getLength(); r++)
262             {
263               // no mapping to gaps in sequence
264               if (jalview.util.Comparison.isGap(asp.getCharAt(r)))
265               {
266                 matched[r] = false; // exclude from common set
267                 continue;
268               }
269               int pos = mapping[m].getPDBResNum(asp.findPosition(r));
270
271               if (pos < 1 || pos == lastPos)
272                 continue;
273
274               lastPos = pos;
275
276               commonpositions[m][r] = (mapping[m].getChain() != " " ? ":"
277                       + mapping[m].getChain() : "")
278                       + "/" + (pdbfnum + 1) + ".1";
279             }
280             break;
281           }
282         }
283       }
284     }
285     command.append("select ");
286     // form the matched pair selection strings
287     String sep = "";
288     for (int r = 0; r < matched.length; r++)
289     {
290       if (matched[r])
291       {
292         command.append(sep);
293         command.append("(");
294         for (int s = 0; s < commonpositions.length; s++)
295         {
296           if (s > 0)
297           {
298             command.append(" | ");
299           }
300           command.append(commonpositions[s][r]);
301         }
302         command.append(")");
303         sep = " | ";
304       }
305     }
306     evalStateCommand(command.toString());
307   }
308
309   public void evalStateCommand(String command)
310   {
311     jmolHistory(false);
312     if (lastCommand == null || !lastCommand.equals(command))
313     {
314       viewer.evalStringQuiet(command + "\n");
315     }
316     jmolHistory(true);
317     lastCommand = command;
318   }
319
320   /**
321    * colour any structures associated with sequences in the given alignment
322    * using the getFeatureRenderer() and getSequenceRenderer() renderers but only
323    * if colourBySequence is enabled.
324    */
325   public void colourBySequence(boolean showFeatures, AlignmentI alignment)
326   {
327     if (!colourBySequence)
328       return;
329     if (ssm == null)
330     {
331       return;
332     }
333     String[] files = getPdbFile();
334     SequenceRenderer sr = getSequenceRenderer();
335
336     FeatureRenderer fr = null;
337     if (showFeatures)
338     {
339       fr = getFeatureRenderer();
340     }
341
342     StringBuffer command = new StringBuffer();
343
344     for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
345     {
346       StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
347
348       if (mapping == null || mapping.length < 1)
349         continue;
350
351       int lastPos = -1;
352       for (int s = 0; s < sequence.length; s++)
353       {
354         for (int sp, m = 0; m < mapping.length; m++)
355         {
356           if (mapping[m].getSequence() == sequence[s]
357                   && (sp = alignment.findIndex(sequence[s])) > -1)
358           {
359             SequenceI asp = alignment.getSequenceAt(sp);
360             for (int r = 0; r < asp.getLength(); r++)
361             {
362               // no mapping to gaps in sequence
363               if (jalview.util.Comparison.isGap(asp.getCharAt(r)))
364               {
365                 continue;
366               }
367               int pos = mapping[m].getPDBResNum(asp.findPosition(r));
368
369               if (pos < 1 || pos == lastPos)
370                 continue;
371
372               lastPos = pos;
373
374               Color col = sr.getResidueBoxColour(sequence[s], r);
375
376               if (showFeatures)
377                 col = fr.findFeatureColour(col, sequence[s], r);
378               String newSelcom = (mapping[m].getChain() != " " ? ":"
379                       + mapping[m].getChain() : "")
380                       + "/"
381                       + (pdbfnum + 1)
382                       + ".1"
383                       + ";color["
384                       + col.getRed()
385                       + ","
386                       + col.getGreen()
387                       + ","
388                       + col.getBlue() + "]";
389               if (command.toString().endsWith(newSelcom))
390               {
391                 command = condenseCommand(command.toString(), pos);
392                 continue;
393               }
394               // TODO: deal with case when buffer is too large for Jmol to parse
395               // - execute command and flush
396
397               command.append(";select " + pos);
398               command.append(newSelcom);
399             }
400             break;
401           }
402         }
403       }
404     }
405     evalStateCommand(command.toString());
406   }
407
408   public boolean isColourBySequence()
409   {
410     return colourBySequence;
411   }
412
413   public void setColourBySequence(boolean colourBySequence)
414   {
415     this.colourBySequence = colourBySequence;
416   }
417
418   StringBuffer condenseCommand(String command, int pos)
419   {
420
421     StringBuffer sb = new StringBuffer(command.substring(0,
422             command.lastIndexOf("select") + 7));
423
424     command = command.substring(sb.length());
425
426     String start;
427
428     if (command.indexOf("-") > -1)
429     {
430       start = command.substring(0, command.indexOf("-"));
431     }
432     else
433     {
434       start = command.substring(0, command.indexOf(":"));
435     }
436
437     sb.append(start + "-" + pos + command.substring(command.indexOf(":")));
438
439     return sb;
440   }
441
442   public void createImage(String file, String type, int quality)
443   {
444     System.out.println("JMOL CREATE IMAGE");
445   }
446
447   public String createImage(String fileName, String type,
448           Object textOrBytes, int quality)
449   {
450     System.out.println("JMOL CREATE IMAGE");
451     return null;
452   }
453
454   public String eval(String strEval)
455   {
456     // System.out.println(strEval);
457     // "# 'eval' is implemented only for the applet.";
458     return null;
459   }
460
461   // End StructureListener
462   // //////////////////////////
463
464   public float[][] functionXY(String functionName, int x, int y)
465   {
466     return null;
467   }
468
469   public float[][][] functionXYZ(String functionName, int nx, int ny, int nz)
470   {
471     // TODO Auto-generated method stub
472     return null;
473   }
474
475   public Color getColour(int atomIndex, int pdbResNum, String chain,
476           String pdbfile)
477   {
478     if (getModelNum(pdbfile) < 0)
479       return null;
480     // TODO: verify atomIndex is selecting correct model.
481     return new Color(viewer.getAtomArgb(atomIndex));
482   }
483
484   /**
485    * returns the current featureRenderer that should be used to colour the
486    * structures
487    * 
488    * @return
489    */
490   public abstract FeatureRenderer getFeatureRenderer();
491
492   /**
493    * instruct the Jalview binding to update the pdbentries vector if necessary
494    * prior to matching the jmol view's contents to the list of structure files
495    * Jalview knows about.
496    */
497   public abstract void refreshPdbEntries();
498
499   private int getModelNum(String modelFileName)
500   {
501     String[] mfn = getPdbFile();
502     if (mfn == null)
503     {
504       return -1;
505     }
506     for (int i = 0; i < mfn.length; i++)
507     {
508       if (mfn[i].equalsIgnoreCase(modelFileName))
509         return i;
510     }
511     return -1;
512   }
513
514   // ////////////////////////////////
515   // /StructureListener
516   public String[] getPdbFile()
517   {
518     if (modelFileNames == null)
519     {
520       String mset[] = new String[viewer.getModelCount()];
521       for (int i = 0; i < mset.length; i++)
522       {
523         mset[i] = viewer.getModelFileName(i);
524       }
525       modelFileNames = mset;
526     }
527     return modelFileNames;
528   }
529
530   /**
531    * map from string to applet
532    */
533   public Map getRegistryInfo()
534   {
535     // TODO Auto-generated method stub
536     return null;
537   }
538
539   /**
540    * returns the current sequenceRenderer that should be used to colour the
541    * structures
542    * 
543    * @return
544    */
545   public abstract SequenceRenderer getSequenceRenderer();
546
547   // ///////////////////////////////
548   // JmolStatusListener
549
550   public void handlePopupMenu(int x, int y)
551   {
552     jmolpopup.show(x, y);
553   }
554
555   // jmol/ssm only
556   public void highlightAtom(int atomIndex, int pdbResNum, String chain,
557           String pdbfile)
558   {
559     if (modelFileNames == null)
560     {
561       return;
562     }
563
564     // look up file model number for this pdbfile
565     int mdlNum = 0;
566     String fn;
567     // may need to adjust for URLencoding here - we don't worry about that yet.
568     while (mdlNum < modelFileNames.length
569             && !pdbfile.equals(modelFileNames[mdlNum]))
570     {
571       // System.out.println("nomatch:"+pdbfile+"\nmodelfn:"+fn);
572       mdlNum++;
573     }
574     if (mdlNum == modelFileNames.length)
575     {
576       return;
577     }
578
579     jmolHistory(false);
580     // if (!pdbfile.equals(pdbentry.getFile()))
581     // return;
582     if (resetLastRes.length() > 0)
583     {
584       viewer.evalStringQuiet(resetLastRes.toString());
585     }
586
587     eval.setLength(0);
588     eval.append("select " + pdbResNum); // +modelNum
589
590     resetLastRes.setLength(0);
591     resetLastRes.append("select " + pdbResNum); // +modelNum
592
593     eval.append(":");
594     resetLastRes.append(":");
595     if (!chain.equals(" "))
596     {
597       eval.append(chain);
598       resetLastRes.append(chain);
599     }
600     {
601       eval.append(" /" + (mdlNum + 1));
602       resetLastRes.append("/" + (mdlNum + 1));
603     }
604     eval.append(";wireframe 100;" + eval.toString() + " and not hetero;");
605
606     resetLastRes.append(";wireframe 0;" + resetLastRes.toString()
607             + " and not hetero; spacefill 0;");
608
609     eval.append("spacefill 200;select none");
610
611     viewer.evalStringQuiet(eval.toString());
612     jmolHistory(true);
613
614   }
615
616   boolean debug = true;
617
618   private void jmolHistory(boolean enable)
619   {
620     viewer.evalStringQuiet("History " + ((debug || enable) ? "on" : "off"));
621   }
622
623   public void loadInline(String string)
624   {
625     loadedInline = true;
626     viewer.openStringInline(string);
627   }
628
629   public void mouseOverStructure(int atomIndex, String strInfo)
630   {
631     int pdbResNum;
632     int mdlSep = strInfo.indexOf("/");
633     int chainSeparator = strInfo.indexOf(":"), chainSeparator1 = -1;
634
635     if (chainSeparator == -1)
636     {
637       chainSeparator = strInfo.indexOf(".");
638       if (mdlSep > -1 && mdlSep < chainSeparator)
639       {
640         chainSeparator1 = chainSeparator;
641         chainSeparator = mdlSep;
642       }
643     }
644     pdbResNum = Integer.parseInt(strInfo.substring(
645             strInfo.indexOf("]") + 1, chainSeparator));
646
647     String chainId;
648
649     if (strInfo.indexOf(":") > -1)
650       chainId = strInfo.substring(strInfo.indexOf(":") + 1,
651               strInfo.indexOf("."));
652     else
653     {
654       chainId = " ";
655     }
656
657     String pdbfilename = modelFileNames[frameNo]; // default is first or current
658     // model
659     if (mdlSep > -1)
660     {
661       if (chainSeparator1 == -1)
662       {
663         chainSeparator1 = strInfo.indexOf(".", mdlSep);
664       }
665       String mdlId = (chainSeparator1 > -1) ? strInfo.substring(mdlSep + 1,
666               chainSeparator1) : strInfo.substring(mdlSep + 1);
667       try
668       {
669         // recover PDB filename for the model hovered over.
670         pdbfilename = viewer
671                 .getModelFileName(new Integer(mdlId).intValue() - 1);
672       } catch (Exception e)
673       {
674       }
675       ;
676     }
677     if (lastMessage == null || !lastMessage.equals(strInfo))
678       ssm.mouseOverStructure(pdbResNum, chainId, pdbfilename);
679
680     lastMessage = strInfo;
681   }
682
683   public void notifyAtomHovered(int atomIndex, String strInfo, String data)
684   {
685     if (data != null)
686     {
687       System.err.println("Ignoring additional hover info: " + data
688               + " (other info: '" + strInfo + "' pos " + atomIndex + ")");
689     }
690     mouseOverStructure(atomIndex, strInfo);
691   }
692
693   /*
694    * { if (history != null && strStatus != null &&
695    * !strStatus.equals("Script completed")) { history.append("\n" + strStatus);
696    * } }
697    */
698
699   public void notifyAtomPicked(int atomIndex, String strInfo, String strData)
700   {
701     /**
702      * this implements the toggle label behaviour copied from the original
703      * structure viewer, MCView
704      */
705     if (strData != null)
706     {
707       System.err.println("Ignoring additional pick data string " + strData);
708     }
709     int chainSeparator = strInfo.indexOf(":");
710     int p = 0;
711     if (chainSeparator == -1)
712       chainSeparator = strInfo.indexOf(".");
713
714     String picked = strInfo.substring(strInfo.indexOf("]") + 1,
715             chainSeparator);
716     String mdlString = "";
717     if ((p = strInfo.indexOf(":")) > -1)
718       picked += strInfo.substring(p + 1, strInfo.indexOf("."));
719
720     if ((p = strInfo.indexOf("/")) > -1)
721     {
722       mdlString += strInfo.substring(p, strInfo.indexOf(" #"));
723     }
724     picked = "((" + picked + ".CA" + mdlString + ")|(" + picked + ".P"
725             + mdlString + "))";
726     jmolHistory(false);
727
728     if (!atomsPicked.contains(picked))
729     {
730       viewer.evalStringQuiet("select " + picked + ";label %n %r:%c");
731       atomsPicked.addElement(picked);
732     }
733     else
734     {
735       viewer.evalString("select " + picked + ";label off");
736       atomsPicked.removeElement(picked);
737     }
738     jmolHistory(true);
739     // TODO: in application this happens
740     //
741     // if (scriptWindow != null)
742     // {
743     // scriptWindow.sendConsoleMessage(strInfo);
744     // scriptWindow.sendConsoleMessage("\n");
745     // }
746
747   }
748
749   public void notifyCallback(int type, Object[] data)
750   {
751     try
752     {
753       switch (type)
754       {
755       case JmolConstants.CALLBACK_LOADSTRUCT:
756         notifyFileLoaded((String) data[1], (String) data[2],
757                 (String) data[3], (String) data[4],
758                 ((Integer) data[5]).intValue());
759
760         break;
761       case JmolConstants.CALLBACK_PICK:
762         notifyAtomPicked(((Integer) data[2]).intValue(), (String) data[1],
763                 (String) data[0]);
764         // also highlight in alignment
765       case JmolConstants.CALLBACK_HOVER:
766         notifyAtomHovered(((Integer) data[2]).intValue(), (String) data[1],
767                 (String) data[0]);
768         break;
769       case JmolConstants.CALLBACK_SCRIPT:
770         notifyScriptTermination((String) data[2],
771                 ((Integer) data[3]).intValue());
772         break;
773       case JmolConstants.CALLBACK_ECHO:
774         sendConsoleEcho((String) data[1]);
775         break;
776       case JmolConstants.CALLBACK_MESSAGE:
777         sendConsoleMessage((data == null) ? ((String) null)
778                 : (String) data[1]);
779         break;
780       case JmolConstants.CALLBACK_ERROR:
781         // System.err.println("Ignoring error callback.");
782         break;
783       case JmolConstants.CALLBACK_SYNC:
784       case JmolConstants.CALLBACK_RESIZE:
785         refreshGUI();
786         break;
787       case JmolConstants.CALLBACK_MEASURE:
788
789       case JmolConstants.CALLBACK_CLICK:
790
791       default:
792         System.err.println("Unhandled callback " + type + " "
793                 + data[1].toString());
794         break;
795       }
796     } catch (Exception e)
797     {
798       System.err.println("Squashed Jmol callback handler error:");
799       e.printStackTrace();
800     }
801   }
802
803   public boolean notifyEnabled(int callbackPick)
804   {
805     switch (callbackPick)
806     {
807     case JmolConstants.CALLBACK_ECHO:
808     case JmolConstants.CALLBACK_LOADSTRUCT:
809     case JmolConstants.CALLBACK_MEASURE:
810     case JmolConstants.CALLBACK_MESSAGE:
811     case JmolConstants.CALLBACK_PICK:
812     case JmolConstants.CALLBACK_SCRIPT:
813     case JmolConstants.CALLBACK_HOVER:
814     case JmolConstants.CALLBACK_ERROR:
815       return true;
816     case JmolConstants.CALLBACK_RESIZE:
817     case JmolConstants.CALLBACK_SYNC:
818     case JmolConstants.CALLBACK_CLICK:
819     case JmolConstants.CALLBACK_ANIMFRAME:
820     case JmolConstants.CALLBACK_MINIMIZATION:
821     }
822     return false;
823   }
824
825   public void notifyFileLoaded(String fullPathName, String fileName2,
826           String modelName, String errorMsg, int modelParts)
827   {
828     if (errorMsg != null)
829     {
830       fileLoadingError = errorMsg;
831       refreshGUI();
832       return;
833     }
834     // the rest of this routine ignores the arguments, and simply interrogates
835     // the Jmol view to find out what structures it contains, and adds them to
836     // the structure selection manager.
837     fileLoadingError = null;
838     String[] oldmodels = modelFileNames;
839     modelFileNames = null;
840     chainNames = new Vector();
841     boolean notifyLoaded = false;
842     String[] modelfilenames = getPdbFile();
843     ssm = StructureSelectionManager.getStructureSelectionManager();
844     // first check if we've lost any structures
845     if (oldmodels != null && oldmodels.length > 0)
846     {
847       int oldm = 0;
848       for (int i = 0; i < oldmodels.length; i++)
849       {
850         for (int n = 0; n < modelfilenames.length; n++)
851         {
852           if (modelfilenames[n] == oldmodels[i])
853           {
854             oldmodels[i] = null;
855             break;
856           }
857         }
858         if (oldmodels[i] != null)
859         {
860           oldm++;
861         }
862       }
863       if (oldm > 0)
864       {
865         String[] oldmfn = new String[oldm];
866         oldm = 0;
867         for (int i = 0; i < oldmodels.length; i++)
868         {
869           if (oldmodels[i] != null)
870           {
871             oldmfn[oldm++] = oldmodels[i];
872           }
873         }
874         // deregister the Jmol instance for these structures - we'll add
875         // ourselves again at the end for the current structure set.
876         ssm.removeStructureViewerListener(this, oldmfn);
877       }
878     }
879     refreshPdbEntries();
880     for (int modelnum = 0; modelnum < modelfilenames.length; modelnum++)
881     {
882       String fileName = modelfilenames[modelnum];
883       if (fileName != null)
884       {
885         boolean foundEntry = false;
886         // search pdbentries and sequences to find correct pdbentry and
887         // sequence[] pair for this filename
888         if (pdbentry != null)
889         {
890           for (int pe = 0; pe < pdbentry.length; pe++)
891           {
892             if (pdbentry[pe].getFile().equals(fileName))
893             {
894               foundEntry = true;
895               MCview.PDBfile pdb;
896               if (loadedInline)
897               {
898                 // TODO: replace with getData ?
899                 pdb = ssm.setMapping(sequence, chains,
900                         pdbentry[pe].getFile(), AppletFormatAdapter.PASTE);
901                 pdbentry[pe].setFile("INLINE" + pdb.id);
902               }
903               else
904               {
905                 // TODO: Jmol can in principle retrieve from CLASSLOADER but
906                 // this
907                 // needs
908                 // to be tested. See mantis bug
909                 // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
910                 String protocol = AppletFormatAdapter.URL;
911                 try
912                 {
913                   File fl = new java.io.File(pdbentry[pe].getFile());
914                   if (fl.exists())
915                   {
916                     protocol = AppletFormatAdapter.FILE;
917                   }
918                 } catch (Exception e)
919                 {
920                 } catch (Error e)
921                 {
922                 }
923                 ;
924                 pdb = ssm.setMapping(sequence, chains,
925                         pdbentry[pe].getFile(), protocol);
926
927               }
928
929               pdbentry[pe].setId(pdb.id);
930
931               for (int i = 0; i < pdb.chains.size(); i++)
932               {
933                 chainNames.addElement(new String(pdb.id + ":"
934                         + ((MCview.PDBChain) pdb.chains.elementAt(i)).id));
935               }
936               notifyLoaded = true;
937             }
938           }
939         }
940         if (!foundEntry && associateNewStructs)
941         {
942           // this is a foreign pdb file that jalview doesn't know about - add
943           // it to the dataset and try to find a home - either on a matching
944           // sequence or as a new sequence.
945           String pdbcontent = viewer.getData("/" + (modelnum + 1) + ".1",
946                   "PDB");
947           // parse pdb file into a chain, etc.
948           // locate best match for pdb in associated views and add mapping to
949           // ssm
950           // if properly registered then
951           notifyLoaded = true;
952
953         }
954       }
955     }
956     // FILE LOADED OK
957     // so finally, update the jmol bits and pieces
958     if (jmolpopup != null)
959     {
960       jmolpopup.updateComputedMenus();
961     }
962     if (!isLoadingFromArchive())
963     {
964       viewer.evalStringQuiet("model 0; select backbone;restrict;cartoon;wireframe off;spacefill off");
965     }
966     setLoadingFromArchive(false);
967     // register ourselves as a listener and notify the gui that it needs to
968     // update itself.
969     ssm.addStructureViewerListener(this);
970     if (notifyLoaded)
971     {
972       FeatureRenderer fr = getFeatureRenderer();
973       if (fr != null)
974       {
975         fr.featuresAdded();
976       }
977       refreshGUI();
978     }
979   }
980
981   public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)
982   {
983     notifyAtomPicked(iatom, strMeasure, null);
984   }
985
986   public abstract void notifyScriptTermination(String strStatus,
987           int msWalltime);
988
989   /**
990    * display a message echoed from the jmol viewer
991    * 
992    * @param strEcho
993    */
994   public abstract void sendConsoleEcho(String strEcho); /*
995                                                          * { showConsole(true);
996                                                          * 
997                                                          * history.append("\n" +
998                                                          * strEcho); }
999                                                          */
1000
1001   // /End JmolStatusListener
1002   // /////////////////////////////
1003
1004   /**
1005    * @param strStatus
1006    *          status message - usually the response received after a script
1007    *          executed
1008    */
1009   public abstract void sendConsoleMessage(String strStatus);
1010
1011   public void setCallbackFunction(String callbackType,
1012           String callbackFunction)
1013   {
1014     System.err.println("Ignoring set-callback request to associate "
1015             + callbackType + " with function " + callbackFunction);
1016
1017   }
1018
1019   public void setJalviewColourScheme(ColourSchemeI cs)
1020   {
1021     colourBySequence = false;
1022
1023     if (cs == null)
1024       return;
1025
1026     String res;
1027     int index;
1028     Color col;
1029     jmolHistory(false);
1030     // TODO: Switch between nucleotide or aa selection expressions
1031     Enumeration en = ResidueProperties.aa3Hash.keys();
1032     StringBuffer command = new StringBuffer("select *;color white;");
1033     while (en.hasMoreElements())
1034     {
1035       res = en.nextElement().toString();
1036       index = ((Integer) ResidueProperties.aa3Hash.get(res)).intValue();
1037       if (index > 20)
1038         continue;
1039
1040       col = cs.findColour(ResidueProperties.aa[index].charAt(0));
1041
1042       command.append("select " + res + ";color[" + col.getRed() + ","
1043               + col.getGreen() + "," + col.getBlue() + "];");
1044     }
1045
1046     evalStateCommand(command.toString());
1047     jmolHistory(true);
1048   }
1049
1050   public void showHelp()
1051   {
1052     showUrl("http://jmol.sourceforge.net/docs/JmolUserGuide/", "jmolHelp");
1053   }
1054
1055   /**
1056    * open the URL somehow
1057    * 
1058    * @param target
1059    */
1060   public abstract void showUrl(String url, String target);
1061
1062   /**
1063    * called when the binding thinks the UI needs to be refreshed after a Jmol
1064    * state change. this could be because structures were loaded, or because an
1065    * error has occured.
1066    */
1067   public abstract void refreshGUI();
1068
1069
1070   /**
1071    * @param renderPanel
1072    * @param jmolfileio - when true will initialise jmol's file IO system (should be false in applet context)
1073    * @param htmlName
1074    * @param documentBase
1075    * @param codeBase
1076    * @param commandOptions
1077    */
1078   public void allocateViewer(Component renderPanel, boolean jmolfileio, String htmlName,
1079           URL documentBase, URL codeBase, String commandOptions)
1080   {
1081     viewer = JmolViewer.allocateViewer(renderPanel,
1082             (jmolfileio ? new SmarterJmolAdapter() : null),
1083             htmlName + ((Object) this).toString(), documentBase, codeBase,
1084             commandOptions, this);
1085   }
1086
1087   public void setLoadingFromArchive(boolean loadingFromArchive)
1088   {
1089     this.loadingFromArchive = loadingFromArchive;
1090   }
1091
1092   public boolean isLoadingFromArchive()
1093   {
1094     return loadingFromArchive;
1095   }
1096
1097   public void setBackgroundColour(java.awt.Color col)
1098   {
1099     jmolHistory(false);
1100     viewer.evalStringQuiet("background [" + col.getRed() + ","
1101             + col.getGreen() + "," + col.getBlue() + "];");
1102     jmolHistory(true);
1103   }
1104 }