upgrading structure/sequence binding so that a structure view can contain many pdb...
[jalview.git] / src / jalview / appletgui / AppletJmol.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.appletgui;
19
20 import java.util.*;
21 import java.awt.*;
22 import java.awt.event.*;
23
24 import jalview.api.SequenceStructureBinding;
25 import jalview.datamodel.*;
26 import jalview.structure.*;
27 import jalview.io.*;
28
29 import org.jmol.api.*;
30 import org.jmol.adapter.smarter.SmarterJmolAdapter;
31
32 import org.jmol.popup.*;
33 import org.jmol.viewer.JmolConstants;
34
35 import jalview.schemes.*;
36
37 public class AppletJmol extends EmbmenuFrame implements StructureListener,
38         JmolStatusListener, KeyListener, ActionListener, ItemListener, SequenceStructureBinding
39
40 {
41   Menu fileMenu = new Menu("File");
42
43   Menu viewMenu = new Menu("View");
44
45   Menu coloursMenu = new Menu("Colours");
46
47   Menu chainMenu = new Menu("Show Chain");
48
49   Menu helpMenu = new Menu("Help");
50
51   MenuItem mappingMenuItem = new MenuItem("View Mapping");
52
53   CheckboxMenuItem seqColour = new CheckboxMenuItem("By Sequence", true);
54
55   MenuItem chain = new MenuItem("By Chain");
56
57   MenuItem charge = new MenuItem("Charge & Cysteine");
58
59   MenuItem zappo = new MenuItem("Zappo");
60
61   MenuItem taylor = new MenuItem("Taylor");
62
63   MenuItem hydro = new MenuItem("Hydrophobicity");
64
65   MenuItem helix = new MenuItem("Helix Propensity");
66
67   MenuItem strand = new MenuItem("Strand Propensity");
68
69   MenuItem turn = new MenuItem("Turn Propensity");
70
71   MenuItem buried = new MenuItem("Buried Index");
72
73   MenuItem user = new MenuItem("User Defined Colours");
74
75   MenuItem jmolHelp = new MenuItem("Jmol Help");
76
77   JmolViewer viewer;
78
79   JmolPopup jmolpopup;
80
81   Panel scriptWindow;
82
83   TextField inputLine;
84
85   TextArea history;
86
87   SequenceI[] sequence;
88
89   String[] chains;
90
91   StructureSelectionManager ssm;
92
93   RenderPanel renderPanel;
94
95   AlignmentPanel ap;
96
97   String fileLoadingError;
98
99   boolean loadedInline;
100
101   PDBEntry pdbentry;
102
103   boolean colourBySequence = true;
104
105   Vector atomsPicked = new Vector();
106
107   /**
108    * datasource protocol for access to PDBEntry
109    */
110   String protocol = null;
111
112   public AppletJmol(PDBEntry pdbentry, SequenceI[] seq, String[] chains,
113           AlignmentPanel ap, String protocol)
114   {
115     this.ap = ap;
116     this.sequence = seq;
117     this.chains = chains;
118     this.pdbentry = pdbentry;
119     this.protocol = protocol;
120     if (pdbentry.getId() == null || pdbentry.getId().length() < 1)
121     {
122       if (protocol.equals(AppletFormatAdapter.PASTE))
123       {
124         pdbentry.setId("PASTED PDB"
125                 + (chains == null ? "_" : chains.toString()));
126       }
127       else
128       {
129         pdbentry.setId(pdbentry.getFile());
130       }
131     }
132
133     if (jalview.bin.JalviewLite.debug)
134     {
135       System.err
136               .println("AppletJmol: PDB ID is '" + pdbentry.getId() + "'");
137     }
138
139     String alreadyMapped = StructureSelectionManager
140             .getStructureSelectionManager().alreadyMappedToFile(
141                     pdbentry.getId());
142     MCview.PDBfile reader = null;
143     if (alreadyMapped != null)
144     {
145       reader = StructureSelectionManager.getStructureSelectionManager()
146               .setMapping(seq, chains, pdbentry.getFile(), protocol);
147       // PROMPT USER HERE TO ADD TO NEW OR EXISTING VIEW?
148       // FOR NOW, LETS JUST OPEN A NEW WINDOW
149     }
150     MenuBar menuBar = new MenuBar();
151     menuBar.add(fileMenu);
152     fileMenu.add(mappingMenuItem);
153     menuBar.add(viewMenu);
154     mappingMenuItem.addActionListener(this);
155     viewMenu.add(chainMenu);
156     menuBar.add(coloursMenu);
157     menuBar.add(helpMenu);
158
159     charge.addActionListener(this);
160     hydro.addActionListener(this);
161     chain.addActionListener(this);
162     seqColour.addItemListener(this);
163     zappo.addActionListener(this);
164     taylor.addActionListener(this);
165     helix.addActionListener(this);
166     strand.addActionListener(this);
167     turn.addActionListener(this);
168     buried.addActionListener(this);
169     user.addActionListener(this);
170
171     jmolHelp.addActionListener(this);
172
173     coloursMenu.add(seqColour);
174     coloursMenu.add(chain);
175     coloursMenu.add(charge);
176     coloursMenu.add(zappo);
177     coloursMenu.add(taylor);
178     coloursMenu.add(hydro);
179     coloursMenu.add(helix);
180     coloursMenu.add(strand);
181     coloursMenu.add(turn);
182     coloursMenu.add(buried);
183     coloursMenu.add(user);
184
185     helpMenu.add(jmolHelp);
186
187     setMenuBar(menuBar);
188
189     renderPanel = new RenderPanel();
190     embedMenuIfNeeded(renderPanel);
191     this.add(renderPanel, BorderLayout.CENTER);
192     viewer = JmolViewer.allocateViewer(renderPanel,
193             new SmarterJmolAdapter(), "jalviewJmol", ap.av.applet
194                     .getDocumentBase(), ap.av.applet.getCodeBase(), "",
195             this);
196
197     jmolpopup = JmolPopup.newJmolPopup(viewer, true, "Jmol", true);
198
199     this.addWindowListener(new WindowAdapter()
200     {
201       public void windowClosing(WindowEvent evt)
202       {
203         closeViewer();
204       }
205     });
206
207     if (pdbentry.getFile() != null)
208     {
209       // import structure data from pdbentry.getFile based on given protocol
210       if (protocol.equals(AppletFormatAdapter.PASTE))
211       {
212         loadInline(pdbentry.getFile());
213       }
214       else if (protocol.equals(AppletFormatAdapter.FILE)
215               || protocol.equals(AppletFormatAdapter.URL))
216       {
217         viewer.openFile(pdbentry.getFile());
218       }
219       else
220       {
221         // probably CLASSLOADER based datasource..
222         // Try and get a reader on the datasource, and pass that to Jmol
223         try
224         {
225           java.io.Reader freader = null;
226           if (reader != null)
227           {
228             if (jalview.bin.JalviewLite.debug)
229             {
230               System.err
231                       .println("AppletJmol:Trying to reuse existing PDBfile IO parser.");
232             }
233             // re-use the one we opened earlier
234             freader = reader.getReader();
235           }
236           if (freader == null)
237           {
238             if (jalview.bin.JalviewLite.debug)
239             {
240               System.err
241                       .println("AppletJmol:Creating new PDBfile IO parser.");
242             }
243             FileParse fp = new FileParse(pdbentry.getFile(), protocol);
244             fp.mark();
245             // reader = new MCview.PDBfile(fp);
246             // could set ID, etc.
247             // if (!reader.isValid())
248             // {
249             // throw new Exception("Invalid datasource.
250             // "+reader.getWarningMessage());
251             // }
252             // fp.reset();
253             freader = fp.getReader();
254           }
255           if (freader == null)
256           {
257             throw new Exception(
258                     "Invalid datasource. Could not obtain Reader.");
259           }
260           viewer.openReader(pdbentry.getFile(), pdbentry.getId(), freader);
261         } catch (Exception e)
262         {
263           // give up!
264           System.err.println("Couldn't access pdbentry id="
265                   + pdbentry.getId() + " and file=" + pdbentry.getFile()
266                   + " using protocol=" + protocol);
267           e.printStackTrace();
268         }
269       }
270     }
271
272     jalview.bin.JalviewLite.addFrame(this, "Jmol", 400, 400);
273   }
274
275   /**
276    * create a new binding between structures in an existing jmol viewer instance and
277    * an alignpanel with sequences that have existing PDBFile entries. Note, this does not open a new Jmol window, 
278    * or modify the display of the structures in the original jmol window.
279    * @param viewer2
280    * @param alignPanel
281    * @param seqs - sequences to search for associations
282    */
283   public AppletJmol(JmolViewer viewer2, AlignmentPanel alignPanel,
284           SequenceI[] seqs)
285   {
286     
287     // TODO Auto-generated constructor stub
288   }
289
290   public void loadInline(String string)
291   {
292     loadedInline = true;
293     viewer.openStringInline(string);
294   }
295
296   void setChainMenuItems(Vector chains)
297   {
298     chainMenu.removeAll();
299
300     MenuItem menuItem = new MenuItem("All");
301     menuItem.addActionListener(this);
302
303     chainMenu.add(menuItem);
304
305     CheckboxMenuItem menuItemCB;
306     for (int c = 0; c < chains.size(); c++)
307     {
308       menuItemCB = new CheckboxMenuItem(chains.elementAt(c).toString(),
309               true);
310       menuItemCB.addItemListener(this);
311       chainMenu.add(menuItemCB);
312     }
313   }
314
315   boolean allChainsSelected = false;
316
317   void centerViewer()
318   {
319     jmolHistory(false);
320     StringBuffer cmd = new StringBuffer();
321     String lbl;
322     int mlength, p,mnum;
323     for (int i = 0; i < chainMenu.getItemCount(); i++)
324     {
325       if (chainMenu.getItem(i) instanceof CheckboxMenuItem)
326       {
327         CheckboxMenuItem item = (CheckboxMenuItem) chainMenu.getItem(i);
328         if (item.getState())
329         {
330           lbl = item.getLabel();
331           mlength = 0;
332           do
333           {
334             p = mlength;
335             mlength = lbl.indexOf(":", p);
336           } while (p < mlength && mlength < (lbl.length() - 2));
337                   mnum = 1+getModelNum(lbl.substring(0, mlength));
338         if (mnum>0)
339           {cmd.append(":" + lbl.substring(mlength + 1) + " /"
340                 + mnum + " or ");
341           }
342         }
343       }
344     }
345
346     if (cmd.length() > 0)
347       cmd.setLength(cmd.length() - 4);
348
349     viewer
350             .evalString("select *;restrict " + cmd + ";cartoon;center "
351                     + cmd);
352     jmolHistory(true);
353   }
354
355   private int getModelNum(String modelFileName)
356   {
357     String[] mfn = getPdbFile();
358     if (mfn == null)
359     {
360       return -1;
361     }
362     for (int i = 0; i < mfn.length; i++)
363     {
364       if (mfn[i].equalsIgnoreCase(modelFileName))
365         return i;
366     }
367     return -1;
368   }
369
370   void closeViewer()
371   {
372     viewer.setModeMouse(org.jmol.viewer.JmolConstants.MOUSE_NONE);
373     // remove listeners for all structures in viewer
374     StructureSelectionManager.getStructureSelectionManager()
375             .removeStructureViewerListener(this, this.getPdbFile());
376     // and shut down jmol
377     viewer.evalStringQuiet("zap");
378     viewer.setJmolStatusListener(null);
379
380     viewer = null;
381
382     this.setVisible(false);
383   }
384
385   public void actionPerformed(ActionEvent evt)
386   {
387     jmolHistory(false);
388     if (evt.getSource() == mappingMenuItem)
389     {
390       jalview.appletgui.CutAndPasteTransfer cap = new jalview.appletgui.CutAndPasteTransfer(
391               false, null);
392       Frame frame = new Frame();
393       frame.add(cap);
394
395       jalview.bin.JalviewLite.addFrame(frame, "PDB - Sequence Mapping",
396               550, 600);
397       cap.setText(StructureSelectionManager.getStructureSelectionManager()
398               .printMapping(pdbentry.getFile()));
399     }
400     else if (evt.getSource() == charge)
401     {
402       colourBySequence = false;
403       seqColour.setState(false);
404       viewer
405               .evalStringQuiet("select *;color white;select ASP,GLU;color red;"
406                       + "select LYS,ARG;color blue;select CYS;color yellow");
407     }
408
409     else if (evt.getSource() == chain)
410     {
411       colourBySequence = false;
412       seqColour.setState(false);
413       viewer.evalStringQuiet("select *;color chain");
414     }
415     else if (evt.getSource() == zappo)
416     {
417       setJalviewColourScheme(new ZappoColourScheme());
418     }
419     else if (evt.getSource() == taylor)
420     {
421       setJalviewColourScheme(new TaylorColourScheme());
422     }
423     else if (evt.getSource() == hydro)
424     {
425       setJalviewColourScheme(new HydrophobicColourScheme());
426     }
427     else if (evt.getSource() == helix)
428     {
429       setJalviewColourScheme(new HelixColourScheme());
430     }
431     else if (evt.getSource() == strand)
432     {
433       setJalviewColourScheme(new StrandColourScheme());
434     }
435     else if (evt.getSource() == turn)
436     {
437       setJalviewColourScheme(new TurnColourScheme());
438     }
439     else if (evt.getSource() == buried)
440     {
441       setJalviewColourScheme(new BuriedColourScheme());
442     }
443     else if (evt.getSource() == user)
444     {
445       new UserDefinedColours(this);
446     }
447     else if (evt.getSource() == jmolHelp)
448     {
449       try
450       {
451         ap.av.applet.getAppletContext().showDocument(
452                 new java.net.URL(
453                         "http://jmol.sourceforge.net/docs/JmolUserGuide/"),
454                 "jmolHelp");
455       } catch (java.net.MalformedURLException ex)
456       {
457       }
458     }
459     else
460     {
461       allChainsSelected = true;
462       for (int i = 0; i < chainMenu.getItemCount(); i++)
463       {
464         if (chainMenu.getItem(i) instanceof CheckboxMenuItem)
465           ((CheckboxMenuItem) chainMenu.getItem(i)).setState(true);
466       }
467       centerViewer();
468       allChainsSelected = false;
469     }
470     jmolHistory(true);
471   }
472   private void jmolHistory(boolean enable)
473   {
474     viewer.setBooleanProperty("history", enable);
475   }
476   public void setJalviewColourScheme(ColourSchemeI cs)
477   {
478     colourBySequence = false;
479     seqColour.setState(false);
480
481     if (cs == null)
482       return;
483
484     String res;
485     int index;
486     Color col;
487     jmolHistory(false);
488
489     Enumeration en = ResidueProperties.aa3Hash.keys();
490     StringBuffer command = new StringBuffer("select *;color white;");
491     while (en.hasMoreElements())
492     {
493       res = en.nextElement().toString();
494       index = ((Integer) ResidueProperties.aa3Hash.get(res)).intValue();
495       if (index > 20)
496         continue;
497
498       col = cs.findColour(ResidueProperties.aa[index].charAt(0));
499
500       command.append("select " + res + ";color[" + col.getRed() + ","
501               + col.getGreen() + "," + col.getBlue() + "];");
502     }
503
504     viewer.evalStringQuiet(command.toString());
505     jmolHistory(true);
506   }
507
508   public void itemStateChanged(ItemEvent evt)
509   {
510     if (evt.getSource() == seqColour)
511     {
512       lastCommand = null;
513       colourBySequence = seqColour.getState();
514       colourBySequence(ap);
515     }
516     else if (!allChainsSelected)
517       centerViewer();
518   }
519
520   public void keyPressed(KeyEvent evt)
521   {
522     if (evt.getKeyCode() == KeyEvent.VK_ENTER && scriptWindow.isVisible())
523     {
524       viewer.evalString(inputLine.getText());
525       history.append("\n$ " + inputLine.getText());
526       inputLine.setText("");
527     }
528
529   }
530
531   public void keyTyped(KeyEvent evt)
532   {
533   }
534
535   public void keyReleased(KeyEvent evt)
536   {
537   }
538
539   String[] modelFileNames = null;
540
541   // ////////////////////////////////
542   // /StructureListener
543   public String[] getPdbFile()
544   {
545     if (modelFileNames == null)
546     {
547       String mset[] = new String[viewer.getModelCount()];
548       for (int i = 0; i < mset.length; i++)
549       {
550         mset[i] = viewer.getModelFileName(i);
551       }
552       modelFileNames = mset;
553     }
554     return modelFileNames;
555   }
556
557   String lastMessage;
558
559   // jmol/ssm only
560   public void mouseOverStructure(int atomIndex, String strInfo)
561   {
562     int pdbResNum;
563     int mdlSep = strInfo.indexOf("/");
564     int chainSeparator = strInfo.indexOf(":"), chainSeparator1 = -1;
565
566     if (chainSeparator == -1)
567     {
568       chainSeparator = strInfo.indexOf(".");
569       if (mdlSep > -1 && mdlSep < chainSeparator)
570       {
571         chainSeparator1 = chainSeparator;
572         chainSeparator = mdlSep;
573       }
574     }
575     pdbResNum = Integer.parseInt(strInfo.substring(
576             strInfo.indexOf("]") + 1, chainSeparator));
577
578     String chainId;
579
580     if (strInfo.indexOf(":") > -1)
581       chainId = strInfo.substring(strInfo.indexOf(":") + 1, strInfo
582               .indexOf("."));
583     else
584     {
585       chainId = " ";
586     }
587
588     String pdbfilename = pdbentry.getFile();
589     if (mdlSep > -1)
590     {
591       if (chainSeparator1 == -1)
592       {
593         chainSeparator1 = strInfo.indexOf(".", mdlSep);
594       }
595       String mdlId = (chainSeparator1 > -1) ? strInfo.substring(mdlSep + 1,
596               chainSeparator1) : strInfo.substring(mdlSep + 1);
597       try
598       {
599         // recover PDB filename for the model hovered over.
600         pdbfilename = viewer
601                 .getModelFileName(new Integer(mdlId).intValue() - 1);
602       } catch (Exception e)
603       {
604       }
605       ;
606     }
607     if (lastMessage == null || !lastMessage.equals(strInfo))
608       ssm.mouseOverStructure(pdbResNum, chainId, pdbfilename);
609
610     lastMessage = strInfo;
611   }
612
613   StringBuffer resetLastRes = new StringBuffer();
614
615   StringBuffer eval = new StringBuffer();
616
617   // jmol/ssm only
618   public void highlightAtom(int atomIndex, int pdbResNum, String chain,
619           String pdbfile)
620   {
621     int mdlNum = 1+getModelNum(pdbfile);
622     if (mdlNum==0)
623     {
624       return;
625     }
626
627     jmolHistory(false);
628     // if (!pdbfile.equals(pdbentry.getFile()))
629     // return;
630     if (resetLastRes.length() > 0)
631     {
632       viewer.evalStringQuiet(resetLastRes.toString());
633     }
634
635     eval.setLength(0);
636     eval.append("select " + pdbResNum); // +modelNum
637
638     resetLastRes.setLength(0);
639     resetLastRes.append("select " + pdbResNum); // +modelNum
640
641     if (!chain.equals(" "))
642     {
643     eval.append(":");
644     resetLastRes.append(":");
645       eval.append(chain);
646       resetLastRes.append(chain);
647     }
648     // if (mdlNum != 0)
649     {
650       eval.append(" /" + (mdlNum));
651       resetLastRes.append("/" + (mdlNum));
652     }
653     eval.append(";wireframe 100;" + eval.toString() + " and not hetero;");
654
655     resetLastRes.append(";wireframe 0;" + resetLastRes.toString()
656             + " and not hetero; spacefill 0;");
657
658     eval.append("spacefill 200;select none");
659
660     viewer.evalStringQuiet(eval.toString());
661     jmolHistory(true);
662
663   }
664
665   public void updateColours(Object source)
666   {
667     colourBySequence((AlignmentPanel) source);
668   }
669
670   // End StructureListener
671   // //////////////////////////
672
673   public Color getColour(int atomIndex, int pdbResNum, String chain,
674           String pdbfile)
675   {
676     if (!pdbfile.equals(pdbentry.getFile()))
677       return null;
678
679     return new Color(viewer.getAtomArgb(atomIndex));
680   }
681
682   String lastCommand;
683
684   FeatureRenderer fr = null;
685
686   public void colourBySequence(AlignmentPanel sourceap)
687   {
688     this.ap = sourceap;
689
690     if (!colourBySequence)
691       return;
692     String[] files = getPdbFile();
693     SequenceRenderer sr = new SequenceRenderer(ap.av);
694
695     boolean showFeatures = false;
696
697     if (ap.av.showSequenceFeatures)
698     {
699       showFeatures = true;
700       if (fr == null)
701       {
702         fr = new jalview.appletgui.FeatureRenderer(ap.av);
703       }
704
705       fr.transferSettings(ap.seqPanel.seqCanvas.getFeatureRenderer());
706     }
707
708     StringBuffer command = new StringBuffer();
709
710     for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
711     {
712       StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
713
714       if (mapping == null || mapping.length < 1)
715         continue;
716
717       int lastPos = -1;
718       for (int s = 0; s < sequence.length; s++)
719       {
720         for (int sp, m = 0; m < mapping.length; m++)
721         {
722           if (mapping[m].getSequence() == sequence[s]
723                   && (sp = ap.av.alignment.findIndex(sequence[s])) > -1)
724           {
725             SequenceI asp = ap.av.alignment.getSequenceAt(sp);
726             for (int r = 0; r < asp.getLength(); r++)
727             {
728               // no mapping to gaps in sequence
729               if (jalview.util.Comparison.isGap(asp.getCharAt(r)))
730               {
731                 continue;
732               }
733               int pos = mapping[m].getPDBResNum(asp.findPosition(r));
734
735               if (pos < 1 || pos == lastPos)
736                 continue;
737
738               lastPos = pos;
739
740               Color col = sr.getResidueBoxColour(sequence[s], r);
741
742               if (showFeatures)
743                 col = fr.findFeatureColour(col, sequence[s], r);
744               String newSelcom = (mapping[m].getChain() != " " ? ":"
745                       + mapping[m].getChain() : "")
746                       + "/"
747                       + (pdbfnum + 1)
748                       + ".1"
749                       + ";color["
750                       + col.getRed()
751                       + ","
752                       + col.getGreen()
753                       + ","
754                       + col.getBlue() + "]";
755               if (command.toString().endsWith(newSelcom))
756               {
757                 command = condenseCommand(command.toString(), pos);
758                 continue;
759               }
760               // TODO: deal with case when buffer is too large for Jmol to parse
761               // - execute command and flush
762
763               command.append(";select " + pos);
764               command.append(newSelcom);
765             }
766             break;
767           }
768         }
769       }
770     }
771
772     jmolHistory(false);
773     if (lastCommand == null || !lastCommand.equals(command.toString()))
774     {
775       viewer.evalStringQuiet(command.toString());
776     }
777     jmolHistory(true);
778     lastCommand = command.toString();
779   }
780
781   StringBuffer condenseCommand(String command, int pos)
782   {
783
784     StringBuffer sb = new StringBuffer(command.substring(0, command
785             .lastIndexOf("select") + 7));
786
787     command = command.substring(sb.length());
788
789     String start;
790
791     if (command.indexOf("-") > -1)
792     {
793       start = command.substring(0, command.indexOf("-"));
794     }
795     else
796     {
797       start = command.substring(0, command.indexOf(":"));
798     }
799
800     sb.append(start + "-" + pos + command.substring(command.indexOf(":")));
801
802     return sb;
803   }
804
805   // ///////////////////////////////
806   // JmolStatusListener
807
808   public String eval(String strEval)
809   {
810     // System.out.println(strEval);
811     // "# 'eval' is implemented only for the applet.";
812     return null;
813   }
814
815   public void createImage(String file, String type, int quality)
816   {
817   }
818
819   public void notifyFileLoaded(String fullPathName, String fileName2,
820           String modelName, String errorMsg, int modelParts)
821   {
822     if (errorMsg != null)
823     {
824       fileLoadingError = errorMsg;
825       repaint();
826       return;
827     }
828     fileLoadingError = null;
829     modelFileNames = null;
830     
831     String[] modelfilenames = getPdbFile();
832     ssm = StructureSelectionManager.getStructureSelectionManager();
833     boolean modelsloaded=false;
834     for (int modelnum = 0; modelnum < modelfilenames.length; modelnum++)
835     {
836       String fileName = modelfilenames[modelnum];
837       if (fileName != null)
838       {
839         // search pdbentries and sequences to find correct pdbentry and sequence[] pair for this filename
840         if (pdbentry.getFile().equals(fileName))
841         {
842           modelsloaded=true;
843           MCview.PDBfile pdb;
844           if (loadedInline)
845           {
846             pdb = ssm.setMapping(sequence, chains, pdbentry.getFile(),
847                     AppletFormatAdapter.PASTE);
848             pdbentry.setFile("INLINE" + pdb.id);
849           }
850           else
851           {
852             // TODO: Jmol can in principle retrieve from CLASSLOADER but this
853             // needs
854             // to be tested. See mantis bug
855             // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
856
857             pdb = ssm.setMapping(sequence, chains, pdbentry.getFile(),
858                     AppletFormatAdapter.URL);
859
860           }
861
862           pdbentry.setId(pdb.id);
863
864           Vector chains = new Vector();
865           for (int i = 0; i < pdb.chains.size(); i++)
866           {
867             chains.addElement(new String(pdb.id + ":"
868                     + ((MCview.PDBChain) pdb.chains.elementAt(i)).id));
869           }
870           setChainMenuItems(chains);
871
872           colourBySequence(ap);
873
874           StringBuffer title = new StringBuffer(sequence[0].getName() + ":"
875                   + pdbentry.getId());
876
877           if (pdbentry.getProperty() != null)
878           {
879             if (pdbentry.getProperty().get("method") != null)
880             {
881               title.append(" Method: ");
882               title.append(pdbentry.getProperty().get("method"));
883             }
884             if (pdbentry.getProperty().get("chains") != null)
885             {
886               title.append(" Chain:");
887               title.append(pdbentry.getProperty().get("chains"));
888             }
889           }
890
891           this.setTitle(title.toString());
892
893         }
894         else
895         {
896           // this is a foreign pdb file that jalview doesn't know about - add it to the dataset
897           // and try to find a home - either on a matching sequence or as a new sequence.
898           String pdbcontent = viewer.getData("/" + (modelnum + 1) + ".1",
899                   "PDB");
900           // parse pdb file into a chain, etc.
901           // locate best match for pdb in associated views and add mapping to
902           // ssm
903           modelsloaded=true;
904         }
905       }
906       }
907       if (modelsloaded) {
908       // FILE LOADED OK
909       jmolpopup.updateComputedMenus();
910       viewer
911               .evalStringQuiet("model 0; select backbone;restrict;cartoon;wireframe off;spacefill off");
912
913       ssm.addStructureViewerListener(this);
914     }
915   }
916
917   public void sendConsoleEcho(String strEcho)
918   {
919     if (scriptWindow == null)
920       showConsole(true);
921
922     history.append("\n" + strEcho);
923   }
924
925   public void sendConsoleMessage(String strStatus)
926   {
927     if (history != null && strStatus != null
928             && !strStatus.equals("Script completed"))
929     {
930       history.append("\n" + strStatus);
931     }
932   }
933
934   public void notifyScriptTermination(String strStatus, int msWalltime)
935   {
936   }
937
938   public void handlePopupMenu(int x, int y)
939   {
940     jmolpopup.show(x, y);
941   }
942
943   public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)
944   {
945     notifyAtomPicked(iatom, strMeasure, null);
946   }
947
948   public void notifyAtomPicked(int atomIndex, String strInfo, String strData)
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
964               .indexOf("."));
965
966     if ((p=strInfo.indexOf("/"))> -1)
967             {
968       mdlString += strInfo.substring(p, strInfo.indexOf(" #"));
969             }
970     picked = "((" + picked + ".CA" + mdlString+")|(" + picked + ".P" + 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
985   }
986
987   public void notifyAtomHovered(int atomIndex, String strInfo, String data)
988   {
989     if (data != null)
990     {
991       System.err.println("Ignoring additional hover info: " + data);
992     }
993     mouseOverStructure(atomIndex, strInfo);
994   }
995
996   public void showUrl(String url)
997   {
998     try
999     {
1000       ap.av.applet.getAppletContext().showDocument(new java.net.URL(url),
1001               "jmolOutput");
1002     } catch (java.net.MalformedURLException ex)
1003     {
1004     }
1005   }
1006
1007   public void showConsole(boolean showConsole)
1008   {
1009     if (scriptWindow == null)
1010     {
1011       scriptWindow = new Panel(new BorderLayout());
1012       inputLine = new TextField();
1013       history = new TextArea(5, 40);
1014       scriptWindow.add(history, BorderLayout.CENTER);
1015       scriptWindow.add(inputLine, BorderLayout.SOUTH);
1016       add(scriptWindow, BorderLayout.SOUTH);
1017       scriptWindow.setVisible(false);
1018       history.setEditable(false);
1019       inputLine.addKeyListener(this);
1020     }
1021
1022     scriptWindow.setVisible(!scriptWindow.isVisible());
1023     validate();
1024   }
1025
1026   public float[][] functionXY(String functionName, int x, int y)
1027   {
1028     return null;
1029   }
1030
1031   // /End JmolStatusListener
1032   // /////////////////////////////
1033
1034   class RenderPanel extends Panel
1035   {
1036     Dimension currentSize = new Dimension();
1037
1038     Rectangle rectClip = new Rectangle();
1039
1040     public void update(Graphics g)
1041     {
1042       paint(g);
1043     }
1044
1045     public void paint(Graphics g)
1046     {
1047       currentSize = this.getSize();
1048       rectClip = g.getClipBounds();
1049
1050       if (viewer == null)
1051       {
1052         g.setColor(Color.black);
1053         g.fillRect(0, 0, currentSize.width, currentSize.height);
1054         g.setColor(Color.white);
1055         g.setFont(new Font("Verdana", Font.BOLD, 14));
1056         g.drawString("Retrieving PDB data....", 20, currentSize.height / 2);
1057       }
1058       else
1059       {
1060         viewer.renderScreenImage(g, currentSize, rectClip);
1061       }
1062     }
1063   }
1064
1065   public String createImage(String fileName, String type,
1066           Object textOrBytes, int quality)
1067   {
1068     // TODO Auto-generated method stub
1069     return null;
1070   }
1071
1072   public float[][][] functionXYZ(String functionName, int nx, int ny, int nz)
1073   {
1074     // TODO Auto-generated method stub
1075     return null;
1076   }
1077
1078   public Hashtable getRegistryInfo()
1079   {
1080     // TODO Auto-generated method stub
1081     return null;
1082   }
1083
1084   public void notifyCallback(int type, Object[] data)
1085   {
1086     try
1087     {
1088       switch (type)
1089       {
1090       case JmolConstants.CALLBACK_LOADSTRUCT:
1091         notifyFileLoaded((String) data[1], (String) data[2],
1092                 (String) data[3], (String) data[4], ((Integer) data[5])
1093                         .intValue());
1094
1095         break;
1096       case JmolConstants.CALLBACK_PICK:
1097         notifyAtomPicked(((Integer) data[2]).intValue(), (String) data[1],
1098                 (String) data[0]);
1099         // also highlight in alignment
1100       case JmolConstants.CALLBACK_HOVER:
1101         notifyAtomHovered(((Integer) data[2]).intValue(), (String) data[1],
1102                 (String) data[0]);
1103         break;
1104       case JmolConstants.CALLBACK_SCRIPT:
1105         notifyScriptTermination((String) data[2], ((Integer) data[3])
1106                 .intValue());
1107         break;
1108       case JmolConstants.CALLBACK_ECHO:
1109         sendConsoleEcho((String) data[1]);
1110         break;
1111       case JmolConstants.CALLBACK_MESSAGE:
1112         sendConsoleMessage((data == null) ? ((String) null)
1113                 : (String) data[1]);
1114         break;
1115       case JmolConstants.CALLBACK_MEASURE:
1116       case JmolConstants.CALLBACK_CLICK:
1117       default:
1118         System.err.println("Unhandled callback " + type + " " + data);
1119         break;
1120       }
1121     } catch (Exception e)
1122     {
1123       System.err.println("Squashed Jmol callback handler error:");
1124       e.printStackTrace();
1125     }
1126   }
1127
1128   public boolean notifyEnabled(int callbackPick)
1129   {
1130     switch (callbackPick)
1131     {
1132     case JmolConstants.CALLBACK_ECHO:
1133     case JmolConstants.CALLBACK_LOADSTRUCT:
1134     case JmolConstants.CALLBACK_MEASURE:
1135     case JmolConstants.CALLBACK_MESSAGE:
1136     case JmolConstants.CALLBACK_PICK:
1137     case JmolConstants.CALLBACK_SCRIPT:
1138     case JmolConstants.CALLBACK_HOVER:
1139     case JmolConstants.CALLBACK_ERROR:
1140       return true;
1141     case JmolConstants.CALLBACK_CLICK:
1142     case JmolConstants.CALLBACK_ANIMFRAME:
1143     case JmolConstants.CALLBACK_MINIMIZATION:
1144     case JmolConstants.CALLBACK_RESIZE:
1145     case JmolConstants.CALLBACK_SYNC:
1146     }
1147     return false;
1148   }
1149
1150   public void setCallbackFunction(String callbackType,
1151           String callbackFunction)
1152   {
1153     System.err.println("Ignoring set-callback request to associate "
1154             + callbackType + " with function " + callbackFunction);
1155
1156   }
1157
1158 }